Let's Encrypt Wildcard Certificate Configuration with AWS Route 53

Let's Encrypt is a great way to upgrade your websites to use https or SSL. If you're using a fairly common/basic setup it's fairly straightforward to configure your server to use Let's Encrypt certificates.

However for a site I was working on, we needed the recently supported wildcard domains as our site provided vanity subdomains to our users. This means dynamically created domains like chris.example.com can be made to use https without needing to manually create a new certificate for each subdomain. Unfortunately there was little clear documentation for configuring all this unless you were an expert magician trained in the magic that is Linux system administration and the black magic that is networking and DNS.

The website I was doing this for was a Node.js site running behind an Nginx proxy on an AWS EC2 Ubuntu instance, and the domain was managed by Route 53.

For the purposes of creating and using an SSL certificate, you can assume the node.js server didn't exist as the setup would be the same as for a site running solely on Nginx. This guide assumes you haven't installed the Let's Encrypt tool called certbot yet but the information should still be helpful if you have.

Note

If you do not need a wildcard certificate then there are much easier (and simpler) guides out there that you should use instead.

Install Certbot

To install certbot you can run the following commands.

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx

These instructions come from https://certbot.eff.org/lets-encrypt/ubuntuxenial-nginx which assumes I'm using Nginx on Ubuntu 16.04. If you're using something else you can find the appropriate commands on that page using the dropdowns.

Install pip

Pip is a package manager for the python language. We'll need this to install the Route 53 plugin we'll be using with certbot.

sudo apt-get update && sudo apt-get -y upgrade
sudo apt-get install python-pip

Install Route 53 certbot plugin

When installing it, make sure the version number matches the version of certbot. You can find this with the following command: certbot --version. My version of certbot was 0.22.2.

pip install certbot_dns_route53==0.22.2

Configure Your AWS User

For the certbot_dns_route53 plugin to work it needs to be able to connect to AWS using an access key with the correct permissions.

To do this securely you'll want to create a new user that only has the necessary permissions it needs to work.

You can find instructions for creating a user here. The basics of it is you'll want a user with Programmatic access (not console), add it to a group (I created a new one just for this user and any future certbot users I might need).

The user will need specific permissions that are required to allow the certbot plugin to create the necessary CNAME records. These can be added by manually selecting them from a very long list or you can use the json view to give it the following permissions.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "route53:ListHostedZones",
                "route53:GetChange"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect" : "Allow",
            "Action" : [
                "route53:ChangeResourceRecordSets"
            ],
            "Resource" : [
                "arn:aws:route53:::hostedzone/YOURHOSTEDZONEID"
            ]
        }
    ]
}

Make sure you replace YOURHOSTEDZONEID with the instance ID of your hosted zone.

Once the user is created don't forget to download and save your access key and secret access key (somewhere secure, these are as sensitive as your passwords). I personally use 1Password but there are similar tools available.

Setup AWS credentials

We now need to put the AWS credentials on the server so the plugin can use them. I run all my certbot commands out of the default user's home folder, your setup might be different (or you might have a better idea of where to run certbot).

In the home folder create an .aws folder and inside that create a text file with the name credentials with the following contents.

[default]
aws_access_key_id=XXXXXX
aws_secret_access_key=XXXX/XXXXX

Replace the placeholders with the access key and secret access key that you just saved from AWS and fill them in.

Running Certbot

I created a folder called letsencrypt in my home folder that contains folders called log, config, and work to allow certbot to run from my home folder and help me avoid needing to run it with the sudo command. There is probably a better way of doing this part but couldn't figure it out so far.

To create the initial certificate I ran the following command:

certbot certonly -d example.com -d *.example.com --dns-route53 --logs-dir /home/username/letsencrypt/log/ --config-dir /home/username/letsencrypt/config/ --work-dir /home/username/letsencrypt/work/ -m email@example.com --agree-tos --non-interactive --server https://acme-v02.api.letsencrypt.org/directory

A short explanation of the above command

  • certonly: This means only create the certificate instead of updating the server config and/or restarting the server. I didn't use this when creating a normal SSL certificate, and I'm not sure if it's necessary for a wildcard cert or not, but I found it works.
  • -d: This specified what domains you wish to register with the certificate. You can pass more than one of these at a time. Before wildcard certificates you'd have to pass one of these for each subdomain you were using.
  • --dns-route53: this specifies that we want to use the plugin to verify that we control the DNS for the domain. DNS validation is the only way to validate wildcard certificates.
  • --logs-dir, --work-dir, --config-dir: points to a directory, allowing the certbot command to be run without sudo permission.
  • -m: set the email address.
  • --agree-tos: agrees to the terms and conditions.
  • --non-interactive: stops it from asking you for stuff. This is intended for using the command in an automated script for example.
  • --server: manually point to the server that allows wildcard certificates. The default server that it uses without this command does not support wildcard certificates at the time of this article being published.

Once you run the command, if it was successful, the new certificate files should be saved in the following folder if your using the same configuration as I did /home/username/letsencrypt/config/live/example.com/. There should be two files in the folder, one called fullchain.pem and another called privkey.pem.

Add cert files to your Nginx config

With the files created, we now need to add them to our server configuration.

Open the nginx config file for your website. In that file there should be a server { } block with various settings like listen and location.

Inside the server block for your site add the following lines to it.

ssl_certificate /home/username/letsencrypt/config/live/example.com/fullchain.pem;
ssl_certificate_key /home/username/letsencrypt/config/live/example.com/privkey.pem;

Don't forget to replace username with the username of your server's account that has the keys saved in it and example.com with the domain you created the certificate for.

Once you've added this to the nginx config file, save the file and close it and then restart the server. For me I use the following command:

sudo service nginx reload

After the server restarts if everything went well, you should now be able to browser your website using the https://example.com version of your website!

Renew Certificate

Now that you've created and configured the certificate, you'll now need to set the certificate to renew as it expires after 90 days.

To renew the certificate you can use the certbot utility. Below is the command I used.

certbot renew --dns-route53 --logs-dir /home/username/letsencrypt/log/ --config-dir /home/username/letsencrypt/config/ --work-dir /home/username/letsencrypt/work/ --non-interactive --server https://acme-v02.api.letsencrypt.org/directory --post-hook "sudo service nginx reload"

This is similar to the first command but instead it will try to renew all the certificates that have been created. The --post-hook lets you specify a command to run after the renew succeeds. We make this part of the command so that this command can be added to the cronjobs to automatically try and renew the certificate every day. (This article wont be covering configuring cronjobs as that's a whole article on its own, and there's already many resources out there that you can use to do this).

Conclusion

Following the above you should now have a wildcard certificate configured for your domain and being served on your nginx webserver.

© 2018-2020 Christopher Muller