Manage Let's Encrypt certificates with Digital Ocean Spaces

Introduction

Managing and distributing Let's Encrypt SSL certificates can be tedious. In this article, I will share my strategy for obtaining, renewing and distributing my SSL certificates with let's encrypt. I wanted to have to type only one command to:

  • get a new certificate for a subdomain
  • renew all my certificates that needed renewal
  • publish the certificates to Digital Ocean spaces or S3
  • pull the certificates on my webservers

To issue and renew let's encrypt certificate we will use DNS verification using lego (with Digital Ocean's DNS). We will distribute the certificates using Digital Ocean spaces, an equivalent of S3.

All four commands are in a makefile that you can find at: https://github.com/charignon/certificates.

Prerequisites

Step 1 — Set up the aws cli and creating a bucket

In this step, we will be creating a bucket domain-certificates to hold our certificates.

Let's start by setting up the aws cli credentials and bucket:

export AWS_ACCESS_KEY_ID="YYYY" # change with your key id
export AWS_SECRET_ACCESS_KEY="XXXX" # change with your key
export BUCKET=domain-certificates

If like me you are using digital ocean spaces, you need to specify an endpoint when using the aws cli. To avoid repeatedly typing the --endpoint argument, I created an alias. If instead, you use S3, just use aws instead of awscli in all of the future commands:

alias awscli="aws --endpoint-url=https://nyc3.digitaloceanspaces.com"

Now let's use this alias to create a new bucket and check that it exists:

awscli s3api create-bucket --bucket $BUCKET --region nyc3
awscli s3api list-buckets

We can also check that lego can be invoked as well:

lego --version # prints "lego version 0.4.0" for me

If this does not work for you, you might want to adjust your $GOPATH environment variable so that your shell can find lego.

Step 2 — Generating and uploading a certificate for a subdomain

Let's assume that you own the domain foobar.com and want to generate a new certificate for prod.foobar.com. To generate a new certificate for your subdomain, we can use lego. The instructions vary depending on your DNS provider. Here is what I am doing for Digital Ocean's DNS:

export DO_AUTH_TOKEN=XXX
lego --accept-tos  --email [email protected] --dns digitalocean -d prod.foobar.com run

If all goes well, this will create a new certificate in the ".lego" folder. We can now upload this folder to the bucket that we created (in a acme folder):

aws s3 sync $(pwd)/.lego  s3://domain-certificates/acme

Voila! You just issued a new certificate and stored it online.

Step 3 — Downloading the certificate on another host

On your webserver you can download the certificate we just issued by using the following command:

aws s3 sync s3://domain-certificates/acme $(pwd)/.lego

Step 4 - Renewing domains

To renew a previously issued certificate stored in the ".lego" folder, you can use:

lego -d prod.foobar.com renew --days 30

Step 5 - Automating with a Makefile

Let's automate all these steps in a Makefile. Let's first create a rule to add a new domain by calling lego :

LEGO=lego --accept-tos  --email [email protected] --dns digitalocean 

new:
	@$(if ${DOMAIN}, echo "Setting up ${DOMAIN}", echo "Please set DOMAIN env var" ; exit 1 )
	${LEGO} -d ${DOMAIN} run

We can invoke it like this:

DOMAIN=staging.foobar.com make new

Next we need a way to upload and download certificates to and from our bucket. Let's create two rules for that:

ENDPOINT=--endpoint-url https://nyc3.digitaloceanspaces.com
LOCAL_PATH=${PWD}/.lego
S3_PATH="s3://domain-certificates/acme"
AWS=aws ${ENDPOINT}

upload:
	@echo "uploading"
	${AWS} s3 sync ${LOCAL_PATH} ${S3_PATH}

download:
	@echo "Download"
	${AWS} s3 sync ${S3_PATH} ${LOCAL_PATH}

Finally, to renew all of the domains, we need to be able to query a list of domain in ".lego" :

DOMAINS=`ls .lego/certificates | grep key | sed "s/.key$$//"`

And loop through all of the subdomains, which leads to the following makefile rule:

DOMAINS=`ls .lego/certificates | grep key | sed "s/.key$$//"`

renew:
	@echo "Renewing ${DOMAINS}"
	@for d in ${DOMAINS};do ${LEGO} -d $$d renew --days 30 ; done

I can use the rules we just defined to download and renew all of my certificates:

make download && make renew && make upload

The final touch is to make the rules phony because they don't produce files:

.PHONY: new renew upload download

Putting it all together, the makefile looks like:

.PHONY: new renew upload download
ENDPOINT=--endpoint-url https://nyc3.digitaloceanspaces.com
AWS=aws ${ENDPOINT}
LEGO=lego --accept-tos --email [email protected] --dns digitalocean
S3_PATH="s3://domain-certificates/acme"
LOCAL_PATH=${PWD}/.lego
DOMAINS=`ls .lego/certificates | grep key | sed "s/.key$$//"`

new:
	@$(if ${DOMAIN}, echo "Setting up ${DOMAIN}", echo "Please set DOMAIN env var" ; exit 1 )
	${LEGO} -d ${DOMAIN} run

renew:
	@echo "Renewing ${DOMAINS}"
	@for d in ${DOMAINS};do ${LEGO} -d $$d renew --days 30 ; done

upload:
	@echo "uploading"
	${AWS} s3 sync ${LOCAL_PATH} ${S3_PATH}

download:
	@echo "Download"
	${AWS} s3 sync ${S3_PATH} ${LOCAL_PATH}

This makefile makes it a lot easier to manage your certificates! You can now quickly create, renew, upload and download certificates. I hope that it makes your life easier!

The code is available on Github at https://github.com/charignon/certificates.