Hey @danshaw, I put together an EA for this back in April to track our cert expirations after we moved to a one year expiration. Since our machine certs are identified by the hostname of the machine and living in the System keychain (yours might be in Login), we were able to parse out the cert names based on the hostname of the machine (and if there were multiple certs, clean them up when we went through the renewal process ((see script below))), but since all certs with the SHA-1 hash and the cert info are returned top-down, oldest-newest, I can throw in that sed statement to grab the last occurrence of a result that starts SHA-1, which will get me the newest cert on the machine that matches my criteria (even if there were duplicates) and gives me an accurate representation of how long it had before expiration. Below is the EA (spit out the result as an integer and you can smart group it out as needed) that I'm using in my environment:
#!/bin/bash
#Created 4/14/16; NRJA
CompName=$(hostname)
ExpDate=$(/usr/bin/security find-certificate -a -c $CompName -p -Z "/Library/Keychains/System.keychain" | sed -n 'H; /^SHA-1/h; ${g;p;}' | /usr/bin/openssl x509 -noout -enddate 2>/dev/null | cut -f2 -d=)
EpochOne=$(date -j -f '%b %d %T %Y %Z' "$ExpDate" '+%s')
EpochTwo=$(date +%s)
MathOne=$(expr $EpochOne - $EpochTwo)
MathTwo=$(expr $MathOne / 86400)
echo "<result>${MathTwo}</result>"
From there, we have machine certs that are issued as part of our internal wireless/VPN setup policy, and we're able to issue a new one as part of that PKG installation. I also wrote a duplicate cert lookup script (below) that will clean up any remaining duplicate certs if they exist on the machine (again, identified by the hostname of the machine, because that's how our certs are issued) until only the newest one remains:
#!/bin/bash
#Created 4/15/16; NRJA
# Declare variables
CompName=$(hostname)
CertCount=$(/usr/bin/security find-certificate -a -c $CompName -p -Z "/Library/Keychains/System.keychain" | grep SHA-1 | wc | awk '{print $1}')
# Search for and clean up extra machine certificates until only the newest remains
if [ $CertCount == 1 ]; then
echo "Computer only has one cert"
exit 0
elif [ $CertCount == 0 ]; then
echo "Computer has no machine cert"
exit 0
else
Certs=$(/usr/bin/security find-certificate -a -c $CompName -p -Z "/Library/Keychains/System.keychain" | grep SHA-1 | awk '{print $3}' | tail -r | tail +2 | tail -r)
while [ "$Certs" != "" ]
do
Certs=$(/usr/bin/security find-certificate -a -c $CompName -p -Z "/Library/Keychains/System.keychain" | grep SHA-1 | awk '{print $3}' | tail -r | tail +2 | tail -r)
security delete-certificate -Z $Certs /Library/Keychains/System.keychain &2>/dev/null
done
fi
@NoahRJ This is great! Thanks so much. We also name our computers by hostname so that works out perfectly. A couple followup questions:
With this script you can track how many days are left on the certificate. When it is time to deploy again are you using a config profile that you then install as a package/policy or are you using the JSS config profile to deploy? If using a package, why that instead of using the built in JSS to deploy the config profile?
In my tests, when I installed the config profile again it created a duplicate certificate, am I to assume that you then have that 2nd script run to remove the old certificates? How often do you run the script? Only after installation of the config profile or on a regular basis?
@danshaw Happy to help!
We use a package that includes profiles as part of the payload because we have a couple of different profiles that install based on location, so there's a postflight script that includes logic to determine which profile to install. Also, we've generally just had more success using the profiles command to install a profile rather than relying on the JSS to do it.
Yep, that's correct! We only run that script after this cert renewal policy hits our scope (smart group with machines with less than 30 days remaining according to our EA) to clean up duplicates. And the script doesn't run on a regular basis. It doesn't matter for us if a computer has duplicate certs, because we're working off the newest cert on the machine for the EA, and it'll be cleaned up once a year as is.
@NoahRJ This is great stuff. Thanks for posting. Couple of questions myself, if you don't mind..
Since you're installing using the profiles command, does that make the profiles local, instead of via MDM? It does right?
When renewing the certs for wireless, for example, are you able to accomplish this without an interruption of connection? We also used machine based certificate authentication to get on our internal networks but I'm wondering if I renew an expiring existing certificate while the user is actively using that internal network, would they then lose connection (even if just briefly)? If your users do lose connection, how are you getting them back on without interaction? Or does it require some user interaction?
Profiles has a -W option that is supposed to attempt to renew a certificate in an installed profile. Has anyone tried that instead of reinstalling the profile entirely?
@NoahRJ I'm testing out your script and I'm seeing an issue where the script returns that it can't remove some certificates. It's also exiting properly but not removing certificates in some cases. We use an AD CA requested by a profile so this would be of great help to us. Any insight as to why this is hit or miss would be great.
@alexjdale Yea I've tried using, -W with also a -p flag and then including the profiler identifier, but still no dice. By it's description, you would think -W is exactly what you need: “sets up command to renew certificates in an installed profile”.
Hey @danshaw , hoping you can help me out. I'm trying to run your first script but keep getting "Failed conversion of '' using format
%b %d %T %Y %Z'' ". Any thoughts?
Sorry for the delay here, folks.
@perrycj That's correct that these are local profiles through our method of installation. We haven't had issues with our admin users removing them, and even if they do (bricking a wireless connection, for example), we have a Self Service policy that reinstalls company security certs, rebinds to AD, reissues AD cert, and reinstalls the wireless profile, so it's kind of a catchall for any self-inflicted issues they may incur.
And yeah, when renewing these certs, we've found the connection is actually almost always stable or reconnects. In our process, we're reissuing the new cert before removing the old one (which is partially why we avoided using the Profiles -W command), so even if there's a brief drop of connection, the wireless profile or VPN connection can seamlessly latch onto the newly installed machine cert for authentication.
@jhbush1973, hard to troubleshoot without knowing more specifics of your environment. Sounds like it is working sometimes, but not always? On those machines that aren't removing old certs, I'd run my code down line by line on the machine and see what's being returned for each value. The script exits properly because I threw a "&2>/dev/null" on the third-to-last line, so any error output is being redirected to /dev/null. You can try stripping that value out and it'll return error notices on why it's not able to remove some of the old certs.
@gabriel_martinez, that error looks like the ExpDate variable isn't being set and the date command in EpochOne doesn't have anything to manipulate since it's trying to convert a null value. Are your machine certs named by computer name, or do they have some other unique value attached to them? I'm grabbing the hostname value and setting that as the determination for the certificate to renew in System.keychain when I set the ExpDate var, so if that's not how they're defined in your environment, you'll need to set that value based on their names. If you're not sure, you can open up Keychain Access and look for the certificate under the System keychain, and then figure out what unique value is attached and update the code accordingly.
@NoahRJ Thanks for the reply back and thanks for answering my questions.
I don't see it mentioned, so I wanted to ask.. how are you actually getting new machine certs on your clients then? I see where you check for expiration and get rid of old ones once new certs are on.. but how are you getting new machine certs to generate? In my experiences, renewing the certs requires user interaction. Do you have a way or are you using a way where it's automated in generating a new cert on a client? Hopefully that makes sense.
@NoahRJ Thanks. We are using our own variable in there to get the cert name. I realized the issue was that we store the cert in the Login keychain. I got it working now. Thanks!
@perrycj Oops, definitely didn't mention that. We have a few different payloads in our wireless config profile that we're installing that handle this. We have our Network payload, a couple Certificate payloads, and an AD Certificate payload. Since our wireless uses an AD cert to connect, we just generate that machine cert with a request to our certificate server via our specified certificate authority.
@NoahRJ So is that request scripted? We do the same things, in terms of how you have your payloads. One for network, one for certs and one for AD.
But I'm assuming you send a request. Currently, at least in my experiences, it doesn't just happen on it's own even with those payloads in place.
@perrycj No, we don't script anything to generate that cert. It all happens under the AD Certificate payload. Upon installation of the profile for us, the payload will go out to the specified cert server validated with a CA that's already installed on the machine (or is included as part of the profile), and generate a cert based on the custom computer certificate template we've put together on the AD side. This (pretty out of date) article on JN talks a little more about it: https://www.jamf.com/jamf-nation/articles/209/requesting-a-certificate-from-a-microsoft-certificate-authority-using-the-casper-suite
@noahRJ Oh I totally get that. We do the same thing. I'm asking once that process happens a first time, how are you getting the new cert on the Mac 6-12 months down the line? Or should I say, how are you getting it to generate a new cert once the original one starts to expire? Hope that's clear.
@NoahRJ I tried running your first script for the EA and it kept failing. I found that I had to change the tail number to 38 lines instead of 37. Now it reports properly!
@perrycj, yeah, so that's where the duplicate cert cleanup stuff comes in. As part of the automated renewal process, we'll install that same wireless profile again over the top, which will only serve to pull down a new machine cert from AD, and then the second script I posted initially will clean up the older cert(s).
@AVmcclint, that's a good call out. The length of our SHA-1 hash + certificate info is 37 lines, so that's why my tail command is tail -n 37. What would better is a sed statement of the last match that starts with SHA-1, since that will always be the newest cert on the machine. Something like this should work:
/usr/bin/security find-certificate -a -c $CompName -p -Z "/Library/Keychains/System.keychain" | sed -n 'H; /^SHA-1/h; ${g;p;}' | /usr/bin/openssl x509 -noout -enddate 2>/dev/null | cut -f2 -d=
Want to give that a shot in your environment and see if it works for you? I'll update my original post accordingly with that new (and ugly) sed statement.
@NoahRJ Ah, yes. I get what you mean now. For some reason I was assuming you were pulling it some automated way. I wasn't even thinking about just installing the profile again. Yes that would definitely work. If your profiles are MDM though, makes it a little trickier.
I have time scheduled with Apple in about 2 weeks and we're going to try to come up with a way to script automatic renew instead of A) the user getting prompted to do it or
installing the profile again. Hopefully we can come up with an automated way to take care of this.
If we do come up with anything, I will update this thread with what we find out.
@NoahRJ Ah, yes. I get what you mean now. For some reason I was assuming you were pulling it some automated way. I wasn't even thinking about just installing the profile again. Yes that would definitely work. If your profiles are MDM though, makes it a little trickier.
I have time scheduled with Apple in about 2 weeks and we're going to try to come up with a way to script automatic renew instead of A) the user getting prompted to do it or
installing the profile again. Hopefully we can come up with an automated way to take care of this.
If we do come up with anything, I will update this thread what we find out.
@perrycj I know this is sort of an old thread. I've dived into the -W option. I've done some tests and it looks like the -W option only works when the certificate is expiring in the next 14 days. If this is not the case, it will give a "returned 0 ((null))" exit code.
I've used @NoahRJ 's script to read out how many days until the certificate expires. Next, I tweaked it to use the -W option if the number of days is < 14.
I noticed that the default renewal notification interval is 14 days. So I hoped this was linked to the -W option, but when I changed this value to 31 days I still wasn't able to use the -W option earlier than 14 days.
Next up my list is to test how the renewal impacts the network connection if the workstation is using the certificate for its connection.
@mattiasvdm Thanks for the post. Were you able to get it to renew using -W yet? Or is that your next step?
As an FYI for everyone in the thread, looks like there's new automatic cert renewal options via config profile beginning in 10.12.4. It has a fair number of caveats, like it can't renew certs that contain MDM payloads or ones that were issued OTA (in addition to a couple of others), but for the purposes of our environment, it looks promising.
@NoahRJ Thanks for the link to that Apple KBase. I heard of the changes with 10.12.4 but haven't had a chance to read up on it yet. This seems like it may work in our environment.