Let’s Encrypt certificates for local development

LEGO client with Docker and AWS Route53 DNS
thumbnail

While building a couple of browser based prototypes, I’ve hit an interesting problem. Basically, I am trying to replicate a full remote setup with a reverse proxy and TLS SNI while running everything on localhost. Getting the DNS functioning is pretty easy—I just add the required hosts to the /etc/hosts file and I’m done with it.

However, I still need actual certificates trusted by the browser. In 2021, Let’s Encrypt is the way to go. Turns out, this is also pretty easy to do. There’s a Let’s Encrypt client and ACME library written in Go[1] which can be used via Docker on any operating system.

Here’s how to use it with AWS Route53:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
mkdir -p /tmp/certs && cd /tmp/certs
docker run --rm \
    -v $(pwd):/lego \
    -v ${HOME}/.aws/credentials:/root/.aws/credentials \
    -e AWS_PROFILE=lego \
    -ti goacme/lego \
    --accept-tos \
    --domains=subdomain1.example.com \
    --domains=subdomain2.example.com \
    --server=https://acme-staging-v02.api.letsencrypt.org/directory \
    --email=info@example.com \
    --path=/lego \
    --dns=route53 run

This command starts a Docker container using thr latest LEGO Docker image and requests a certificate for two subdomains. It mounts two volumes:

  • current working directory as /lego in the container, this is where the retrieved data will land
  • ${HOME}/.aws/credentials as /root/.aws/credentials

The command assumes that AWS profiles are used and there is a profile named lego. The profile must have the following IAM permissions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
   "Version": "2012-10-17",
   "Statement": [
       {
           "Sid": "",
           "Effect": "Allow",
           "Action": [
               "route53:GetChange",
               "route53:ChangeResourceRecordSets",
               "route53:ListResourceRecordSets"
           ],
           "Resource": [
               "arn:aws:route53:::hostedzone/*",
               "arn:aws:route53:::change/*"
           ]
       },
       {
           "Sid": "",
           "Effect": "Allow",
           "Action": "route53:ListHostedZonesByName",
           "Resource": "*"
       }
   ]
}

As I have not specified the hosted zone ID in the command, LEGO client will figure it out from the --domains flags. The output directory is configured with the --path flag pointing to the first volume target.

To fetch the real production certificate, use https://acme-v02.api.letsencrypt.org/directory as the --server value.

Pretty neat. I now have LE real certificates without messing with remote HTTP servers.

Full documentation is available here[2].

§use as a library

The LEGO client can be used as a library in any go program. Here’s a repository[3] wrapping the library in a more high level client. Very decent sample can be found in the examples folder.