Let's Encrypt design

We want the internet to be confident who we are. As shown in the Certificate Authority design, we have our own Certificate Authority. But now we want to get our keys signed by the Let's Encrypt service. We will add the Let's Encrypt functionality to our certificate_signed role so that you can ask for a local self-signed certificate or a let's encrypt certificate. This image illustrates how it works:

OpenSSL layout

The Root Certificate Authority is a service run by Let's Encrypt, and it will automatically sign certificates using its own API. These certificates last three months, at which time you will need to fetch a fresh one.

The certificate client is our own web server (the NGINX Docker container), which needs to be visible on the Internet on both HTTP and HTTPS. This functionality will be provided by our own NGINX Docker image running on the webserver. The web server will provide the HTML content. There are two sets of information: the actual content of your website, which you write yourself, and the ACME handshake. This is a randomly numbered subdirectory under your webserver. It contains the evidence that the Certbot asking for the certificate is really the one controlling that website. Let's Encrypt will try to access that on the URL provided using HTTP on port 80 of the URL provided.

So every month or so, our Certbot will wake up and:

  1. Generate a new private key for the website.
  2. Generate a Certificate Signing Request (CSR) from this key.
  3. Send the CSR to Let's Encrypt over their API.
  4. Let's Encrypt will send CertBot a challenge:
    • Please put this information into http://www.example.com.
  5. Certbot will put the information where it is supposed to go and acknowledge this to Let's Encrypt
  6. Let's Encrypt will try to access the file.
  7. If it finds what it is expecting, let's Encrypt will send a new certificate to CertBot
  8. CertBot will save that certificate in its own /etc/letsencrypt/live directory.
  9. Our playbook will then copy that certificate where it needs to go.

And then, certified access to the website can continue.

References

Certbot Docker image

We use the core Certbot image from Dockerhub: certbot/certbot. We can download this as follows:

# docker pull certbot/certbot
Using default tag: latest
latest: Pulling from certbot/certbot
Digest: sha256:c23159d30afdd9c97960578aa4654f5901de6cae394958f894074dedd55e599d
Status: Image is up to date for certbot/certbot:latest
docker.io/certbot/certbot:latest

Directory structure

This is the directory structure for Let's Encrypt images:

Host Container Contents
/local/www /local/www Root directory for the web server, also used by the NGINX container and Apache HTTPD
$DOCKER/apps/www-nerdhole/ - Application home ($APPHOME) for www-nerdhole
$APPHOME/etc/letsencrypt /etc/letsencrypt Let's Encrypt configuration files
$APPHOME/var/lib/letsencrypt /var/lib/letsencrypt Run files for Let's Encrypt
$APPHOME/var/log/letsencrypt /var/log/letsencrypt Log files for Let's encrypt

Because we want to look at these files directly, we will mount the directories under the Docker Home, here shown as $DOCKER_HOME, which is /local/docker. The Certbot will see the files in /etc/letsencrypt, where Docker host will see them in /local/docker/apps/www-nerdhole/vol/etc/letsencrypt:

  • /local/docker - The docker home
  • apps/ - The place where we store application files
  • www-nerdhole - The name of the application
  • vol/ - Where we store the volumes for the application
  • etc/letsencrypt - The particular volume.

Note that we have only one set of volumes here for all containers. If we have multiple containers with separate directories, we add another level for this. The certificates are stored in /etc/letsencrypt/live/www-nerdhole/:

  • privkey.pem : the private key for your certificate.
  • fullchain.pem: the certificate file used in most server software.
  • chain.pem : used for OCSP stapling in Nginx >=1.3.7.

We will use the full chain file as required by NGINX. The certificate_signed role takes parameters to specify where the various files need to go, so we will diverge from Certbot recommendations and copy the file there after we get it.

Certbot initial setup

We have the webserver running in a separate docker container with its certificate and content directories imported from the host. Hence, the Certbot docker image will not configure NGINX for us. We will be using Certbot using the "webroot" method: It will put the responses to the challenges it receives into the HTML directory when asked. In our case. the web data is in /local/www/, and the challenge will be stored in /local/www/.well-known/acme-challenge/$RANDOM_STRING.

The certbot command to achieve this is:

certbot certonly \
--webroot \
--webroot-path /local/www \
--cert-name www-nerdhole \
--domain www.nerdhole.me.uk \
--email certs@nerdhole.me.uk
  • certonly - Specifies that Certbot is not to configure the web server, only get the certificate.
  • webroot - A Certbot plugin that answers the challenge by putting content into the web server for Let's Encrypt to verify.
  • webroot-path - Where the web server's root directory is
  • cert-name - Name of the certificate. Used in various directory names.
  • domain - The name of the server this certificate is for. If your server has multiple names, you can specify them all in additional --domain lines.
  • email - An email address where Let's Encrypt can reach you on certificate related issues. I created a special alias.

The first time you run this through Docker Run, you will be asked to agree to Let's Encrypt's Terms of service and sign up to its newsletters. You need to do that by hand. After you do this, the next requests are non-interactive suitable for automation.

Using Let's Encrypt

We can use the certificate_signed role for any service, and specify where we want the certificate and key to go. This is how we specify the new key:

roles:
- role: certificate_signed
  action: install
  service:
    name: www-nerdhole
    keyname: my-service
    key_directory: /local/mykeys
    certificate_directory: /local/mycerts
    subjectAltNames:
    - www.nerdhole.me.uk
    scope: letsencrypt
    webroot: /local/www
  • action - Either "install" or "renew".
  • service - Information on the service this certificate is for
    • name - Name of the service, used for directory and filenames
    • keyname - Name of the key for when you have multiple keys per service
    • key_directory - Where to put the private key on the host
    • certificate_directory - Where to put the certificate on the host
    • subjectAltNames - List of DNS names where the service can be found on the net
    • scope - What kind of key you need:
      • local - Self-signed certificate from the local Certificate Authority
      • letsencrypt - Globally-accepted key from Let's Encrypt
    • webroot - Directory on the host where the web pages are, only needed for Let's Encrypt