Let's Encrypt

![Let’s encrypt] ({filename}/static/images/letsencrypt.svg)

This Article is all about the SSL cert signing and auto signing for Let’s Encrypt. I only cover nginx based setup and does require root and knowledge of some basic nginx vhost configuration.

Let’s start with what Let’s Encrypt is and how to use it. It is a public CA that signs for free, there are some limits but reaching them is not likeley in most cases. To Get a valid cert they use their own application that is developed in tho open on this github page.

Installing the client is pretty simple:

user@server:~$ git clone https://github.com/letsencrypt/letsencrypt
user@server:~$ cd letsencrypt
user@server:~/letsencrypt$ ./letsencrypt-auto --help

Now that you have the client you can request a first cert:

./letsencrypt-auto certonly -a webroot --webroot-path=/var/www/your/web/root -d test.yourdomain.com

For this to work you need the have:

  • the domain test.yourdomain.com going to the dir /var/www/your/web/root
  • write permisions in the folder
  • allow for the following location to be reached from a external server http://test.yourdomain.com/.well-known/acme-challenge/

The last point is vital for checking if you are the owner of the domain and if you are like me and 403 everything starting with “.” then this will fail and you need to work around it in your vhost conf.

I tackle this all in one simple nginx include:

user@server:~$ cat /etc/nginx/includes/ssl-validate.conf
location ~ acme-challenge {
    root        /etc/letsencrypt/temp;

This sets the root dir for anything that looks like a acme-challenge to: /etc/letsencrypt/temp This needs to be included in the top of every port 80 vhost you want to have this work for.include /etc/nginx/includes/ssl-validate.conf;

That was half of the equation the “webroot” part of it now we need to get some domains in a list, a script to request cert and a cronjob to do this automnomous.

First you need to put the repo checkout in a sensible place:

user@server:~$ cd /usr/local/bin/
user@server:~$ git clone https://github.com/letsencrypt/letsencrypt

Then we need a script to check and manage the certs:

user@server:~$  cat /usr/local/bin/ssl-renew
#vhosts=$(ls -lah /var/log/nginx/*access*log |awk '{print $9}' | sed 's/\/var\/log\/nginx\///g' | sed 's/.access.log//g' |grep -ve '*\|access.log' |uniq |sort -h)


export DIR=/etc/letsencrypt/temp
mkdir -p $DIR

for i in $vhosts
  echo "-------------------------------------------------------------------------------------------"
  echo $i

  if openssl x509 -checkend 604800 -noout -in /etc/letsencrypt/live/$i/cert.pem
    echo "Certificate is good for at least another week"
    echo "Certificate has expired or will do so within 7 days!"
    echo "(or is invalid/not found)"
    echo "starting renew procedure"
    /usr/local/bin/letsencrypt/letsencrypt-auto --renew certonly -a webroot --webroot-path=$DIR -d $i
    /usr/sbin/nginx -t && /usr/sbin/nginx -s reload


As you can see there are 2 ways of loading the vhosts in to the loop from a simpje list of from the names in the nginx access logs my filenames look like: some-subdomain.aapjeisbaas.nl.access.log

For now just stick with the list if you need more flexibility you cat try the nginx access log trick.

This script checks the cert is in the default location and if it is valid for at least a week. If one of those things is not true it will request if it is not found or renew a cert. (for extra security remove --renew to generate a new key for every cert.)

Now chmod the file, run it and check output debug where needed. If all goes well add it to a cron file:

user@server:~$  cat /etc/cron.d/ssl-check 
7 2 * * * root  /usr/local/bin/ssl-renew | /sbin/sendmail -F mail@domain.com -t mail@domain.com

The certs are in /etc/letsencrypt/live/ every domain has their own folder. Here is a example nginx conf for the test domain.

user@server:~$ cat /etc/nginx/conf.d/test.domain.com.conf
server {
    listen      80;
    listen      [::]:80;
    server_name test.domain.com;
    include     /etc/nginx/includes/ssl-validate.conf;
    location / {
        return      302 https://test.domain.com/;
server {
    listen      443;
    listen      [::]:443;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/test.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/test.domain.com/privkey.pem;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    resolver your.closest.dns.resolver; # maybe use if you want

    server_name test.domain.com;

    include     /etc/nginx/includes/log.conf;

    root        /var/www/vhosts/test.domain.com;

    location / {
      add_header "Cache-Control" $cacheable_types;
Stein van Broekhoven
Cloud & Open-Source magician 🧙‍♂️

I try to find the KISS in complex systems and share it with the world.

comments powered by Disqus