Using Letsencrypt for TLS

Scripting for simplicity

In previous posts I pointed out why TLS is important, how to configure Apache to score an A+ and how to tune HTTP headers. All this is dependent on getting an SSL cert.

Some jargon explained

Before we delve into a “how to”, some basic jargon should be explained:

SSL/TLS

TLS (“Transport Layer Security”) is the successor to SSL (“Secure Socket Layer”). SSL was created by Netscape in the mid 90s (I remember installing “Netscape Commerce Server” in 1996). When you hear people talk about SSL and TLS they pretty much use the terms interchangeably. You can think of it as a progression SSL2, SSL3, TLS1.0, TLS1.1, TLS1.2 with each version better than the previous.

SSL/TLS is what makes the connection to the server secure.

SSL Certificate

This is a file you place on your server and this basically says “I am server x.y.z”. The server for this website says “I am server www.sweharris.org”. There are other things this certificate can say (”I can also be called sweharris.org”). Different certificates can also be used for other reasons (e.g. signing code), but I won’t go into that here.

The SSL certificate is signed by a trusted party (the “Certificate Authority” - CA), so when your browser goes to a web site it can verify the certificate is valid. Your browser has a list of CAs that the browser creator trusts. If I tried to create a certificate for “google.com” then no CA would sign it, and your browser would say “Nope!”

Domain Validation

In order to get a certificate signed the CA needs to know that you are who you claim you are. “Domain Validation” (DV) is the weakest and easiest method of validating ownership. This may be done by sending an email to a standard address (eg postmaster or hostmaster). It might be done by requiring you to put a specific file on the web server. Or changing a DNS entry… the idea is to require the requester to prove they have access to a controlled resource associated with the domain.

This techique is used elsewhere; e.g. if you put your website into Google Webmaster Tools then it will require you to put a specific entry on your server (eg /googleSOMESTRING.html). If you want to use Google Apps for your mail then it may want a DNS entry (eg googleSOMESTRING IN CNAME google.com.). These are both “domain validation” examples.

Extended Verification

Extended Validation (EV) certs take a deeper approach to validating the request; they also check the owner claims. So, for example, I might be able to get a DV cert for chas3.com, but I would not be able to get an EV cert saying “this is owned by JPMorgan Chase”. An EV cert is meant to help reduce phishing attacks by helping the web user spot if an EV cert is in use.

Compare my site to Chase’s:

mysite
chase

We both have the green padlock, showing the cert’s are good, but the Chase entry has more details about the owner next to the padlock.

For a site like mine a DV cert is sufficient. You need to decide if you want a DV or EV for your site.

Getting a cheap cert

When I first started out in 1996 getting a cert was a hard tedious process. We had to fax (fax!) details to one of the very few CAs that were around, and it took weeks to get the cert. Painful. It was effectively an EV process, long before such a thing really existed.

As time went on the DV process became easier to get, but they still cost money. It wasn’t really worth it for most people.

In 2012 I noticed that StartCom were providing free DV certs, under the brand StartSSL. This was an Israeli company and their CA was a common one. It made sense.

The StartSSl process was pretty painful. It was totally manual and involved using a web site with cut’n’paste. The DV process expired after 30 days so every renewal required revalidation. But it was free, and I only had 7 certs with them, so I could jump through the hoops every so often.

Unfortunately, it’s not clear if StartSSL is still trustworth, and may now operate from China.

Letsencrypt

Fortunately Letsencrypt started up. Their goal was to automate a lot of the manual steps, while still providing free DV certs. They even provide a client so you can basically say “encrypt my site” and their software will create the necessary cert files, do the DV process (stick a file on the server), retrieve the signed result, reconfigure your webserver daemon and, boom, in seconds your site is now SSL enabled. The certificate is only valid for 90 days, but that’s not a problem; the same software can automatically renew the cert before it expires.

Very impressive.

Unfortuntately it doesn’t work for me. My site is replicated across multiple providers (one copy at Linode in Texas; one copy at Panix in New York; one copy at OVH in Canada). I manage content on my home machine and rsync the results out. I also manage the webserver configurations via ansible.

If I ran their client on one machine then I’d have to copy the resulting certs to the other two… it’s doable, but messy.

However there was another way of doing the DV certification, and that’s via DNS. Now my DNS is also replicated (hidden primary server at home; public primary at the three sites). So this means I could do the cert DV validation via DNS from my home machine, then push the resulting cert out to the web servers via ansible.

The original Letsencrypt client couldn’t do DNS validation, but Lukas Schaur created an alternative shell based client called deydrated. This is a much simpler program, but allowed DNS mode to be used, and had a call out so I could modify how the DNS change ran.

How it works

Last week one of my certs needed renewing, so I went through the renew process:

$ cd etc/localnet/Letsencrypt/
$ ./refresh
 + Valid till Dec  6 23:35:00 2016 GMT (Less than 8 days). Renewing!
 + Signing domains...
 + Generating private key...
 + Generating signing request...
 + Requesting challenge for linode.spuddy.org...
 + Responding to challenge for linode.spuddy.org...
 + Challenge is valid!
 + Requesting certificate...
 + Done!
 + Creating fullchain.pem...
/home/sweh/etc/localnet/PlayBooks/Files/Postfix/Per-host/linode

The refresh command calls ./dehydrated -c to do all the hard work of talking to the Letsencrypt servers. It noticed that the linode cert needed renewing, and got a new signed cert. My script then compared all the managed certs and copied any changed ones into the ansible tree. In this case the single cert for the linode Postfix server (also used by INN) was updated.

Since a change was done I could then use ansible to push it out:

$ cd ../PlayBooks/
$ ansible-playbook -l linode postfix-inn.yml 

PLAY [Set up postfix config on mail servers] ********************************** 

[...]

TASK: [send machine specific certs] ******************************************* 
changed: [linode] => (item=privkey.pem)
changed: [linode] => (item=fullchain.pem)

NOTIFIED: [restart postfix] *************************************************** 
changed: [linode]

PLAY [Set up INN] ************************************************************* 

[...]

TASK: [send INN privkey] ****************************************************** 
changed: [linode]

TASK: [send INN cert chain] *************************************************** 
changed: [linode]

[...]

PLAY RECAP ******************************************************************** 
linode                     : ok=21   changed=4    unreachable=0    failed=0   

And that’s it! I can also verify the new certs are in place and working

$ cd ../Letsencrypt
$ ./check_remote nntp linode.spuddy.org
             linode.spuddy.org NNTP: OK notAfter=Feb 27 16:31:00 2017 GMT
$ ./check_remote smtp linode.openvpn linode.spuddy.org
             linode.spuddy.org SMTP: OK notAfter=Feb 27 16:31:00 2017 GMT

How to know when to renew a cert

It would be bad to allow a cert to expire, so you need ample warning of when a cert needs to be refreshed. Because of the tree used by the dehydrated code is consistent this is very easy to script:

#!/bin/ksh -p

# Warn if cert will expire in this many days
let DAYS=${DAYS:-7}
let warn=DAYS*86400

typeset -R25 h
bad=""

cd /home/sweh/etc/localnet/Letsencrypt/certs || exit
for a in */cert.pem
do
  b=$(openssl x509 -checkend $warn -in $a)
  if [ "x$b" = "xCertificate will expire" ]
  then
    b=$(openssl x509 -noout -dates -in $a | sed -n 's/^notAfter=//p')
    h=${a%/*}
    bad="$bad\nletsencrypt $h: $b"
  fi
done

if [ -n "$bad" ]
then
  print Following certificate expiration dates:
  print "$bad"
fi

So I can now see what certs are due to expire:

$ checkssl
$ DAYS=20 checkssl
$ DAYS=25 checkssl
Following certificate expiration dates:

letsencrypt           bofh.spuddy.org: Dec 28 23:46:00 2016 GMT
letsencrypt        gallery.spuddy.org: Dec 28 23:47:00 2016 GMT
letsencrypt          games.spuddy.org: Dec 28 23:47:00 2016 GMT
letsencrypt           home.spuddy.org: Dec 28 23:47:00 2016 GMT
letsencrypt       mercury7.spuddy.org: Dec 28 23:48:00 2016 GMT
letsencrypt     panix-news.spuddy.org: Dec 28 23:48:00 2016 GMT

This is easy to put into a cron job. Indeed

1 1 * * * /home/sweh/bin/checkssl

In the morning I can refresh any certs necessary while drinking my coffee :-)

Automation

So far I’ve only automated 90% of the job. I still have to run the refresh and ansible-playbook commands. Partly this is because it’s so easy, partly because it gives me positive feedback (I know the work was done and the push successful), but mostly because this is “good enough” for my use case.

When I started using this process I would occassionally get errors with the DV step; I’m not sure if that was due to DNS propagation issues or caching at the LetsEncrypt side or something else. I haven’t had a problem for quite a while now, but I like to keep my eye on it.

But we can see, quite easily, how a daily “refresh/push” process could be created so the overnight “checkssl” job could become a “check and refresh” job.

Currently I have 12 certificates managed this way, and it takes less than 5 minutes a month to manage.

Summary

Where DV certs are sufficient, a tooling around Letsencrypt is perfectly adequate and sensible. It can even be used for internal web sites, as long as DNS challenge/response can be handled.

There’s little excuse to not use SSL on web sites any more!