Help me understand acme-dns

Hi again @danb35,

Just for the records, I've tested trying to issue a certificate covering 3 domains and it doesn't work:

$ --issue --dns dns_acmedns -d acmedns1.domain.tld -d acmedns2.domain.tld -d acmedns3.domain.tld --test
[Sun Apr  8 20:07:54 CEST 2018] Using stage ACME_DIRECTORY:
[Sun Apr  8 20:07:55 CEST 2018] Creating domain key
[Sun Apr  8 20:07:55 CEST 2018] The domain key is here: /root/
[Sun Apr  8 20:07:55 CEST 2018] Multi domain='DNS:acmedns1.domain.tld,DNS:acmedns2.domain.tld,DNS:acmedns3.domain.tld'
[Sun Apr  8 20:07:55 CEST 2018] Getting domain auth token for each domain
[Sun Apr  8 20:07:55 CEST 2018] Getting webroot for domain='acmedns1.domain.tld'
[Sun Apr  8 20:07:55 CEST 2018] Getting new-authz for domain='acmedns1.domain.tld'
[Sun Apr  8 20:07:56 CEST 2018] The new-authz request is ok.
[Sun Apr  8 20:07:56 CEST 2018] Getting webroot for domain='acmedns2.domain.tld'
[Sun Apr  8 20:07:56 CEST 2018] Getting new-authz for domain='acmedns2.domain.tld'
[Sun Apr  8 20:07:57 CEST 2018] The new-authz request is ok.
[Sun Apr  8 20:07:57 CEST 2018] Getting webroot for domain='acmedns3.domain.tld'
[Sun Apr  8 20:07:57 CEST 2018] Getting new-authz for domain='acmedns3.domain.tld'
[Sun Apr  8 20:07:58 CEST 2018] The new-authz request is ok.
[Sun Apr  8 20:07:58 CEST 2018] Found domain api file: /root/
[Sun Apr  8 20:07:58 CEST 2018] Using acme-dns
[Sun Apr  8 20:07:58 CEST 2018] Found domain api file: /root/
[Sun Apr  8 20:07:58 CEST 2018] Using acme-dns
[Sun Apr  8 20:07:59 CEST 2018] Found domain api file: /root/
[Sun Apr  8 20:07:59 CEST 2018] Using acme-dns
[Sun Apr  8 20:07:59 CEST 2018] Sleep 120 seconds for the txt records to take effect
[Sun Apr  8 20:10:00 CEST 2018] Verifying:acmedns1.domain.tld
[Sun Apr  8 20:10:03 CEST 2018] Success
[Sun Apr  8 20:10:03 CEST 2018] Verifying:acmedns2.domain.tld
[Sun Apr  8 20:10:06 CEST 2018] acmedns2.domain.tld:Verify error:Incorrect TXT record
[Sun Apr  8 20:10:06 CEST 2018] Removing DNS records.
[Sun Apr  8 20:10:06 CEST 2018] Using acme-dns
[Sun Apr  8 20:10:06 CEST 2018] Using acme-dns
[Sun Apr  8 20:10:06 CEST 2018] Using acme-dns
[Sun Apr  8 20:10:06 CEST 2018] Please add '--debug' or '--log' to check more details.
[Sun Apr  8 20:10:06 CEST 2018] See:

It works if you use only 2 domains or if you try to issue the cert again because 2 of 3 domains were already validated... but it could be a pain to renew the cert ;).

1 Like

Does certbot handle this differently than Because given the way handles it (with the credentials specified by environment variables), it’d be all but impossible to issue and maintain a multi-SAN cert using acme-dns–there’s no way for to use different credentials for different domains. Which means it’d be fine for wildcards, but pretty much useless for anything else.

@danb35, certbot works as but the problem here is the hook script, I’m working on a hook script (it is a shell script) for certbot, once finished I could try to adapt it to so won’t be a limit of domains per cert.

I wrote an Certbot hook script, and a library too. They handle the things with the best UX I could come up with. That is: the hook will register an unique acme-dns account for every domain you request in the background, and will prompt you to add the CNAME records to your main DNS zone. All the acme-dns credentials are then stored on the box, and reused on renewal without any prompts whatsoever.

Take a look, the hook can be found at

The in the repo above has finegrained instructions how to get up and running with it.

Is this limitation something that’s in acme-dns, something in Boulder, or something that’s inherent in the way DNS works? If the former, it seems that the easiest answer would be to alter acme-dns to remove it. If Boulder, changing that wouldn’t be as easy, but could still be do-able. If it’s something that’s baked into DNS, well, that’s obviously harder to work around.

Aside from the issue of the hook script, it just seems that logistically it’d be a pain to keep track of which hostname belongs to which randomly-generated subdomain.

Ah, I’d missed that–I looked at the hook page, and realized that it auto-registered, but not that it created a separate registration for each domain. I assume, then, that it also keeps track of which domain belongs to which registration? That would definitely avoid a lot of the logistical pain.

It's a design decision made in acme-dns. The purpose is to promote automation and to make everything as secure and flexible as possible per default. The security standpoint here is that if one set of credentials gets compromised, it wouldn't affect certificates of all of the domains handled on that box. The flexibility standpoint in this case would be that if one of, say ten domains handled on box A is also handled on box B, the credentials for this one box can be shared, instead of sharing the credentials used to validate all the domains on box A.

I hope that didn't come out too complicated.

This is the trick with automation. The hook linked above takes care of all this.

1 Like

If you are ever going to need this information, it should be easily available from your main zone, and if you lose your credentials, it's very easy to re-create new ones and just change the CNAME in the main zone.

From that perspective, yes, it makes sense. So the real idea is that one set of credentials = one FQDN, but you allow two TXT records for the sake of wildcards.

That means that a multi-domain SAN cert may need a bunch of registrations for randomly-generated validation FQDNs, but you let the software automate that correlation, rather than making the user handle it on a recurring basis.

I think I’m getting it. It sounds like I’ll need to look back at certbot–I haven’t been a fan of it in the past, preferring the shell-based clients like dehydrated and

Yes to everything in your last response. (I didn’t want to quote the whole comment).

This functionality is naturally achievable on any ACME client, but I personally lack the time to write support for them. With Certbot I’m familiar with, so writing a hook was a no-brainer, and while I might be biased, I’m a fan :wink:

I released the important bits of the hook handling as a Python library ( ) for developers who wish to write support for other clients however.

OK, I won’t say I thoroughly understand it, but I think I understand enough to make it work for my purposes. Thanks for all the help (not to mention the software).

1 Like

Awesome! I think I should compile a FAQ from questions like the ones in this thread at some point in the future to make the installation and usage clearer at some point.

I have one other question, though it’s more of a side note than anything else: since acme-dns already implements a DNS server, why does it use HTTP validation to obtain its own cert? Seems it would be simpler all around if it used DNS validation.

Edit: for clarity above.

This functionality makes use of awesome Go package autocert, and leveraging this widely used and battletested package results into less potential of bugs and less code to maintain.

So supposing I set up acme-dns, and I expose its API to the Internet, it looks like anyone would be able to register and obtain credentials (it looks like corsorigins in config.cfg would be the way to restrict that by IP?). And, for that matter, to use my acme-dns instance to do domain validation. I’m not really intending to open up a public acme-dns server, but is there any real problem with this?

Hi @danb35,

Yep... but you have a directive to disable registrations:

# disable registration endpoint
disable_registration = false

CORS is not the way to restrict it by IP because using CORS headers to allow or not the resource to one domain, generally relies on the client (for example curl or a browser) to support the restrictions imposed by the server. In this case I would use a few firewall rules to restrict access.


1 Like

Bandwith and CPU resources aside, there's no real problem with it.
If you wish to add authentication in front of acme-dns, you could for example use a reverse Nginx proxy and set up basic authentication in it. That will require slight modifications to the hook script however.

Firewall rules are probably the best way to restrict the API access, if you control a somewhat static set of potential clients.

1 Like

That is a good idea, something like this should work (it is just an example…):

Create the htpasswd file:

htpasswd -c /etc/nginx/acme-dns.htpasswd username

Create the nginx conf file:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name    auth.domain.tld;
    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;
    location /register {
            proxy_pass                              http://localhost:8080/register;
            proxy_buffering                         off;
            proxy_set_header Host                   $http_host;
            proxy_set_header X-Real-IP              $remote_addr;
            proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto      $scheme;
            auth_basic                              "acme-dns restricted";
            auth_basic_user_file                    /etc/nginx/acme-dns.htpasswd;
    location /update {
            proxy_pass                              http://localhost:8080/update;
            proxy_buffering                         off;
            proxy_set_header Host                   $http_host;
            proxy_set_header X-Real-IP              $remote_addr;
            proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto      $scheme;

and using for example curl to register without username and password:

$ curl -X POST https://auth.domain.tld/register
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>

and using the right user and password:

$ curl -u username:password -X POST https://auth.domain.tld/register
1 Like


I have been following this thread since I got a reply in another thread from danb35.

I’ve been able to manually acquire my wildcard ssl cert thanks to everyone’s help so far but I need to get this working using acme-dns as there are many wildcard ssl certs to get and it’s not practical to manually get and update the txt records every 60 days for all the domains.

BUT… What I can’t grasp still for this automatic acme-dns method is where in the procedure does LE let’s me know what the txt record it is looking for so I can post the credentials and txt to my acme-dns server.

I must be missing something fundamentally simple and it’s in front of my nose and I can’t see it.

Do I manually get the txt record necessary and use that to update the acme-dns instance BUT… that would defeat the automatic part of updating it.

Confused and Crossed eyed now.

The idea is that you also run a Let’s Encrypt client application like Certbot. The Let’s Encrypt client receives the details of the TXT record that the CA wants to have created via the ACME protocol.

With Certbot, this can be used with something like

to then make the necessary updates in acme-dns. After all, acme-dns is not an alternative to a Let’s Encrypt client like Certbot; it’s a complement to the Let’s Encrypt client that exists in order to let it perform a DNS-01 challenge without having a separate DNS provider that supports API-based TXT record updates.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.