acme-dns
acme-dns copied to clipboard
NS and A record can both exist for 1 domain?
in README.md:
- NS record for auth.example.org pointing to auth.example.org (this means, that auth.example.org is responsible for any *.auth.example.org records)
- A record for auth.example.org pointing to 198.51.100.1
seems the doc above assumes that for the domain: auth.example.org, NS record and A record can exist at the same time, but in Cloudflare, I failed to do so.
After add an A record for my domain: my-dns-server.xxx.com, I failed to add NS record for it, the error is: non-NS records already exists with that host. (Code: 81055)
In Cloudflare, I have 2 records:
type | name | content |
---|---|---|
NS | acme-dns | ns.acme-dns.example.com |
A | ns.acme-dns | 192.0.2.0 |
In acme-dns' config file, I have 3 records:
# default A
"acme-dns.example.com. A 192.0.2.0",
# A
"ns.acme-dns.example.com. A 192.0.2.0",
# NS
"acme-dns.example.com. NS ns.acme-dns.example.com."
This is based on the older setup instructions, but this has worked for several years and works with the current codebase.
A lot more people seem to have issues with the current setup instructions than the older ones (which also caused issues)
Thanks @jvanasco . I am one of the people who is confused by the instructions, but I blame my lack of knowledge and experience, not necessarily the instructions.
Can you provide your insights on the naming convention for these entries? I have changed them around based on what makes sense to me, but I am trying to learn why this might NOT be correct:
type | name | content | question/comment |
---|---|---|---|
NS | ns1.acme-dns | acme-dns.example.com | I would have thought the NS record name would have a value of NS in the name, and content contain the value "acme-dns.example.com". As way of contrast, the content value could have been "banana.example.com", which would not make any sense to most people, but I would know that the banana subdomain is where my DNS server resides. Is there a reason why this naming approach is opposite in your cloudflare example? |
A | acme-dns | 192.0.2.0 | Again, this record is the subdomain pointing to a particular IP, which should represent a particular machine. If I name this A record "banana", it does not really matter if the machine at that IP is running a DNS server, or database server, or whatever. I thought it was the NS record that says "the device at 'acme-dns.example.com' (which is a machine running on 192.0.2.0 as specified through this A record) should expect to find a name server there that will respond to DNS queries". Or another way I interpret this, "I am an NS record named 'ns1.acme-dns.example.com'. If you query me, I point to 'acme-dns.example.com', and they will fulfill your DNS query". Am I way off base here on how these DNS Records work? |
This all matters because I get confused with what the values should be provided in the config.cfg
file. From the instructions:
# domain name to serve the requests off of
domain = "auth.example.org"
# zone name server
nsname = "auth.example.org"`
Based on the NS and A records from your cloudflare example, I would guess the values would be:
domain = "ns.acme-dns.example.com"
nsname = "acme-dns.example.com"
which further confuses me. Did I misunderstand how these pieces fit together?
I appreciate your assistance in helping me understand. I have read so much on this, I think I have just confused myself (RFC-1034, RFC-1035, RFC-2606, RFC-4367, RFC-2219, and tons of blogs, write-ups, and unhelpful youtube videos!).
Thanks again for your insights.
Here is the read me at an earlier version, which uses these names and has descriptions :
https://github.com/joohoi/acme-dns/blob/0991b3e3c9f1dc56c596e05af149820c02bfd89e/README.md
It was changed to the new names for clarity, but I find this set of instructions easier.
That's interesting, thanks for the link. So if I read that correctly, the older instructions assumed there was an A record for the ns1.xxx.xxx.xxx, in addition to the NS record? Or maybe at some point it was realized that you can simply add an NS record, point it to the A record and call it a day (i.e. simplify the host DNS records). I will have to think on this, probably do some experimenting to verify.
One other item I noticed in the older readme, the tls parameter is set to "none"; in the newer version it is set to "letsencryptstaging". @rtfmoz2 found this change was causing a problem starting acme-dns as a service and reported as issue #228.
Following up after more investigation and documenting what I learned so future confused people can hopefully benefit.
The way I have been thinking about how NS records work is in fact incorrect. The critical assumption here is that acme-dns.example.com
not only specifies a subdomain, but also represents a DNS sub-zone that will be independently managed (i.e. a separate name server) which will be responsible for that subdomain. This implies that everything at acme-dns.example.com
, including additional sub-domains (i.e. www.acme-dns.example.com
, or foo.bar.acme.dns.example.com
) would be managed by this other nameserver that will be identified in the NS record. This is what we want, and exactly what @joohoi states in his instructions.
DNS servers process in a series of recursions that work through referrals. NS records ARE the referrals. So to resolve acme-dns.example.com
we would start with root nameservers that have NS records that refer to top level domains (TLDs), such as "com.", "net.", "org,", etc. The root nameservers do not contain records for our specific acme-dns.example.com
, but they have referrals (NS Records) to nameservers for com.
, matching the first part of our query (the farthest to the right). Now the server will re-query for acme-dns.example.com
to just the com.
nameservers. The com.
nameservers will have a NS record for example
which will be another referral to nameservers responsible for example.com
. This will repeat again, querying for acme-dns.example.com
to the nameserver(s) responsible for example.com
domain until it finds acme-dns.example.com
.
This recursion will continue until it a) finds an appropriate A record, known as a "glue record", which will point to the corresponding IP address and successfully resolve the query; or b) responds with NXDOMAIN (non-existent domain) because there were no more referrals (NS records) and no A records to successfully resolve the query.
Going back to our examples above, here is my revised commentary:
Type | Name | Content | Comment |
---|---|---|---|
NS | acme-dns | ns1.acme-dns.example.com | The name field needs to specify the subdomain we want to manage separately, the content field needs to specify the DNS name where we can find the new nameserver. We can interpret this as "for the subdomain of 'acme-dns.example.com', use the nameserver called 'ns1.acme-dns.example.com' to resolve your queries". |
A | ns1.acme-dns | 192.0.2.0 | We have to resolve where the NS record is pointing to (in this case ns1.acme-dns.example.com), so create the glue record so DNS queries know what IP address to respond with when looking for acme-dns.example.com |
If you setup a new nameserver, you have to create a glue record (A record) for that NS entry, otherwise you will create a circular reference (an endless loop) in your DNS entries. Specifying the A record will avoid the loop and allow DNS queries to resolve correctly.
I know that @jvanasco and @joohoi have explained what the configuration settings should be, hopefully my sharing what I learned will help others understand why the settings need to be the way they are.
I think the critical point here is the NS records can only be FQDN entries not IP addresses. So, you have to specify a FQDN for the NS record. That FQDN could be anything, but ideally another A record that you manage and can point to the IP address that is hosting the acme-dns DNS service (i.e. the service sitting behind port 53).
For example:
$ dig google.com. ns
...
;; ANSWER SECTION:
google.com. 7160 IN NS ns4.google.com.
google.com. 7160 IN NS ns1.google.com.
google.com. 7160 IN NS ns2.google.com.
google.com. 7160 IN NS ns3.google.com.
Then, taking ns1.google.com as example:
$ dig ns1.google.com. A
...
;; ANSWER SECTION:
ns1.google.com. 7192 IN A 216.239.32.10
So, if google had a subdomain acme-dns.google.com there would be an NS record for that subdomain which could be anything and intern resolve to the appropriate IP address with the acme-dns service running.
Hope that helps to clarify some more.
Hi @DiveInto just now I experienced the same thing, and I managed to find a way by deleting existing records before you add NS records. Make sure the existing records value has been applied in the new domain manager (delegated).
Okay, so I'm trying to set this up myself and I want to thank both of you, @jvanasco and @OneAceGuy, for at least pointing me in a direction that appears to be helpful. The older version of the README seems much more clear to me as well, so thank you so much for posting that. However, I still have a couple of related questions.
First of all, other than saving a couple of keystrokes, I don't see how creating the NS
record for auth.example.org
pointing to auth.example.org
is any "simpler" than pointing it to ns1.auth.example.org
since you still need the A
record pointing to the public IP anyway. In fact, it seems to me that the ns1.auth.example.org
version actually makes the entire flow of DNS resolution more clear in the long run. The version that points directly back to itself (auth.example.org
-> auth.example.org
) presents a loop in the logic in my mind, which I actually ran up against in my second point/question:
Secondly, my DNS host would not allow me to set up the NS
record as defined in the current README (auth.example.org. 86400 IN NS auth.example.org.
) because, when they tried to test that configuration with my A
record set up to point to my ACME-DNS server's public IP address, it resulted in an infinite loop of DNS lookups:
Current DNS query (
auth.example.org. 86400 IN A 198.51.100.1
): Client -> query goes to Registrar -> Registrar delegated the zone to DNS Host -> DNS Host DNS servers display theA
record198.51.100.1
DNS query with NS record (
auth.example.org. 86400 IN NS auth.example.org.
): Client -> query goes to Registrar-> Registrar delegated the zone to DNS Host -> DNS Host DNS servers delegates the subdomain toauth.example.org
-> and then it returns to the Registrar for the root domain and loops infinitely
By creating the A
record as ns1.auth.example.org. 86400 IN A 198.51.100.1
and the NS
record pointing to that as auth.example.org. 86400 IN NS ns1.auth.example.org.
, it should "loop" once, but prevent the infinite recursion b/c the second lookup will actually be for ns1.auth.example.org
instead of asking again for just auth.example.org
. I believe the ns1.xxx.xxx.xxx
method is a more reliable way overall to ensure that the DNS host is able to avoid such infinite loop scenarios. Is there something simply inherent in the way that certain DNS servers/resolvers operate differently than others that causes this recursion issue, or am I just being dense?
Finally, and somewhat tangentially, my (limited) understanding is that the A
record created in this process acts as a "glue" record. Now, I readily admit that I'm far from having an in-depth knowledge of DNS, so I'm still looking for a satisfying answer of what a "glue" record is, exactly, and, more importantly to me at this point is, is there any difference between creating a "glue" record and any other A
record for the zone. Or, to ask it another way, if I were to look at just the "glue" record and another A
record in the zone, could I tell a difference (besides the specific details about the host, target, and TTL)?
i.e., I don't see anything "special" that I have to do before, after, or during the creation of the A
record to flag it as a "glue" record. Unless it's simply that this particular (regular, old) A
record exists in the zone to resolve the target of the NS
record, and that's the only thing that identifies it as a "glue" record instead of any other, run-of-the-mill A
record. If someone could please confirm/clarify this for me just so I can stop Googling, I'd greatly appreciate it.
Just to provide a more "concrete" example to (maybe) help visualize this whole thing and answer the questions, here is the (redacted) zone file for the domain on which I am trying to set up my ACME-DNS server:
;Configuration for DNS Zone example.org
;-----;example.org;
example.org. 300 IN SOA ns.mydnshost.com. dnsadmin.mydnshost.com. (
1234567890
900
900
1800
60
)
example.org. 86400 IN A 198.51.100.1
ftp.example.org. 86400 IN A 198.51.100.2
portal.example.org. 86400 IN A 198.51.100.3
support.example.org. 86400 IN A 198.51.100.4
[...]
example.org. 86400 IN MX 0 mail.example.org.
example.org. 86400 IN NS ns2.mydnshost.com.
example.org. 86400 IN NS ns.mydnshost.com.
auth.example.org. 86400 IN NS ns1.auth.example.org.
[...]
ns1.auth.example.org. 86400 IN A 198.51.100.100
I'm still waiting for my DNS host to actually add the NS
record for auth.example.org
so I can't test everything out yet. But, if I'm correctly understanding what I'm reading here, that last line (the A
record for ns1.auth.example.org
that looks basically like every other A
record in the zone) is a "glue" record. The only way one could know that it's specifically a glue record would be by looking at the entire zone and recognizing the fact that this particular A
record defines the IP address of one of the NS
records in the zone.
Pretty sure you just need the NS record [in your main DNS], which in turn delegates the lookup (including the A record) to the acme-dns service for that whole subdomain.
Thanks for pointing out my less-than-clear explanation above, @webprofusion-chrisc and sorry for the confusion. The zone file I posted is for the main/root domain (with my DNS hosting service). I was trying to get some clarity regarding the specific DNS records required with the DNS host/resolver for the root of the domain, including the glue record pointing to the nameserver that will be hosted by my local ACME-DNS installation.
From what I understood reading the current README.md, it says to create the A
record without the ns1
prefix (auth.example.org
) and the NS
record pointed to that A
record on the DNS server where the main/root domain is being resolved. However, my DNS host tells me that it can't be done like that (ref. the second section in my previous post). That's why it looks like the instructions for this process from the previous version of the README posted by @jvanasco seem like a more "accurate"/"reliable" configuration.
Just for additional reference, I have the ACME-DNS server configured with the A
record for the delegated auth.example.org
subdomain:
[general]
# DNS interface. Note that systemd-resolved may reserve port 53 on 127.0.0.53
# In this case acme-dns will error out and you will need to define the listening interface
# for example: listen = "127.0.0.1:53"
listen = "10.1.1.100:53"
# protocol, "both", "both4", "both6", "udp", "udp4", "udp6" or "tcp", "tcp4", "tcp6"
protocol = "both"
# domain name to serve the requests off of
domain = "auth.example.org"
# zone name server
nsname = "ns1.auth.example.org"
# admin email address, where @ is substituted with .
nsadmin = "dnsadmin.example.org"
# predefined records served in addition to the TXT
records = [
# domain pointing to the public IP of your acme-dns server
"auth.example.org. A 198.51.100.100",
"ns1.auth.example.org. A 198.51.100.100",
# specify that auth.example.org will resolve any *.auth.example.org records
"auth.example.org. NS ns1.auth.example.org.",
]
I hope that helps to clarify what I was trying to ask. Please let me know if I can provide any more detail, and thanks again.
EDIT: Actually, I just realized that I probably don't need the A
record for ns1.auth.example.org
in the ACME-DNS configuration, since it's already set up on the main DNS host, so I'll go ahead and get rid of that. Still, I believe I have everything else set up "correctly" and, when my DNS host finally adds the NS
record to my main domain's zone file, I should be able to get things rolling.
Okay. Please let me know if I'm completely off-base here but I believe I'm finally starting to fully understand what I'll need to do from start to finish with regards to configuration (at least in my environment). Here's basically what I've come up with:
DNS Records
NOTE: In this documentation:
example.org
is the root domain you are managing (either through your own or third-party-hosted DNS services)- (optional)
ftp.example.org
is a subdomain for which you want acme-dns to serve and process ACME DNS-01 challengesauth.example.org
is the hostname of your acme-dns server- acme-dns will serve
*.auth.example.org
records198.51.100.1
is the public IP address of the system running acme-dns These values should be changed based on your environment.
First, you will need to add some DNS records on the main DNS server for
example.org
:
- Create an
NS
record forauth.example.org
pointing tons1.auth.example.org
(this means, thatns1.auth.example.org
is responsible for resolving any/all*.auth.example.org
requests)
- example:
auth.example.org. 86400 IN NS ns1.auth.example.org.
- Create an
A
record forns1.auth.example.org
pointing to198.51.100.1
(this is the "glue" record that prevents the DNS lookup from looping indefinitely)
- example:
ns1.auth.example.org. 86400 IN A 198.51.100.100
- If you're using IPv6, create an
AAAA
record forns1.auth.example.org
pointing to the IPv6 address of your acme-dns server
- example:
ns1.auth.example.org. 86400 IN AAAA ::ffff:c633:6401
- After you've completed the registration, you will also need to create one or more
CNAME
records pointing to the acme-dns subdomain the registration generates
- The ACME client you use will provide additional details on how to get this "ACME Magic" subdomain for the target of these
CNAME
records.- For the root domain or a wildcard domain:
_acme-challenge.example.org. CNAME a097455b-52cc-4569-90c8-7a4b97c6eba8.auth.example.org
- For each subdomain (e.g.,
ftp.example.org
):
_acme-challenge.ftp.example.org. CNAME a097455b-52cc-4569-90c8-7a4b97c6eba8.auth.example.org
Configuration
Sample
config.cfg
file[general] # DNS interface. Note that systemd-resolved may reserve port 53 on 127.0.0.53 # In this case acme-dns will error out and you will need to define the listening interface # for example: listen = "127.0.0.1:53" listen = "127.0.0.1:53" # protocol, "both", "both4", "both6", "udp", "udp4", "udp6" or "tcp", "tcp4", "tcp6" protocol = "both" # domain name to serve the requests off of domain = "auth.example.org" # zone name server nsname = "ns1.auth.example.org" # admin email address, where @ is substituted with . nsadmin = "dnsadmin.example.org" # predefined records served in addition to the TXT records = [ # domain pointing to the public IP of your acme-dns server "auth.example.org. A 198.51.100.1", "ns1.auth.example.org. A 198.51.100.1", # specify that auth.example.org will resolve any *.auth.example.org records "auth.example.org. NS ns1.auth.example.org.", ] [...]
Usage (self-hosted acme-dns server)
- Get credentials and unique subdomain (simple POST request to
http://auth.example.org/register
)
One "gotcha" I believe I see in this process (and, I could certainly be wrong or simply over-thinking things) is with regards to the registration on the self-hosted acme-dns server. If I'm correct in my assumptions, I'll need to do that registration through my server - i.e., I can't register on auth.acme-dns.io
and use those credentials on my self-hosted acme-dns server.
Let me try to clarify this. Basically there are two ways the DNS setup can work work.
Options 1: your server's primary name is "acme.example.com". You then delegate the domain "acme.example.com" to this server, whose name also happens to be "acme.example.com" (yes that seems weird, but it is perfectly valid).
This delegation allows your server to respond to DNS queries for both acme.example.com
and <subdomain>.acme.example.com
To configure this, in the parent domain (example.com), you need to insert an NS record for the delegation:
acme.example.com. NS acme.example.com.
You also need to insert glue record(s) into the parent domain, so that a resolver chasing the referral can solve the "chicken-and-egg" problem of needing the nameserver's IP address before it can do the resolution of name to IP address.
acme.example.com. A 192.0.2.1
acme.example.com. AAAA 2001:db8::1
Then, on the acme-dns server itself, you also need to hold the authoritative, identical copy of all these records:
domain = "acme.example.com"
records = [
"acme.example.com. NS acme.example.com.",
"acme.example.com. A 192.0.2.1",
"acme.example.com. AAAA 2001:db8::1",
]
This is because the values in the parent zone are, in effect, just hints to the resolver. The resolver will use them to locate the authoritative nameserver, but will only return an answer fetched from the authoritative server itself.
You should also put the corresponding settings in the SOA record, although these don't really make much difference in practice, you're just documenting things for a human to see:
nsname = "acme.example.com"
nsadmin = "hostmaster.example.com"
This is the way the sample configuration in the documentation works. And of course, the API endpoint is https://acme.example.com
Option 2: the acme-dns server has a different name outside of this zone, for example nsa.example.net
In this case, the name nsa.example.net
has been set up to resolve to IPv4 and/or IPv6 addresses already. So you just need a simple delegation with no glue:
acme.example.com. NS nsa.example.net.
Within the zone, you still need to return these records:
domain = "acme.example.com"
records = [
"acme.example.com. NS nsa.example.net.",
"acme.example.com. A 192.0.2.1",
"acme.example.com. AAAA 2001:db8::1",
]
This is because we still need acme.example.com
to resolve to the right address for the API endpoint https://acme.example.com
, and we need an authoritative in-zone copy of the NS record.
Here are the matching SOA settings, again not really important:
nsname = "nsa.example.net"
nsadmin = "hostmaster.example.com"
Now, in this case you might think that https://nsa.example.net
would also work as the API endpoint, because it resolves to the same IP address - but it doesn't. This is firstly because acme-dns only fetches a certificate for "acme.example.com"; it's unable to fetch one for nsa.example.net (well, maybe it could using a http-01 challenge, but it doesn't). And secondly, when an incoming https connection arrives, acme-dns looks at the Server Name Indication for the hostname that the client is trying to connect to, and when it sees the user is trying to talk to a host other than "acme.example.com" it drops the connection on the floor.
Aside: there is an option 3, but you should forget about it. This is when your server is known as ns1.acme.example.com
(for delegation) and acme.example.com
(for API). It requires even more records to be added to the acme-dns server records
section, and it will just confuse you even more. You can make it work, but why bother when (1) and (2) are simpler?
If I'm correct in my assumptions, I'll need to do that registration through my server - i.e., I can't register on auth.acme-dns.io and use those credentials on my self-hosted acme-dns server
It's either/or.
If you register on auth.acme-dns.io
, then you put CNAME records pointing at <subdomain>.auth.acme-dns.io
, and you send your DNS updates to the API auth.acme-dns.io
. The subdomain, username and password are all the ones which were returned from auth.acme-dns.io
when you registered.
If you register on acme.selfhosted.com
, then you put CNAME records pointing at <subdomain>.acme.selfhosted.com
, and you send your DNS updates to the API at acme.selfhosted.com
. The subdomain, username and password are all the ones which were returned from acme.selfhosted.com
when you registered.
It has to work like this: the same server that you registered with is the same one that's checking the username and password when you connect, against what's in its local database (postgres or sqlite). There's no crossover between these, because the databases aren't connected.
Hi,
I've tried creating an A record for ns.acme.example.com
to a public IP and NS record for acme.example.com
pointing to ns.acme.example.com
as mentioned above. However, the NS record was not getting propagated at all. (I've waited for 2 days)
DNS provider tested : Cloudflare, Hurricane electric
Can anyone please advise how to get this working? Thank you
Firstly, please don't hijack an existing issue with a new problem.
Secondly, I recommend you follow option 1 in the post above, especially if you don't really understand DNS. What you should do is in the master nameserver for the "example.com" zone, add an A record for "acme.example.com" pointing to a public IP and an NS record for "acme.example.com" pointing to "acme.example.com".
Don't attempt to use ns.acme.example.com as the name of the acme-dns server unless you fully understand DNS delegations and glue records. And unless you tell us the actual domain rather than "example.com", we can't give you any more specific advice.
Hi @candlerb
Thanks for your reply and guidance.
I went back with Hurricane Electric which allows me to use option 1, after solving the default listen: 127.0.0.1:53
, it is now working as expected.
P.S. The reason I created ns.acme.example.com
and acme.example.com
because this is how auth.acme-dns.io
set-up.
dig +short auth.acme-dns.io ns
ns.auth.acme-dns.io.
Here is the read me at an earlier version, which uses these names and has descriptions :
https://github.com/joohoi/acme-dns/blob/0991b3e3c9f1dc56c596e05af149820c02bfd89e/README.md
It was changed to the new names for clarity, but I find this set of instructions easier.
Agreed; much easier explanation - I actually got it to work now. The current example leads to believe that everything should be tied to auth.example.org.
Thank you for resolving HOURS of diagnostication .. what also helped (and might help others) was this little tool: https://dig.ping.pe/
E.g., https://dig.ping.pe/965d5223-c904-43e9-860e-b64fce111f71.auth.example.org:TXT:8.8.8.8 returned Status: NXDOMAIN
but after your clearer finding, now returns 965d5223-c904-43e9-860e-b64fce111f71.auth.example.org. 1 IN TXT "EOiXmyLVFGDxLAGz7yYjVdEjZC6NefLfgWdO8xIzdkW"
https://dig.ping.pe/965d5223-c904-43e9-860e-b64fce111f71.auth.example.org:TXT:8.8.8.8
Note: auth.example.org should of course be changed with your domain name; this was just for clarity and link into current and previous documentation as highlighted by @jvanasco