Ari Bader-Natal

Let's Encrypt, Cloudfront Edition

Filed under:

UPDATE: AWS recently introduced Amazon Certificate Manager, a service that offers a simple way to get a free SSL/TLS certificate that you can use with the Cloudfront distribution in front of static S3-hosted websites. This allows you to accomplish the same result as the setup below (with the added benefits of auto-renewing and wildcard certificates) with just a few clicks in the AWS Web Console. Highly recommended.


You can now use Let's Encrypt certificates with all of your S3-hosted static websites.

Last year, I rebuilt my personal website at https://aribadernatal.com as a static website, which made it easier to maintain, cheaper to run, and faster to load. Written in Markdown, generated by Jekyll, hosted in an S3 bucket, and accessed from Cloudfront over https, using a free SSL certificate that I set up through StartSSL. (Hat tip to the Homebrew Website Meetup for the encouragement/challenge.) There are already a number of nice end-to-end guides online detailing how to set this up, so I won't retread this ground. Instead, I'm going just cover the last leg: how to generate and use the brand new Let's Encrypt certificates with a Cloudfront+S3 static website.

The Easy Way

Install and run the Let's Encrypt CLI plugin for S3/CloudFront validation and installation. Done.

The Harder Way

Unfortunately, that didn't work for me, so I took a more hands-on route. I generated a certificate for the set of relevant domains, uploaded the challenge file to S3 to prove that I controlled the domain, then uploaded the certificate to the IAM certificate store, and finally updated the Cloudfront distribution to use this new certificate.

The notes that I jotted down along the way were mostly intended as a reference for my future self, as I know that I'll need to repeat this process in a few months, when/before these certificates expire. As a result, the notes below likely skip over initial setup steps (e.g. installing the AWS CLI tools.) Apologies.

Terminal snippets

For S3+Cloudfront static websites to work properly, your S3 bucket name must be the same as the domain name.

BUCKET=aribadernatal.com

I set up my static websites to be accessible from both the root domain and from the www subdomain. I want my certificate to cover both.

SUBDOMAIN="www.aribadernatal.com"

Finally, I run the letsencrypt script in certonly manual mode. Note that Cloudfront has a 2048-bit max for SSL certs.

./letsencrypt-auto certonly -a manual -d $BUCKET -d $SUBDOMAIN --rsa-key-size 2048 --server https://acme-v01.api.letsencrypt.org/directory --agree-tos

At this point, you'll be prompted to prove that you control each (sub)domain that you specified by placing a particular file in a particular web-accessible location on your server. In a new terminal window, I created the file and copied it to my S3 bucket.

FILENAME="lotsofgibberish-skjldf2ofw09dfwfasdfasldkfajsdf"
 CONTENT="lotsofgibberish-skjldf2ofw09dfwfasdfasldkfajsdf.evenmoregibberish-askldfjkalsfd"

echo $CONTENT > $FILENAME

BUCKET=aribadernatal.com

aws s3 cp $FILENAME s3://$BUCKET/.well-known/acme-challenge/

At this point, you can continue with the interactive letsencrypt-auto process. If you specified any additional subdomains, you'll be prompted to generate and upload one such file for each (sub)domain before a certificate is issued.

Once you're done adding any additional subdomains, you're in business! The next step is to get this certificate into AWS. Here I chose to incorporate the datestamp in the name of the certificate, to make it tell them apart when rotating certificates in three months.

TODAY=$(date +"%Y-%m-%d_%H.%M.%S")

sudo aws iam upload-server-certificate \
  --server-certificate-name $TODAY-$BUCKET \
  --certificate-body file:///etc/letsencrypt/live/$BUCKET/cert.pem \
  --private-key file:///etc/letsencrypt/live/$BUCKET/privkey.pem \
  --certificate-chain file:///etc/letsencrypt/live/$BUCKET/chain.pem \
  --path /cloudfront/$TODAY-$BUCKET/ 

(Note that the trailing slash in the --path above is necessary.)

If you open up the AWS console to Cloudfront and select the distribution associated with your website you can use the "Edit" button on the "General" tab to modify the distribution settings. In the "SSL Certificate" section, the menu below "Custom SSL Certificate (stored in AWS IAM)" should now include your new certificate. Select it, save the change to the distribution settings, and wait an hour for the changes to propagate.

End result: