We have a bunch of machines behind haproxy load balancers. The haproxy is publicly accessible and can use the ACME HTTP method for certificate renewal. The machines behind it are using self-signed certs from our own CA, but there are cases where we might like to have certs on both systems that are “legit”. For example, a gitlab instance that is accessible internally and externally, or our web servers for internal or testing access.

For the internal machines, I came up with the idea of using DNS to verify the registration, while the haproxy continues to do HTTP.

I wanted to restrict the AWS access keys so the compromise of one machine didn’t expose our entire DNS, so I created a recipe that allows for the access key to be limited to a specific name.

Overview

AWS does not allow limiting access based on the DNS record name. There has been a request open since 2014 to implement it, but it doesn’t seem to have any traction. So we are going to create the record for the certificate as it’s own “hosted zone”, and then limit the access key to that.

In this example I’m going to set up a certificate for “sean.example.com”.

Create Hosted Domain

In the AWS console:

Make a note of the Zone ID

Creating the Policy

You can use the visual policy editor, but it’s easier to just paste in this JSON, and substitute your zone ID towards the bottom.

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "route53:ListHostedZones",
                "Resource": "*"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "route53:GetHostedZone",
                    "route53:ChangeResourceRecordSets",
                    "route53:ListResourceRecordSets"
                ],
                "Resource": "arn:aws:route53:::hostedzone/MY-ZONE-ID"
            }
        ]
    }

Create IAM User

Attach the Policy to the User

Creating the Certificate

I used “acme.sh” to create my certificate. It includes support for Route53, where Certbot when I last used it (hopefully fixed now) included an old AWS library that would not work.

So I just cloned the acme.sh repository, and then ran:

export AWS_ACCESS_KEY_ID=<saved from above>
export AWS_SECRET_ACCESS_KEY=<saved from above>
acme.sh --issue --dns dns_me -d sean.example.com -d sean.example.com

After a minute or less, it should tell you where it wrote your certificate.