Skip to main content

I've been tasked with setting up AD Certificates for Macs and after working a bit on it I now have it working where the computer gets a config profile installed that then requests from AD a certificate. Everything is working fine, but now I am at the point where I am not sure what needs to be done when the certificate expires 1-year from now. I need some type of workflow for renewing these as it will be impossible to keep track of all the expiration dates.



After doing some searching on JN, there are some posts a few years old where users recommend scripting this, but there was no one that could get it working.



There is also a feature request to have the JSS auto renew, but that is from 2013 and I don't think that will happen.



If anyone is out there who has put together a workflow for getting this to work I would love to hear about it.

@perrycj Yes, the -W option works fine. But only if the certificate is expiring in the next 14 days. You can test it with a workstation or VM by changing the date to a couple of days before expiring.



I've just tested the renewal of the certificate while the Mac is connected to VPN or WiFi (using the certificate). There was no hick-up at all. It worked fine. I even removed the entire profile while connected to WiFi and nothing happened. It just stayed connected.


@mattiasvdm Nice, I'll definitely try that out then. Thanks for the feedback.


@mattiasvdm I was able to use the profiles -W -p "identifier" (no quotes) command with success. Thanks for verifying that. Here is another Apple kbase talking about how to use the -W command and syntax:



https://support.apple.com/en-us/HT204446


@NoahRJ Thanks for sharing. I've tested the functionality today and the automatic update renewal proces works fine. I just logged into a machine and changed the date to two days before expiring. Then I went out for lunch and when I came back it was renewed. It does keep the old certificate so I'm using your script to remove those.



@perrycj The link you posted doesn't work, it has an 'L' at the end. But I've read it, thanks for that.



So for 10.12.4 machines and newer, I'll use the automatic process. For older OS versions I'll use the script.


How do you script the removal of the old certificate if it has the exact same name? I've been handling the whole renewal process manually on every computer as they start to get their 14 day warnings. If I can make the process 100% hands-off, that would make my day. I do have a 10.12.4 Mac that has a cert that's about to expire soon. I'm looking forward to seeing the renewal in action.


Oops, I just realized the 2nd script in the comments is intended to search and destroy duplicate computer certs. Reading through it, I still don't understand how it knows to only delete the old certs. Where does the magic come into play to leave the brand new cert alone?


@mattiasvdm I fixed it, thanks.


Hey @AVmcclint, the secret sauce is the three commands here: tail -r | tail +2 | tail -r. If you have multiple certs, they'll show up like this when you query through the security command:



Oldest
Older
Old
New
Newer
Newest



When you run the tail -r | tail +2 | tail -r commands after getting that info out from the security command, the output is:



Oldest
Older
Old
New
Newer



So it'll iterate through and remove everything in that list from Newer on up, since the tail -r command reverses the order of those results, strips out the top line with tail +2, and then corrects the order again with tail -r. If there's just one line remaining, the tail +2 will return results of null, since it's always set to exempt the first line of the results. So, when it hits 'while [ "$Certs" != "" ]', if the $Certs variable isn't blank from more than one line being returned, it'll keep cleaning until it is blank, AKA one line is returned and then excluded from the tail +2 command.



Hope that helps!


@NoahRJ That is very creative. My scripting abilities are on the weak side to begin with but when diving into the security command, i'm totally lost. The way you explained the flow, I think I can understand on a conceptual level. :)


@NoahRJ great stuff thanks for sharing! How are you setting the identity preference to use the new certificate?



EDIT: I figured it out



#!/bin/sh
/usr/bin/security set-identity-preference -c $certname -s com.apple.network.eap.system.identity.wlan.ssid.YOUR_SSID /Library/Keychains/System.keychain

@jason.desveaux Were you able to set the identity preference? I know I've been testing with this before and got it working, but somehow it doesn't work with 10.12.4. If I run the set-identity-preference command, it doesn't do anything. There's no identity being created, also no error whatsoever.



EDIT: I think I got it. It's a user setting. I thought it would add the identity to the System keychain so all users can connect to the specific WLAN.



LAST EDIT: 🙂 I found a way to add the WLAN identity preference with the profile to the system keychain. You just have to add your WiFi network to the profile as usual. Choose TLS as the protocol and set the username to %AD_ComputerID% (never knew that profile manager accepted variables).


@mattiasvdm the command I posted should add the identity preference to the system keychain. Since my post, I've come across a few machines where it hasn't worked and I'm not really sure why.



As you mentioned, you can include the identity preference in the profile itself. The problem I have is when you renew the certificate and remove the old one, that preference doesn't get updated to use the new cert automatically.


I made a package in Composer to take most of what's listed above and consolidate it. I have my mobileconfig file in /pvt/tmp, and then a postinstall script to check for network connectivity, then install it. I have tested while connected to VPN, and it does get the new cert before dropping the connection, so this should cover our in-office and remote users. I also noticed I don't need to set which certificate to use in the keychain, so I dropped that part.



We have had issues with the automatic deployment of our network profile, where it would randomly drop from machines, so we've been including a step for techs to login and install it from Self Service manually when building the machines. With this package, I should be able to add it to the application install step that's automated, and reduce a step in the build process!



#!/bin/sh
## postinstall

pathToScript=$0
pathToPackage=$1
targetLocation=$2
targetVolume=$3

# Echo function
echoFunc () {
# Date and Time function for the log file
fDateTime () { echo $(date +"%a %b %d %T"); }

# Title for beginning of line in log file
Title="NetworkProfile:"

# Header string function
fHeader () { echo $(fDateTime) $(hostname) $Title; }

# Check for the log file
if [ -e "/Library/Logs/MachineCert.log" ]; then
echo $(fHeader) "$1" >> "/Library/Logs/MachineCert.log"
else
cat > "/Library/Logs/MachineCert.log"
if [ -e "/Library/Logs/MachineCert.log" ]; then
echo $(fHeader) "$1" >> "MachineCert.log"
else
echo "Failed to create log file, writing to JAMF log"
echo $(fHeader) "$1" >> "/var/log/jamf.log"
fi
fi

# Echo out
echo $(fDateTime) ": $1"
}

echoFunc "======================== Starting Script ========================"

IFS='
'

# Attempt to ping ad.domain.com to make sure we're on the internal network
pingHost=ad.domain.com
if ( ping -c1 $pingHost &>/dev/null )
then
echoFunc "Successfully pinged ad.domain.com, proceeding with profile installation!"

# Install the network profile
echoFunc "Installing new profile"
profiles -I -F "/pvt/tmp/AD_Network_802.1x_Wired_&_Wireless_Configuration_Signed.mobileconfig"
sleep 5

# 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
echoFunc "Computer only has one cert"
elif [ $CertCount == 0 ]; then
echoFunc "Computer has no machine cert"
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)
echoFunc "Deleting $Certs"
security delete-certificate -Z $Certs /Library/Keychains/System.keychain &2>/dev/null
done
fi

# Update the DNS Search domains for the network connections
networksetup -listallnetworkservices | tail -n +2 |
while read CURRENT_SERVICE; do
echoFunc "Setting DNS Search Domains"
/usr/sbin/networksetup -setsearchdomains "${CURRENT_SERVICE}" ad.domain.com domain.com
done
else
echoFunc "Failure to ping ad.domain.com, profile installation NOT attempted!"
fi

unset IFS

# Clean up the network profile
rm -rf "/pvt/tmp/AD_Network_802.1x_Wired_&_Wireless_Configuration_Signed.mobileconfig"

echoFunc "======================== Script Complete ========================"
echoFunc "Exit code: $?"
exit $?

I cleaned up the multi-cert section so it actually deleted the extra certificates. I also added a jamfHelper notification to let the end-user know when their new cert expires.



#!/bin/sh

# Echo function
echoFunc ()
{
# Date and Time function for the log file
fDateTime () { echo $(date +"%a %b %d %T"); }

# Title for beginning of line in log file
Title="MachineCert:"

# Header string function
fHeader () { echo $(fDateTime) $(hostname) $Title; }

# Check for the log file, and write to it
if [ -e "/Library/Logs/MachineCert.log" ]; then
echo $(fHeader) "$1" >> "/Library/Logs/MachineCert.log"
else
cat > "/Library/Logs/MachineCert.log" &
cat "/Library/Logs/MachineCert.log"
if [ -e "/Library/Logs/MachineCert.log" ]; then
echo $(fHeader) "$1" >> "/Library/Logs/MachineCert.log"
else
echo "Failed to create log file, writing to JAMF log"
echo $(fHeader) "$1" >> "/var/log/jamf.log"
fi
fi

# Echo out
echo $(fHeader) "$1"
}

echoFunc "======================== Starting Script ========================"

IFS='
'

# Attempt to ping an internal server to make sure we're on the internal network
pingHost=ad.domain.com
if ( ping -c1 $pingHost &>/dev/null )
then
echoFunc "Successfully pinged $pingHost, proceeding with profile installation!"

# Install the network profile
echoFunc "Installing new profile"
profiles -I -F "/pvt/tmp/Wired_&_Wireless_Configuration_Signed.mobileconfig"
echoFunc "Profile installation result: $?"

# Count the number of machine certificates currently on the device
CertCount=$(security find-certificate -apZ -c $(hostname) "/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 == 0 ]; then
# No machine certificate was found
echoFunc "Computer has no machine certificate!"
elif [ $CertCount == 1 ]; then
# One machine certificate was found

# Find the certificate expiration date
CertExp=$(security find-certificate -apZ -c $(hostname) "/Library/Keychains/System.keychain" | sed -n 'H; /^SHA-1/h; ${g;p;}' | openssl x509 -noout -enddate 2>/dev/null | cut -f2 -d=)
dateformat=$(date -j -f "%b %d %T %Y %Z" "$CertExp" "+%b %d %Y")
echoFunc "Computer certificate expiration: $dateformat, notifying user"
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -button1 "OK" -defaultButton 1 -icon /Applications/Utilities/Keychain Access.app/Contents/Resources/AppIcon.icns -timeout 30 -title "New Machine Certificate" -description "Your new machine certificate expires on: $dateformat. Please make note of this. This window will close in 30 seconds."
else
# Multiple machine certificates were found
echoFunc "Number of machine certificates found: $CertCount"

# Delete the old certificates
Tail=$CertCount
while [ $Tail -ge 2 ]
do
CertsCount=$(security find-certificate -apZ -c $(hostname) "/Library/Keychains/System.keychain" | grep SHA-1 | awk '{print $3}' | tail -r | tail "+$Tail" | tail -r)
echoFunc "Deleting certificate with SHA-1: $CertsCount"
security delete-certificate -Z $CertsCount /Library/Keychains/System.keychain &2>/dev/null
Tail=$(expr $Tail - 1)
done

# Find the new certificate expiration date
CertExp=$(security find-certificate -apZ -c $(hostname) "/Library/Keychains/System.keychain" | sed -n 'H; /^SHA-1/h; ${g;p;}' | openssl x509 -noout -enddate 2>/dev/null | cut -f2 -d=)
dateformat=$(date -j -f "%b %d %T %Y %Z" "$CertExp" "+%b %d %Y")
echoFunc "Computer certificate expiration: $dateformat, notifying user"
/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType utility -button1 "OK" -defaultButton 1 -icon /Applications/Utilities/Keychain Access.app/Contents/Resources/AppIcon.icns -timeout 30 -title "New Machine Certificate" -description "Your new machine certificate expires on: $dateformat. Please make note of this. This window will close in 30 seconds."
fi

# Update the configurations for the network connections
networksetup -listallnetworkservices | tail -n +2 |
while read CURRENT_SERVICE
do
echoFunc "Configuring Current Service: ${CURRENT_SERVICE}"
networksetup -setsearchdomains "${CURRENT_SERVICE}" ad.domain.com
networksetup -setautoproxyurl "${CURRENT_SERVICE}" http://proxy.domain.com:8080/pac
networksetup -setproxybypassdomains "${CURRENT_SERVICE}" 127.0.0.1 *.domain.com
SERVICE_CONFIG_TYPE=`networksetup -getinfo ${CURRENT_SERVICE} | grep "DHCP Configuration"`
if [[ "${SERVICE_CONFIG_TYPE}" == "DHCP Configuration" ]]; then
MAC_ADDRESS=`networksetup -getinfo ${CURRENT_SERVICE} | grep "Ethernet Address" | awk '{ print $3 }'`
if [[ -z ${MAC_ADDRESS} ]]; then
MAC_ADDRESS=`networksetup -getinfo ${CURRENT_SERVICE} | grep "Wi-Fi ID" | awk '{ print $3 }'`
fi
if [[ ${MAC_ADDRESS} != "(null)" && ${MAC_ADDRESS} != "" ]]; then
echoFunc "Setting DHCP Client ID to: ${MAC_ADDRESS}"
networksetup -setdhcp "${CURRENT_SERVICE}" "${MAC_ADDRESS}"
fi
fi
done
else
echoFunc "Failure to ping $pingHost, profile installation NOT attempted!"
fi

unset IFS

# Clean up the network profile
rm -rf "/pvt/tmp/Wired_&_Wireless_Configuration_Signed.mobileconfig"

echoFunc "Exit code: $?"
echoFunc "======================== Script Complete ========================"
exit $?

Hi Team i need help reg the certificate i am deploying the SCEP certificate and the certificate is stored on keychain as on user first and last name and how do i filter as expired and going to expire in 30 days ? do we have any scriot ? kindly help me on that 


Reply