Another “Let’s Encrypt” post for nginx

I’ve replaced the certificate on this site with one issued by Let’s Encrypt and plan to do so for all clients (or enable SSL in the first place) as their domains come up for renewal, or other maintenance work is contracted. The big downside is a 90 day expiry time, which requires a service nginx reload at least that often.

I had no end of issues using the official client as it wouldn’t create the .well-known/acme-challenge files necessary to get the domain to validate (yes, I checked directory permissions.) Instead, Vincent Composieux has some excellent instructions on just using the certonly parameter inside a script. Rundown, including my changes in case the article disappears:

  • clone letsencrypt repository to /opt/letsencrypt
  • create /usr/local/etc/le-example.com-webroot.ini:
# We use a 4096 bit RSA key instead of 2048
rsa-key-size = 4096

email = email@example.org
domains = example.com, www.example.com

authenticator = webroot

# This is the webroot directory of your domain in which
# letsencrypt will write a hash in /.well-known/acme-challenge directory.
webroot-path = /var/www/example.com/
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_dhparam /etc/ssl/dhparams.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

if ($myroot = false) {
        set $myroot $realpath_root;
}

location '/.well-known/acme-challenge' {
    root $myroot/;
    try_files $uri /$1;
}
  • For each site in /etc/nginx/sites-enabled, update the SSL definition to store the webroot in the $myroot variable, then have the root directive (and ssl.conf) reference it:
server {
        listen 443 ssl;
        server_name example.com www.example.com;
        # [...]
        set $myroot /var/www/example.com;
        root $myroot;
        include global/ssl.conf;
        # [...]
}
  • Create the certificate: sudo /opt/letsencrypt/letsencrypt-auto certonly --config /usr/local/etc/le-example.com-webroot.ini
  • Add the certificate paths to each site in sites-enabled:
server {
        # [...]
        include global/ssl.conf;
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
        # [...]
}
  • To automatically renew certificates 30 days before expiry, checking each day: ln -snf /usr/local/bin/renew-certificates.sh /etc/cron.daily/renew-certificates.sh

Some adjustments are obviously necessary for multiple sites but this got me past the point where site validation failed.