Using Certbot –post-hook to configure Let’s Encrypt Certs for use by multiple services
Certbot discourages modifying files in /etc/letsencrypt/ as this can break things. However some services can not read the certificate and key files with their default permissions of being readable only by root. Also some services (e.g. Haproxy) can only use a combined pem file, and can not load individual cert, chain & key files. Rather than modify the files in /etc/letsencrypt/ I like to copy them to another location and set the specific permissions I need. In addition I combine the Full Chain and the Private Key in to one file. This makes for easier service configuration settings.
Start by creating this post-hook script in /etc/letsencrypt/renewal-hooks/post/
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#!/bin/bash # certbot-post-hook.sh # # Takes all Let's Encrypt certs & keys and concats them in # to pem files for use by apache, dovecot, exim, etc. # # Install in to /etc/letsencrypt/renewal-hooks/post/ to # have it run automatically each time renewals are attempted. # # Run /etc/letsencrypt/renewal-hooks/post/certbot-post-hook.sh # manually after creating new certs, or inlcude this option when # creating certs with certbot: # --post-hook /etc/letsencrypt/renewal-hooks/post/certbot-post-hook.sh # make dir if it doesn't already exist if [[ ! -e /etc/ssl/letsencrypt/ ]]; then mkdir -p /etc/ssl/letsencrypt fi # get list of Let's Encrpyt certs cd /etc/letsencrypt/live/ lecerts=(*) # get list of certs in SSL dir cd /etc/ssl/letsencrypt/ sslcerts=(*) # First cycle thru /etc/ssl/letsencrypt/ and remove any pem # files that don't have a cert in /etc/ssl/letsencrypt/ # (removes certs that have been deleted from letsencrypt). for sslcert in "${!sslcerts[@]}" do # set cert variable cert=${sslcerts[$sslcert]} # remove .pem from end of $cert (last four characters) cert=${cert::-4} if [[ ! " ${lecerts[@]} " =~ " $cert " ]]; then rm /etc/ssl/letsencrypt/${sslcerts[$sslcert]} fi done # add / update pem files in /etc/ssl/letsencrypt/ for lecert in "${!lecerts[@]}" do # set cert variable cert=${lecerts[$lecert]} if [ -f "/etc/ssl/letsencrypt/$cert.pem" ]; then # /etc/ssl/letsencrypt/ pem file already exists # get modified times and only upate if newer LECERTTIME=`date +%s -r /etc/letsencrypt/live/$cert/fullchain.pem` SSLCERTTIME=`date +%s -r /etc/ssl/letsencrypt/$cert.pem` if [[ $LECERTTIME -gt $SSLCERTTIME ]]; then # make sure perms are correct, shouldn't really be needed chmod 640 //etc/ssl/letsencrypt/$cert.pem chown root:ssl-cert /etc/ssl/letsencrypt/$cert.pem # replace existing cert with new data cat /etc/letsencrypt/live/$cert/fullchain.pem > /etc/ssl/letsencrypt/$cert.pem cat /etc/letsencrypt/live/$cert/privkey.pem >> /etc/ssl/letsencrypt/$cert.pem fi else # /etc/ssl/letsencrypt/ pem file does not exists. First create # empty file with correct ownership and permissions. Thus the # copied cert is *never* world readable, not even for an instant. touch /etc/ssl/letsencrypt/$cert.pem chmod 640 //etc/ssl/letsencrypt/$cert.pem chown root:ssl-cert /etc/ssl/letsencrypt/$cert.pem cat /etc/letsencrypt/live/$cert/fullchain.pem > /etc/ssl/letsencrypt/$cert.pem cat /etc/letsencrypt/live/$cert/privkey.pem >> /etc/ssl/letsencrypt/$cert.pem fi done |
Set permissions to 750 (rwxr-x—):
1 |
chmod 750 /etc/letsencrypt/renewal-hooks/post/certbot-post-hook.sh |
The script will run automatically each time renewals are attempted. When creating new certs either run the command manually immediately after running certbot:
1 |
/etc/letsencrypt/renewal-hooks/post/certbot-post-hook.sh |
Or add the –post-hook option to your certbot command to have it run immediately after creating a new cert with something like this:
1 |
certbot certonly --post-hook /etc/letsencrypt/renewal-hooks/post/certbot-post-hook.sh --webroot --webroot-path /var/www/html --cert-name example.com -d example.com -d www.example.com |
Now you can configure services to simply load the concatenated pem file instead of having to specify multiple files for the key, cert & chain. For example, an Apache configuration would look something like this:
1 2 3 4 5 6 |
<VirtualHost *:443> ServerName example.com ServerAlias www.example.com DocumentRoot /var/www/example.com/html SSLCertificateFile /etc/ssl/letsencrypt/example.com.pem </VirtualHost> |