Try Catch Finally

How to use a Let's Encrypt SSL certificate to secure a custom domain with AWS CloudFront

Prerequisites #

This guide assumes that you already have a website, or website assets, being served by an AWS S3 bucket behind a CloudFront CDN distribution using a custom domain. It won't go into any detail regarding these services' configuration, which is a great solution for serving modern JAM stack websites that have no underlying web server requirement.

The only problem with using S3 + CloudFront to serve static websites is that Amazon wants you to pay for their own SSL certificate solution and there's no easy and obvious way to configure a third-party solution such as Let's Encrypt. Thankfully once you know how, it's not that difficult to achieve thanks to an excellent third-party Certbot plugin.

Further reading:

Automating the renewal of a Let's Encrypt SSL certificate to secure a custom domain pointing at a CloudFront CDN #

NB: These steps were tested as working within a Ubuntu 18.04 server environment.

Step 1 #

First we need to create a new IAM Policy within our AWS account to handle the communication between our local machine/server and the AWS account hosting the S3 bucket and CloudFront distribution.

Head over to the Identity and Access Management dashboard within the AWS console and proceed to create a new Policy with the following permissions: Sample IAM Policy

You'll need to replace "YOUR-BUCKET" with the name of your own S3 Bucket within the Sample Policy JSON as appropriate. Give the Policy a memorable name which is relevant to our use case such as, "CloudFrontSSLManagement".

Having created the new Policy, create a new IAM User with "programmatic access" only and attach the Policy you have just created. This IAM User will be solely responsible for managing your CloudFront SSL certificates so use a relevant username such as, "cert-admin". Upon reaching the success page, be sure to make a note of the Access Key ID and Secret Access Key credentials as this will be your last opportunity to view them.

Step 2 #

Next, within the server environment which will be responsible for the ongoing renewal of the SSL certificate, we need to have Python3 and Python3 Virtual Environments available. On my Ubuntu box, I needed to install the latter using, sudo apt-get install python3-venv.

mkdir ~/cloudfront-ssl && cd ~/cloudfront-ssl

python3 -m venv cf-ssl-venv

source cf-ssl-venv/bin/activate

pip install certbot certbot-s3front

deactivate

Further reading:

Step 3 #

Now that we have the environment configured as we need it, we'll write a bash script to do all the legwork.

Amend the following as necessary for your situation within a new renew-cert.sh file (create this file within the "~/cloudfront-ssl" directory we created earlier):

#!/bin/sh

source cf-ssl-venv/bin/activate

export AWS_ACCESS_KEY_ID="<YOUR_ACCESS_KEY_ID>"
export AWS_SECRET_ACCESS_KEY="<YOUR_SECRET_ACCESS_KEY>"

certbot --agree-tos -a certbot-s3front:auth \
--certbot-s3front:auth-s3-bucket <YOUR_S3_BUCKET_NAME> \
--certbot-s3front:auth-s3-region <YOUR_S3_BUCKET_REGION> \
-i certbot-s3front:installer \
--certbot-s3front:installer-cf-distribution-id <YOUR_CLOUDFRONT_DISTRIBUTION_ID> \
-d <YOUR_DOMAIN_NAME> --work-dir work --logs-dir log --config-dir conf

Give your current User execution permissions over the file, and then run it for the first time:

chmod +X renew-cert.sh

bash renew-cert.sh

If you have configured everything correctly, the certbot script should run and prompt you to enter an email address, before generating the SSL certificate and installing it against your CloudFront distribution.

Step 4 #

Let’s Encrypt SSL certificates are valid for only 90 days, so in order to automatically renew your certificate so that it never expires we need to do some further work.

First append the following additional parameters to the certbot script in your renew-cert.sh file:

--renew-by-default --text

Once you have done that, running bash renew-cert.sh again should result in the script completing without any additional user input being required. Once this step has been achieved we can configure a cron job to run this script once a week to keep our SSL certificate continually up to date.

Enter the current user's crontab:

crontab -e

Then append the following line (replacing "your-user" as appropriate) to the foot of the crontab:

0 3 * * 1 bash /home/your-user/cloudfront-ssl/renew-cert.sh

This will run the "renew-cert" script at 3am every Monday.