Let’s Encrypt provides free DV SSL certificates for everyone and is now in the open beta phase. I’m not going to go into the details of which of the clients are best, since that depends entirely on your use case (I use acme-tiny and a rule in varnish to intercept all calls to /.well-known/acme-challenge/).
Since the certificates are only valid for 90 days, I often see people suggesting to just renew them via cronjob every 2 months. I find this to be really awful advice, if that renewal fails for any reasons (network problems, local problems, problems with let’s encrypt) the next renewal is a month after the certificate expired. It is also pretty inflexible (what if you would rather prefer to renew them after 80 days).
I use openssl to check daily how long the certificate is still valid, and if a threshold has been reached it tries to renew the certificate (I believe the official client has this functionality too). And if the certificate isn’t renewed by a 2nd threshold, it sends an email altering the admin of the problem (for manually intervening and fixing whatever went wrong).
At the end of this posting I’ll add the complete script, but the quickest way to check how long a certificate is still valid is to use openssl x509 -in -checkend. It will return 0 if the file is still valid in x seconds, and 1 if the certificate doesn’t exist or if the certificate will be expired by then. Just multiply the number of days by 86400 and check if the certificate is still valid:
1 2 | openssl x509 -noout -in "domain.cert" -checkend $( bc <<< "86400 * days") echo $? |
The openssl binary has a few nice options for looking at certificates (both local files and remotely connecting to a server and looking at the provided certificate)
Show information about a local certificate file: openssl x509 -text -noout -in
Connect to a remote server and display the certificate provided: openssl s_client -showcerts -servername foo.bar -connect IP:PORT | openssl x509 -text -noout (–servername foo.bar is only required if you are connecting to a server and need to use SNI to request a cert for a specific domain, i.e. a webserver providing multiple domains on port 443 via SNI. It can of course be omitted if you don’t need it.)
This is the full script I use for checking and renewing certs. It basically just loops through a list of domains, checks if any of the date thresholds are met and then renews certificates/send emails.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #!/bin/bash # how many days before the certificate expires should it be renewed RENEW="28" # how many days before the certificate expires should start sending alerts to the admin ALERT="14" ALERT_EMAIL='admin@your.domain' # some variables set by ansible SSL_BASEDIR="/etc/ssl" ACME_LETSENCRYPT_BINARY="python /usr/local/bin/acme_tiny.py" ACME_CHALLENGE_DIR="/var/www/acme-challenges" ACME_LETSENCRYPT_DIR="${SSL_BASEDIR}/letsencrypt" ACME_ACCOUNT_KEY="${ACME_LETSENCRYPT_DIR}/account.key" # download lets-encrypt-x1-cross-signed.pem if the local copy is more than 3 days old if [[ ! -r "${ACME_LETSENCRYPT_DIR}/lets-encrypt-x1-cross-signed.pem" || $(($(date +%s) - $(date -r "${ACME_LETSENCRYPT_DIR}/lets-encrypt-x1-cross-signed.pem" +%s))) -ge 259299 ]] ; then curl --silent https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem > "${ACME_LETSENCRYPT_DIR}/lets-encrypt-x1-cross-signed.pem" fi for fqdn in www.domain1.foo www.domain2.foo ; do # check if we have to renew a certificate openssl x509 -noout -in "${ACME_LETSENCRYPT_DIR}/${fqdn}.crt" -checkend $( bc <<< "86400 * ${RENEW}") 2>/dev/null if [[ $? -gt 0 ]] ; then ${ACME_LETSENCRYPT_BINARY} \ --account-key "${ACME_ACCOUNT_KEY}" \ --csr "${ACME_LETSENCRYPT_DIR}/${fqdn}.csr" \ --acme-dir "${ACME_CHALLENGE_DIR}" \ > "${ACME_LETSENCRYPT_DIR}/${fqdn}.crt" cat "${ACME_LETSENCRYPT_DIR}/${fqdn}.crt" "${ACME_LETSENCRYPT_DIR}/lets-encrypt-x1-cross-signed.pem" \ > "${ACME_LETSENCRYPT_DIR}/${fqdn}.pem" openssl x509 -noout -text -certopt no_header,no_version,no_pubkey -in "${ACME_LETSENCRYPT_DIR}/${fqdn}.crt" | \ mailx -s "[SSL] OK: ${fqdn} certificate was renewed" ${ALERT_EMAIL} fi # check if we need to alert about certificates that weren't renewed yet openssl x509 -noout -in "${ACME_LETSENCRYPT_DIR}/${fqdn}.crt" -checkend $( bc <<< "86400 * ${ALERT}") 2>/dev/null if [[ $? -gt 0 ]] ; then openssl x509 -noout -text -certopt no_header,no_version,no_pubkey -in "${ACME_LETSENCRYPT_DIR}/${fqdn}.crt" | \ mailx -s "[SSL] ERROR: ${fqdn} certificate will expire soon and wasn't automatically renewed" ${ALERT_EMAIL} fi done |