Automatically renewing AD certificates on macOS with Munki

In one of my environments, we use Active Directory Certificate Services (AD CS) and Group Policy to deploy certificates to our domain-joined Windows PCs for use with various forms of network security, such as 802.1x. Microsoft has provided an option to silently renew certificates nearing their expiration, preventing scenario in which a computer cannot renew its certificate because its certificate to get on the network has already expired.

Our macOS computers are able to obtain certificates from AD CS using a configuration profile (.mobileconfig file) we created using Apple’s Profile Manager and pushed with Munki. (By the way, if you are searching for a free macOS management platform, I highly recommend you evaluate Munki!)

The Problem

When creating the configuration profile in Profile Manager, there is not an option to automatically renew the certificate when it is nearing its expiration. The only option is to alert the user of the certificate’s expiration a number of days in advance, allowing the user to click a button in System Preferences to renew the certificate. Not too elegant…

The Solution

In order to automate this process, I applied a custom Munki install check script (included below) to the configuration profile. If you need a simple GUI to perform this task, have a look at MunkiAdmin. In MunkiAdmin, all you need to do is paste the contents of the script in the Install check script box for the configuration profile package.

Please note that this script assumes the computer name will appear in the name of the certificate. This script, therefore, only works with computer certificates.

Here is the basic flow of the script:

  • Looks through the computer certificates installed on the system.
  • Make sure the certificates came from the internal CA server.
  • Determine the expiration date of the certificates.
  • Send an exit code to Munki, instructing Munki whether it needs to re-install the profile. Simply re-installing the existing profile on top of itself (either manually or programmatically) will force the computer to obtain a new certificate.

When using this script, please be sure to modify the User-defined variables as needed in your environment.

#!/bin/bash

## Renew computer certificate on macOS
## Written by Ian Harrier
## For use with .mobileconfig profiles pushed with Munki

#-------------------------------------------------------------------------------
#  User-defined variables
#-------------------------------------------------------------------------------

# If all certificate(s) have fewer than this many seconds until they expire, a
# new certificate will be requested. (e.g. 7862400 seconds = 13 weeks.)
RENEWAL_SECONDS='7862400'

# Certificates must be issued by this CA to be considered valid. This primarily
# disregards self-signed certificates which may exist on the system.
CERT_ISSUER='Your Super Secure CA'

#-------------------------------------------------------------------------------
#  Script body
#-------------------------------------------------------------------------------

# Make sure system utilities can be found
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin export PATH

# In PEM format, get certs on the system with the hostname in the subject
CERTS_ALL=$(security find-certificate -a -c $(hostname) -p)

# Determine the number of certs that were obtained
CERTS_COUNT=$(echo "$CERTS_ALL" | grep 'BEGIN CERTIFICATE' | wc -l)

echo "[I] $CERTS_COUNT certificates were found on this computer."

# Start with there being 0 certs that expire more than RENEWAL_SECONDS from now
CERTS_GOOD=0

for (( i = 1; i <= $CERTS_COUNT; i++ )); do
    # Get the i'th cert from CERTS_ALL
    CERT=$(echo "$CERTS_ALL" | awk -v n=$i '/^-----BEGIN CERTIFICATE-----/{l++} (l==n){print}')

    if [[ $(echo "$CERT" | openssl x509 -issuer | grep "$CERT_ISSUER") ]]; then
        echo "[I] Certificate $i is from a valid issuer."
    else
        echo "[W] Certificate $i is from an invalid issuer. Skipping certificate."
        continue
    fi

    if [[ $(echo "$CERT" | openssl x509 -checkend $RENEWAL_SECONDS | grep 'Certificate will not expire') ]]; then
        echo "[I] Certificate $i will not expire in the next $RENEWAL_SECONDS seconds."
        ((CERTS_GOOD++))
    else
        echo "[W] Certificate $i will expire in the next $RENEWAL_SECONDS seconds."
    fi
done

if [[ $CERTS_GOOD -gt 0 ]]; then
    echo "[I] There is no need to renew the computer certificate at this time."
    exit 1
else
    echo "[E] The computer certificate needs to be renewed."
    exit 0
fi

Conclusion

Well, that’s about it! It would, of course, be easier if there was an “auto-renew” checkbox when creating the configuration profile in Profile Manager, but that might put SysAdmins out of a job, so I suppose Apple is being nice to us after all…