ACME Certificate for Internal Server using Route 53

This procedure results in a TLS certificate issued by an ACME CA for an internal server using an AWS Route 53 Hosted Zone that is designated for ACME DNS verification.

Consider a company that has a domain registration of company.com with a legacy DNS provider that does not have an API for dynamic record creation. A private AWS Route 53 Hosted Zone exists for internal.company.com that is inaccessible from the public internet; there is no zone delegation from company.com to this zone. A TLS certificate is desired for app.internal.company.com using DNS verification. A public AWS Route 53 Hosted Zone exists for aws.company.com and an AWS IAM user has permission to add TXT records to that zone.

A static CNAME is created manually in the legacy DNS system for _acme-challenge.app.internal.company.com pointing to _acme-challenge.app.aws.company.com. When acme.sh is executed using the DNS verification method, it will create a record in the AWS Route 53 Hosted Zone for aws.company.com that can be queried by the ACME CA.

First let’s get setup for running acme.sh:

  • Create an acme_home directory at ~/.acme.sh.docker or another location of your choice. The reason a directory named ~/.acme.sh.docker is suggested instead of the default location of ~/.acme.sh is that it the Docker container and a local installation of acme.sh can’t share the same configuration files.

  • Create an AWS IAM user that has permission to update AWS Route 53 only. An example Terraform module shows the resources necessary. The example below shows retrieving the AWS API Access Key from using 1Password CLI when performing an interactive experiment. It’s worth noting that the current version of acme.sh stores the AWS credentials in $acme_home/account.conf for later use. This is suboptimal and I hope an option to disable this is added.

Modify the values in the following script and execute to register and an ACME CA:

Then evaluate and modify the following to create a new TLS certificate:

If the procedure completes successfully, the key, certificate, and chain content will be located in $acme_home/$hostname.