Lets Encrypt is a new Certificate Authority (CA), run for the public’s benefit by the Internet Security Research Group (ISRG). At the time of writing it’s currently in Beta and is due to go public in December 2015.
Update: Lets Encrypt went into public -beta on December 3 2015. I have updated this article with the minor change needed to work with the live servers.
Now in the default mode, the standard Lets Encrypt client (it’s not the only one) can manage this automatically – however it’s not ideal if you have more than one server.
What I describe here is how to centralize managing certificate registration (& later renewal) on a central machine. When a certificate is then registered or renewed we can then copy the certs to the remote servers.
The way the ACME protocol (I keep thinking of a certain Coyote when I read that) works is that the client will send the request to the CA and the CA will then verify that the domain is valid and under the client’s ‘control’ by requesting a token back from the domain to confirm that the domain is under the requester’s control.
Now with the standard client it has two standard modes: auto and manual. In addition it also has plugins, of which we’ll be using the webroot plugin as it doesn’t need any apache server restarting and there’s no messy editing of challenge files on each remote server.
So what we’ll do here is to configure a local machine for handling the client and get each apache to forward requests to it when the CA requires it. Once setup you can then leave it configured so it’s available when we come to renewal the certificates.
Note: I’m using a Raspberry PI here but it can be any Linux server. I used a PI as I had one spare (lost count when I hit 20 of them) and it’s easy to secure when not in use – i.e. put the SD card (or the entire PI) into a safe for example.
So what we’ll do is to configure two machines.
- A raspberry PI on the local network. This will run the LetsEncrypt client.
- The mod_proxy module enabled on the remote public Apache server(s).
Now the PI needs to be semi-accessible from the internet. Full access isn’t necessary just port 80 has to be accessible from the outside world – specifically from the public apache servers (not the CA).
For the purposes of this article this will have the hostname client.example.com configured in DNS.
To help limit access to it from port scans etc I’ve got this set with an AAAA entry (i.e. an IPv6 address). There’s no access to the PI from the old IPv4 internet. However this only works for me as my remote apache servers also have full IPv6 connectivity. If your’s don’t have that then the PI needs to have port 80 on a static IPv4 address.
First we need to install Apache, Git and checkout the latest version of the client:
sudo su - apt-get update apt-get upgrade apt-get install apache2 git git clone https://github.com/letsencrypt/letsencrypt
There’s more info in the installation instructions however when we first run the client it should install all other dependencies.
Also you don’t have to be root for all of this but as we need to run letsencrypt as root I did the checkout as root. It also means the client is installed in root’s home directory.
Configure the local Apache
Now on the PI we have Apache installed but we need to configure it. First enable headers:
sudo a2enmod headers
Next create /etc/apache2/conf.d/letsencrypt.conf with the following:
<IfModule mod_headers.c> <LocationMatch "/.well-known/acme-challenge/*"> Header set Content-Type "text/plain" </LocationMatch> </IfModule>
What this will do is ensure that any challenge is returned with the text/plain content type.
Next create the .well-known directory that the client will use and Apache will serve: Note this can vary depending on what version of Apache you have installed.
sudo mkdir /var/www/.well-known
restart apache and go to http;//client.example.com/.well-known you should see a directory listing.
Configure the remote Apache
On the remote apache servers we need to configure each virtual host to proxy .well-known to our new backend server. This only needs to be done on each http (port 80) host. In this example we’ll use example.com and http://www.example.com.
For this we need to add the following:
ProxyPass /.well-known http://client.example.com/.well-known ProxyPassMatch /.well-known !
<VirtualHost *:80> ServerName example.com ServerAlias www.example.com ServerAdmin firstname.lastname@example.org AllowEncodedSlashes On ProxyPreserveHost On # Lets Encrypt server ProxyPass /.well-known http://client.example.com/.well-known ProxyPassMatch /.well-known ! ProxyPass / http://backend.example.com:8080/ ProxyPassReverse / http://backend.example.com:8080/ </VirtualHost>
Now if you already have ProxyPass definitions in there then this must be the first one (we’ll come to why shortly). Also if you haven’t got mod_proxy enabled then you’ll need to enable it:
sudo a2enmod proxy sudo a2enmod proxy_http
Now test apache:
sudo apache2ctl -t
and if all goes well restart it. Now if you go to http://example.com/.well-known/ you should now see the directory on the pi visible.
Configuring with existing proxies
I hinted above that the ProxyPass entry needs to be the first one if you already have proxies configured. The reason is that we need to ensure that all requests to /.well-known is passed through to the backend server.
For me a common proxy is where I have Apache fronting either PI based webcameras or in this instance Apache Tomcat which runs most of my sites. Now as I have this running from the root, Apache proxies everything to tomcat.
So by defining /.well-known first we can then ensure that directory goes to a different proxy. The key is the ProxyPassMatch line. That ensures that all proxies after that point do not intercept this. As / is above /.well-known it won’t work, but here it does because we’ve told it not to handle this new proxy.
Issuing a certificate
Now we’ll create a certificate. Back on the pi, log in as root & presuming the client is in root’s home directory create the certificate.
Here we’ll create a certificate for two domains, example.com and http://www.example.com. Each one needs to be defined together with the -d parameter for each one.
cd letsencrypt/ ./letsencrypt-auto certonly -a webroot \ --webroot-path /var/www \ -d example.com -d www.example.com
Edit: Originally this had the following included in the above. However since LetsEncrypt went public-beta it’s no longer needed:
--server https://acme-v01.api.letsencrypt.org/directory \ --agree-dev-preview
It will take some time, more so for the first time you’ve run this but when it’s done it should show:
– Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/example.com/fullchain.pem. Your cert will
expire on 2016-02-16. To obtain a new version of the certificate in
the future, simply run Let’s Encrypt again.
Installing the certificates
You’ll now find the certificates are now present in a subdirectory of /etc/letsencrypt/live. The name of the directory will be the first directory when you created the cert and within it 4 files:
- cert.pem – this is the certificate
- privkey.pem – the private key for that cert.
- chain.pem – the certificate chain file which links the cert to the CA
- fullchain.pem – the full chain.
All you need to do is to copy those files to each public apache that will host those domains and configure apache to use them.
Now it’s best to copy all of them. Apache requires the private key to handle it’s end of the encryption.
The fourth file, fullchain.pem is also needed. As the CA is new most browsers don’t have it defined as a trusted root. Until browsers pick up the new CA (usually in an update) the certificate will not be trusted so this file contains the chain.pem as well as another certificate from an intermediary CA which is already a trusted root. At some point in the next year this file will not been needed.
Anyhow I’ll leave how you copy this up and configure this to you as a future exercise. This part has been written about elsewhere and it’s pretty standard. Same goes for renewing the certificates. Perhaps you’ll figure out how to fully automate this with your setup.