At this point, you have probably heard of Let’s Encrypt, a CA that provides free SSL certificates. Obtaining a free SSL certificate for a domain only requires proof of ownership. Doing so is very straightforward using their command line utility,
letsencrypt. Because they do a great job explaining how it works behind the scenes, this article will focus on quickly getting a certificate for the domain “mydomain.acme.com”.
Simply put, the registration process looks like this:
- Let’s Encrypt registration command is run for “mydomain.acme.com”
- The command generates a secret path on the host running the command (ex.
- The command then asks that the validation servers perform a HTTP
GETon that path (ex.
- If the server response content matches that of the generated secret, then the certificates will be downloaded to
/etc/letsencrypton the machine running the command
In this article, I’ll briefly should how to use Docker to host a webserver via a nginx container and then start a Let’s Encrypt container to register our domain. Finally, we will copy the certificates from the Let’s Encrypt container to the hosts we are running the Docker commands on so we can distribute them elsewhere.
If you already have webservers running for your domain, then you’ll probably need to do some tinkering with your loadbalancer to proxy the traffic from the Let’s Encrypt servers to the right place in your environment.
First, we are going to need:
- The ability to update the domain’s A record
- An API access token for Digital Ocean (or a different Docker Machine driver)
- The host to be publically accessible over HTTP (80/tcp)
To start off, we will run Docker Machine with the Digital Ocean driver to create a brand new server instance and install the Docker daemon. The command looks like this:
$ docker-machine create -d digitalocean \ --digitalocean-access-token <access_token> \ letsencrypt
Once Docker is installed on our new instance, we can setup our environment to use the remote Docker daemon:
$ eval $(docker-machine env letsencrypt)
And verify it is working with:
$ docker info
Finally, we need the public IP of the instance so we can update our domain’s A record:
$ docker-machine ip letsencrypt
Depending on your DNS provider, the steps for updating the domain’s A record with the public IP of the instance will look different. For Amazon’s Route53 service, it looks like the following:
Now come’s the fun part. We start by running an Nginx container listening on port 80 and exposing the container path
/usr/share/nginx/html as a volume. This will serve up the verification files for the Let’s Encrypt registration process.
$ docker run -d \ -p 80:80 \ --name nginx \ -v /usr/share/nginx/html \ nginx
Next we will start the Let’s Encrypt container:
$ docker run -it \ --name letsencrypt \ --volumes-from nginx \ quay.io/letsencrypt/letsencrypt \ certonly \ --agree-tos \ --webroot \ --webroot-path /usr/share/nginx/html \ -m [email protected] \ -d mydomain.acme.com
Let’s take a look at what’s going on here. The
--volumes-from flag allows for the “letsencrypt” container to modify files on the “nginx” container’s filesystem at the specified path (
/usr/share/nginx/html). By running the
letsencrypt container with the
--webroot --webroot-path /usr/share/nginx/html flags, the generated secret path will automatically be served up by the nginx container. Finally, we provide an email address (
-m [email protected], not used for verification) and the domain we wish to secure (
If successful, the certificates will be downloaded to the letsencrypt container under
/etc/letsencrypt. To retrieve them, we can copy them straight out of the container and onto our local box:
$ docker cp letsencrypt:/etc/letsencrypt/ letsencrypt
To view the certificates:
$ ls letsencrypt/archive/mydomain.acme.com/ cert1.pem chain1.pem fullchain1.pem privkey1.pem
We can also verify that the Let’s Encrypt validation servers hit our nginx container:
$ docker logs nginx x.x.x.x - - [21/Apr/2016:20:45:32 +0000] "GET /.well-known/acme-challenge/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)" "-"
Finally, we can cleanup our Docker containers
$ docker rm -f nginx letsencrypt
Or just erase the entire instance altogether:
$ docker-machine rm letsencrypt