Getting a free TLS certificate from Let's Encrypt
Certified
Let's Encrypt [1] is an open-source project with the goal of making sure every website is encrypting with TLS. The Let's Encrypt project is sponsored by organizations such as Mozilla, Cisco, Akamai, EFF, gandi.net, and many more. The primary role of Let's Encrypt is to offer trusted and free TLS certificates for everyone. Users can even copy and integrate Let's Encrypt technology into their own networks, which means any website can now offer a encrypted option for no cost. On May 8th 2016, Let's Encrypt issued its one millionth certificate.
Building in Trust
Let's Encrypt requires that whoever requests a certificate must prove they control the website for which the certificate is intended. If you wish to receive the certificate for a website, the DNS entry for the domain must point to your IP address in DNS.
To request a certificate for http://www.hanscees.com, I had to run a script on the web server serving http://www.hanscees.com, asking the Let's Encrypt staging-server (LSS) for the certificate. The LSS asks the web server to present a secret file on a subdirectory of the website, checks it, and, if successful, hands over a certificate file. See the Let's Encrypt website for additional information [2].
You can use Let's Encrypt for any website regardless of the web server. For my home-grown sites, I use one ESXI VMware server and deploy pre-installed virtual machines using TurnKey Linux [3], a Debian-based series of pre-configured Linux application hosts. Deploying a new website takes me about 10 minutes.
To secure websites running on Joomla or other web-based applications, I use a front-end reverse-proxy. I recently switched from Nginx to Hiawatha [4] because the Hiawatha configuration makes it extremely easy to protect websites from SQL attacks. A front-end reverse proxy makes using Let's Encrypt much easier: instead of having to use a Let's Encrypt client on every web server, I can renew certificates for many back-end web servers in one script using one machine.
Getting a Free Let's Encrypt SSL/TLS Certificate
Let's Encrypt was a public beta when I wrote this article, and some of the procedures have changed, but this brief introduction will help you get started. Also, keep in mind that you might need to adjust these procedures based on your own configuration.
Consider taking a snapshot of your system or exporting the VM to be sure you can easily revert to the previous configuration in case of a problem.
To set up Let's Encrypt:
cd /root git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt ./letsencrypt-auto --server https://acme-v01.api.letsencrypt.org/directory --help
Now get some coffee and wait for a long download.
The first time you get a certificate, you need to run the commands manually, because you need to enter some manual input, such as an email address. Also, make sure you test with the staging server while you're learning how to use Let's Encrypt, because the real server has some built-in limits: if you try too many times, it will block certificate renewal for your domain for a week.
Start with the test server:
https://acme-staging.api.letsencrypt.org/directory
After you understand how Let's Encrypt works, you can try this using the production server:
https://acme-v01.api.letsencrypt.org/directory
To obtain a non-signed test certificate, change to the letsencrypt
directory:
cd /root/letsencrypt
The basic command for obtaining a certificate is:
./letsencrypt-auto certonly -a webroot --webroot-path /srv/www/example.com/ -d example.com -d www.example.com --server https://acme-staging.api.letsencrypt.org/directory
Next, you need to make sure:
- Your domain (as stated after the
-d
) is registered in DNS and resolves to the IP address of your web server (or proxy) - The
webroot
path you give in theletsencrypt-auto
command is correct, the script can write to that directory, and the web server actually serves that directory.
If you are testing a couple of times, make sure you keep a backup of your Let's Encrypt credentials:
tar -cvzf /root/letsencrypt.tgz /etc/letsencrypt
On my network, I enter:
./letsencrypt-auto certonly -a webroot --webroot-path /var/www/backends/ -d www.test-backend.com --server https://acme-staging.api.letsencrypt.org/directory
After you enter the command, you have to type an email address and agree to the terms of service [5]. If all goes well, you will get a message similar to the output shown in Listing 1.
Listing 1: Successful Certificate
Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/www.test-backend.com /fullchain.pem. Your cert will expire on 2016-03-27. To obtain a new version of the certificate in the future, simply run Let's Encrypt again. If you like Let's Encrypt, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le
If you are continually testing and getting errors saying the archive directory already exists, you might need to clean up some directories:
cd /etc/letsencrypt/ rm -rf archive/www.test-backend.com* rm -rf live/www.test-backend.com* rm -rf renewal/www.test-backend.com* rm -rf keys/* rm -rf csr/*
To use the Let's Encrypt certificate with Hiawatha, you need to process the files to the right format:
cd /etc/letsencrypt/live/www.test-backend.com/ cat privkey.pem cert.pem chain.pem > hiawatha-hc.pem chown www-data:www-data hiawatha-hc.pem chmod 440 hiawatha-hc.pem
When you have a new certificate, you must restart so the web server can use it:
service hiawatha check /etc/init.d/hiawatha restart
Now point your browser to the website and see if you get redirected to HTTPS or receive a warning because you are using the test environment and the certificate is not trusted.
Ignore the warnings and have a look at the certificate: mine was signed by "happy hacker fake CA."
A Real Certificate
Now that you have the process working, you can get a trusted certificate signed by Let's Encrypt.
First, clean up the testing stuff:
tar -cvzf /root/letsencrypt.tgz /etc/letsencrypt cd /etc/letsencrypt/ rm -rf archive/www.test-backend.com* rm -rf live/www.test-backend.com* rm -rf renewal/www.test-backend.com*
Next, get a the real signed certificate as follows:
cd /root/letsencrypt ./letsencrypt-auto certonly -a webroot --webroot-path /var/www/backends/ -d www.test-backend.com --server https://acme-v01.api.letsencrypt.org/directory
If a congratulations follows, you can prep the cert (remember this is for Hiawatha, the instructions will vary if you use a different web server):
cd /etc/letsencrypt/live/www.test-backend.com/ cat privkey.pem cert.pem chain.pem > hiawatha-hc.pem chown www-data:www-data hiawatha-hc.pem chmod 440 hiawatha-hc.pem
Restart Hiawatha, and you should see your website without HTTPS warnings. If you examine the certificate, you'll see that it says:
Issued by: Let's Encrypt Authority X1
You now have an officially signed and trusted certificate. Your website just became one of many secured sites using Let's Encrypt for a safer internet.
Automating Renewal
The Let's Encrypt certificates are designed to expire in three months. This expiration ensures the certificates are "always fresh" and, after a breach, they cannot be used for long.
This short lease period means you might want to automate renewal to save yourself the trouble of repeating the installation process.
For the first renewal, I recommend letting cron take care of it, but then do a check to ensure that all went well. I scripted a proof-of-concept renewal script that I run every week (Listing 2). In addition, I have a second script that alerts me on certificates that are about to expire. The script in Listing 2 does the job, but please post better scripts as you make them. The script will probably work if:
- Your Hiawatha configuration is working for the domains you are serving (so your websites are reachable and the web server can write to the
webroot
directory) - You have manually verified Let's Encrypt works
- You have adjusted all lines that say
#adjust
with settings relevant for your network - You are
root
when you run the script (it can run as a user but you should adjust thetmp
file)
Listing 2: Automated Renewal
01 #!/bin/bash 02 # hanscees@hanscees.com version 27-03-2016 03 #This script will renew one or multiple Let's Encrypt domains 04 # by default it uses the staging server. Adjust to use life Let's Encrypt server 05 # Let me know if you built a better script 06 07 WEBROOT="/var/www/backends/" 08 #we will get certificates for the following domains 09 DOMAINS="www.test-backend.com www.backend.com www.backend.net" #adjust 10 EMAILADMIN="hanscees@hanscees.con" #adjust 11 12 LECROOT="/etc/letsencrypt/live" 13 mkdir /root/letsencrypt #justincase 14 15 #lets get certs 16 echo "will get the certs now" & sleep 3 17 for i in `echo $DOMAINS` ; do 18 echo "getting certs for $i" 19 20 #If certs do not exist yet 21 FILE="$LECROOT/$i/cert.pem" 22 if [ ! -f "$FILE" ] 23 then 24 echo "$FILE does not exists, so lets get certificates" 25 cd /root/letsencrypt 26 #using staging server? Adjust if neccesary 27 ./letsencrypt-auto certonly -a webroot --webroot-path $WEBROOT -d $i --server \ 28 https://acme-staging.api.letsencrypt.org/directory 29 #./letsencrypt-auto certonly -a webroot --webroot-path $WEBROOT -d $i --server \ 30 https://acme-v01.api.letsencrypt.org/directory 31 sleep 33 # can take a while 32 else 33 echo "there is already a certificate, lets test its age" 34 sleep 3 35 36 #only get certs if the current certs arent very youngh: age test 37 if test `find "$LECROOT/$i/cert.pem" -mtime +71` 38 then 39 echo "certificates exist and are rather old" 40 # so lets get new ones 41 cd /root/letsencrypt 42 #using staging server? Adjust if neccesary 43 ./letsencrypt-auto certonly -a webroot --webroot-path $WEBROOT -d $i \ 44 --server https://acme-staging.api.letsencrypt.org/directory 45 #./letsencrypt-auto certonly -a webroot --webroot-path $WEBROOT -d $i \ 46 --server https://acme-v01.api.letsencrypt.org/directory 47 sleep 33 # can take a while 48 else 49 echo " certificates exist, but apparently are very fresh, do not get new ones" 50 sleep 3 51 # notify and exit this loop iteration, continuing with the next 52 echo "certificate $i not refreshed, they are very new so no problem" | 53 mail -s no-need-refresh-cert-$i $EMAILADMIN 54 continue 55 fi # age test 56 57 fi # does cert file exist 58 59 60 #if all is well we have a new certificate, but we need to adjust it to hiawatha pem format 61 #check if pems are indeed new, or skip, could be an error right? 62 #cd $LECROOT/$i 63 if test `find "$LECROOT/$i/cert.pem" -mmin +3600` 64 then 65 # certificates are old, not refreshed, has been an error 66 # notify and exit this loop iteration, continuing with the next 67 echo "certificate $i not refreshed, send fire department" | mail -s problem-cert-$i $EMAILADMIN 68 continue 69 else 70 # certs are fresh, so lets make a new pem #adjust for non-hiawatha webserver 71 echo "certs $i are in lets make a pem" 72 cat $LECROOT/$i/privkey.pem $LECROOT/$i/cert.pem $LECROOT/$i/chain.pem > $LECROOT/$i/hiawatha-hc.pem 73 chown www-data:www-data $LECROOT/$i/hiawatha-hc.pem 74 chmod 440 $LECROOT/$i/hiawatha-hc.pem 75 echo "pemfile is $LECROOT/$i/hiawatha-hc.pem" 76 fi 77 done 78 79 # todo, built in some test? 80 /etc/init.d/hiawatha restart #adjust 81 82 echo "letsencrypt certificates $DOMAINS update just ran, sending email to $EMAILADMIN" 83 echo "letsencrypt certificates $DOMAINS update just ran, please check your websites" | 84 mail -s "letsencrypt-update-$DOMAINS" $EMAILADMIN
To create the script, enter
mkdir /root/scripts vi /root/scripts/updatecerts
and copy the lines in Listing 2 to the updatecerts
file. Then, change the permissions for the file with the following:
chmod +x /root/scripts/updatecerts
Make the necessary modifications to the lines labeled #adjust
, and run the script with:
/root/scripts/updatecerts
If you want to run this script from cron every 4 weeks or so, add a line like the following to cron:
cron crontab -e 30 03 01 */2 * /root/scripts/updatecerts >> /var/log/cron.log 2>&1
If you will be using this script, I strongly recommend you run it a few times manually using the stage area!
To check if your certificates will expire within 4 weeks, you can run the script shown in Listing 3.
Listing 3: expire-check
01 #!/bin/bash 02 # hanscees@hanscees.com version 28-12-2015 03 04 DOMAINS="www.test-backend.com www.thisisagreatwebsite.com" #adjust 05 EMAILADMIN="hanscees@hanscees.con" # adjust 06 07 LECROOT="/etc/letsencrypt/live" 08 for i in `echo $DOMAINS` ; do 09 #checkend is seconds. 1 week = 604800 sec 4 weeks 2419200 16 weeks = 9676800 10 if openssl x509 -checkend 2419200 -noout -in $LECROOT/$i/cert.pem 11 then 12 echo "Certificate is good for another 4 weeks!" 13 else 14 echo "Certificate $i will expire within 4 weeks! (or is invalid/not found)" 15 echo "Certificate $i will expire within 4 weeks! (or is invalid/not found)" | 16 mail -s "TLS certificate $i will expire act now" $EMAILADMIN 17 fi 18 done #end forloop
Conclusion
In this article, I described how to get a free trusted TLS certificate on a Linux web server and also how you
might automate the process, because you will need to refresh your certificates. Have fun setting up many TLS-protected websites with Let's Encrypt.