Use the ACME DNS-Challenge to get a TLS certificate

Marco Franssen

Marco Franssen /

9 min read1730 words

Cover Image for Use the ACME DNS-Challenge to get a TLS certificate

In my previous 2 blogs I have shown you how to build a HTTP/2 webserver. In these blogs we have covered self signed TLS certificates as well retrieving a Certificate via Letsencrypt. I mentioned there you will have to expose your server publicly on the internet. However I now figured out there is another way. So please continue reading.

Let's Encrypt is a free, automated, and open certificate authority brought to you by the nonprofit Internet Security Research Group (ISRG).

Letsencrypt implements the ACME (Automated Certificate Management environment) protocol. In the ACME protocol there are 4 challenge types defined. Let's go briefly over these challenge types, so we can relate this back to my previous blogs before we are going to use the DNS challenge type.

HTTP-01 challenge

This challenge can only be performed on port 80. The client will temporarely place a file on the webserver in the following path.


Once the ACME client tells Letsecrypt the file is there it will be retrieved to authenticate and validate. Upon success you will receive the certificate. For this challenge your webserver has to be publicly reacheable on port 80.

DNS-01 challenge

This challenge requires you to prove the ownership of a domain name. It also allows for issuing wildcard certificates. After receiving a token the client will have to create a DNS TXT record with the following contents.


Letsencrypt will query for this DNS record. Once verified, the client can issue the certificate. As automation is important there is a large amount of DNS providers that expose an API. For this challenge your webserver does not have to be exposes on the internet, and is therefore very convenient to issue development certificates for a domain you personally own.

TLS-SNI-01 challenge

This challenge was defined in draft versions of ACME. It did a TLS handshake on port 443 and sent a specific SNI header, looking for certificate that contained the token. It was disabled in March 2019 because it was not secure enough. So let's quickly forget about this one.

TLS-ALPN-01 challenge

This challenge can be performed on port 443 over TLS only. This challenge is not suitable for most people. It is best suited to authors of TLS-terminating reverse proxies that want to perform host-based validation like HTTP-01, but want to do it entirely at the TLS layer in order to separate concerns.


So now we know a bit about the different ACME Challenge types lets see what we have used in the previous blog. In this blog we had to publicly expose our webserver to be able to request a certificate. The main reason for this is that the does not offer the DNS-01 challenge implementation. We have been using the HTTP-01 challenge on this server to get our certificate.

So now lets focus on how we can issue a certificate via the DNS-01 challenge type. As a fan of the Go programming language I found a bunch of other libraries and tools implementing the ACME protocol. These do implement the DNS-01 challenge type.


Lego can be used both as a cli tool or as a library in your own code. Lego supports a whole bunch of DNS providers.


Certmagic is the library used in the Caddy webserver. To make use of the DNS-01 challenge also certmagic supports the Lego DNS providers.


In this blog I will show you a small example using the Lego cli to issue a certificate for your domain. I'm utilizing for that the Gandi Live DNS (v5) as a DNS Provider.

First I will install Lego from sources using Go. Please note you will require Go 1.12+.

$ GO111MODULE=on go get -u
go: found in v3.5.0
$ lego -h
   lego - Let's Encrypt client written in Go
   lego [global options] command [command options] [arguments...]
   run      Register an account, then create and install a certificate
   revoke   Revoke a certificate
   renew    Renew a certificate
   dnshelp  Shows additional help for the '--dns' global option
   list     Display certificates and accounts information.
   help, h  Shows a list of commands or help for one command

If you don't have Go installed on your machine, there is also a Docker container available.

$ docker run goacme/lego -h
   lego - Let's Encrypt client written in Go
   lego [global options] command [command options] [arguments...]
   run      Register an account, then create and install a certificate
   revoke   Revoke a certificate
   renew    Renew a certificate
   dnshelp  Shows additional help for the '--dns' global option
   list     Display certificates and accounts information.
   help, h  Shows a list of commands or help for one command

For my Gandi Live DNS, I will have to provide my Gandi API Key to be able to communicate with their API. In below example I show you a dummy key, so don't expect this to work for you.

$ export GANDIV5_API_KEY=G4nD1v5L1v3DNSDummyK3y
$ lego --dns gandiv5 -d -d '*' -a -m [email protected] run
2020/04/11 13:31:42 No key found for account [email protected]. Generating a P384 key.
2020/04/11 13:31:42 Saved key to /Users/marco/.lego/accounts/[email protected]/keys/[email protected]
2020/04/11 13:31:43 [INFO] acme: Registering account for [email protected]
!!!! HEADS UP !!!!
    Your account credentials have been saved in your Let's Encrypt
    configuration directory at "/Users/marco/.lego/accounts".
    You should make a secure backup of this folder now. This
    configuration directory will also contain certificates and
    private keys obtained from Let's Encrypt so making regular
    backups of this folder is ideal.
2020/04/11 13:36:11 [INFO] [] acme: Obtaining bundled SAN certificate
2020/04/11 13:36:12 [INFO] [] AuthURL:
2020/04/11 13:36:12 [INFO] [] acme: Could not find solver for: tls-alpn-01
2020/04/11 13:36:12 [INFO] [] acme: Could not find solver for: http-01
2020/04/11 13:36:12 [INFO] [] acme: use dns-01 solver
2020/04/11 13:36:12 [INFO] [] acme: Preparing to solve DNS-01
2020/04/11 13:36:13 [INFO] API response: DNS Record Created
2020/04/11 13:36:13 [INFO] [] acme: Trying to solve DNS-01
2020/04/11 13:36:13 [INFO] [] acme: Checking DNS record propagation using []
2020/04/11 13:36:13 [INFO] Wait for propagation [timeout: 20m0s, interval: 20s]
2020/04/11 13:36:14 [INFO] [] acme: Waiting for DNS record propagation.
2020/04/11 13:36:34 [INFO] [] acme: Waiting for DNS record propagation.
2020/04/11 13:36:54 [INFO] [] acme: Waiting for DNS record propagation.
2020/04/11 13:37:14 [INFO] [] acme: Waiting for DNS record propagation.
2020/04/11 13:37:41 [INFO] [] The server validated our request
2020/04/11 13:37:41 [INFO] [] acme: Cleaning DNS-01 challenge
2020/04/11 13:37:41 [INFO] [] acme: Validations succeeded; requesting certificates
2020/04/11 13:37:42 [INFO] [] Server responded with a certificate.

Now lets inspect the certificate we received. Please note there is also a .key file in the same location which you will have to deploy at your webserver. For security reasons I will not share my key over here.

$ cat .lego/certificates/

To inspect the contents of your certificates you can simply run the following openssl command.

$ openssl x509 -in .lego/certificates/ -noout -text
        Version: 3 (0x2)
        Serial Number:
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
            Not Before: Apr 11 10:37:42 2020 GMT
            Not After : Jul 10 10:37:42 2020 GMT
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (384 bit)
                ASN1 OID: secp384r1
                NIST CURVE: P-384
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
            X509v3 Subject Key Identifier:
            X509v3 Authority Key Identifier:
            Authority Information Access:
                OCSP - URI:
                CA Issuers - URI:
            X509v3 Subject Alternative Name:
            X509v3 Certificate Policies:

Last but not least you can very easily renew your certificate using Lego.

$ lego --dns gandiv5 -d -a -m [email protected] renew
2020/04/11 13:49:23 [] The certificate expires in 89 days, the number of days defined to perform the renewal is 30: no renewal.

Feel free to explore the other options of the cli yourself. There are plenty of DNS providers available.

Last but not least you will have to update your /etc/hosts file to use the certificate on your laptop during development.


This allows me to navigate to my webserver running on localhost using https://mac-dev.marcofranssen.n. See my other 2 blogs on how to build a webserver in Go running on HTTPS or a webserver using Nginx. Simply use the crt and key file we just received via Letsencrypt.

Last but not least you will also find a reference to Traefik. Also Traefik makes use of Lego to be able to get your certificates in Traefik.


Thanks for reading my blog, don't forget to share with your friends and colleagues. Together we can make the web a safer place, and this starts with your development, so you can use modern web features such as HTTP/2 and gRPC, which require you to use TLS.

You have disabled cookies. To leave me a comment please allow cookies at functionality level.

More Stories

Cover Image for Nginx 1.19 supports environment variables and templates in Docker

Nginx 1.19 supports environment variables and templates in Docker

Marco Franssen

Marco Franssen /

In this blog I want to show you a nice new feature in Nginx 1.19 Docker image. I requested it somewhere 2 years ago when I was trying to figure out how I could configure my static page applications more flexibly with various endpoints to backing microservices. Back then I used to have my static pages fetch a json file that contained the endpoints for the apis. This way I could simply mount this json file into my container with all kind of endpoints for this particular deployment. It was some sor…

Cover Image for Building a Elasticsearch cluster using Docker-Compose and Traefik

Building a Elasticsearch cluster using Docker-Compose and Traefik

Marco Franssen

Marco Franssen /

In a previous blog I have written on setting up Elasticsearch in docker-compose.yml already. I have also shown you before how to setup Traefik 1.7 in docker-compose.yml. Today I want to show you how we can use Traefik to expose a loadbalanced endpoint on top of a Elasticsearch cluster. Simplify networking complexity while designing, deploying, and running applications. We will setup our cluster using docker-compose so we can easily run and cleanup this cluster from our laptop. Create a Elasti…

Cover Image for Build a Go Webserver on HTTP/2 using Letsencrypt

Build a Go Webserver on HTTP/2 using Letsencrypt

Marco Franssen

Marco Franssen /

Pretty often I see developers struggle with setting up a webserver running on https. Now some might argue, why to run a webserver on https during development? The reason for that is simple. If you would like to benefit from HTTP/2 features like server push, utilizing the http.Pusher interface, you will need to run your webserver on HTTP/2. That is the only way how you can very early on in the development process test this. In this blog I'm showing you how to do that in Go using Letsencrypt and a…

Cover Image for React Router and Nginx over HTTP/2

React Router and Nginx over HTTP/2

Marco Franssen

Marco Franssen /

In this blogpost I want to show you how you can easily get your React SPA app with clientside router work properly with your Nginx setup. I will also show you how to serve your React App over HTTP/2 and how you can leverage from http2 server pushes. To do so I will show you how to do that with the Nginx Docker image. When running your webapp using the development server you will in general not face any issues, however when running the static build on a production server you will most likely fac…