mjl- / mox

modern full-featured open source secure mail server for low-maintenance self-hosted email
https://www.xmox.nl
MIT License
3.56k stars 99 forks source link

Automatic DNS Configuration (Cloudflare) #126

Open theduke opened 8 months ago

theduke commented 8 months ago

Thanks for the amazing project.

It would be great to have automatic DNS configuration for known providers so users don't have to create all the records manually and keep them up to date.

Personally I'd want to have Cloudflare support.

mjl- commented 8 months ago

i absolutely agree. i just don't know yet what the best way would be to update dns records at all the cloud providers...

it seems like each cloud provider has its own api for managing dns. cloud providers often have sdk's, but they can be huge. i don't want to add provider-specific support to mox, it would result in unbounded growth of dependencies and binary size.

i still have to see if "dns update", https://datatracker.ietf.org/doc/html/rfc2136, is an option. it doesn't seem used much, there could be a good reason. if there is not a good reason, perhaps a separate tool could be written that folks can run locally to translate dns update requests (that mox makes) into cloud-specific api calls. that way, mox can just do a single standard protocol, and we push the cloud complexity in a separate tool. if "dns update" is not enough, perhaps we need to make up a new one...

something like https://github.com/libdns/libdns can be helpful. i also found https://github.com/kubernetes-sigs/external-dns/, that seems to change dns at various cloud operators.

i don't really understand why dns operators make it so hard to manage zones. they could at least have a "zone import" tool: the zone file format has been around for ages and isn't too hard to parse. it feels like i'm overlooking something obvious...

would be great to get some insights about why this isn't a problem for others, and/or how it is commonly solved.

mjl- commented 8 months ago

you hinted at it, but for completeness: automatic dns management will be great for the initial setup, but also for making changes afterwards.

we can have high-level operations like "rotate dkim key", "rotate host tls key used for dane", "enable account binding on caa record", "update mta-sts policy", where mox can wait for propagation (ttl expiry of dns records) during rollout of the configuration change. that will reduce complexity for/mistakes by the operator.

mjl- commented 8 months ago

about dynamic dns updates: they may not have been popular in the past because not all dns servers could (or still can) regenerate the dnssec signatures after a change (if they don't manage the dnssec themselves). but it's more common for dns servers to fully maintain the zone including signatures themselves (i believe at least bind, powerdns, knot do that).

DanielG commented 5 months ago

I'd be happy with some kind of hook that receives a zone file and is expected to make the DNS updates happen. While that doesn't fit very well with mox's monolithic design IMO at some point you just have to give admins a way to shell out to their custom stuff and DNS is so critical not very many are going to want to adapt to what mox wants.

I just packaged https://dotat.at/prog/nsdiff/ for Debian and I think that's a really neat hack to provide a bridge between zonefiles and DDNS updates for people that do what that.

DanielG commented 5 months ago

You could also implement a DNS XFR primary with NOTIFY support in mox and (crazy) users (like me) could use https://dotat.at/prog/nsnotifyd/ to bridge to whatever DNS setup they like using whatever tools they like.

You can download zones using AXFR with dig in case you don't know, once you have the notification support (using nsnotifyd) it's really easy to write some script to push that into whatever service/cli/api the user wants. This stuff also supports authentication using TSIG so it's not as horrifying as some may remember DNS XFRs :)

DanielG commented 5 months ago

If you do want to go the vendor API route, https://github.com/StackExchange/dnscontrol seems to have an even longer list of supported providers.

mjl- commented 4 months ago

Thanks @DanielG, interesting ideas.

If you use nsdiff, would you also make the updates by running nsupdate? If I'm understanding nsupdate correctly, it also just uses the DNS update protocol? If that's the case, wouldn't it be sufficient to just use DNS updates directly for your case? I'm wondering what other kind of nsupdate-like tools are being used to propagate updates.

At this moment, it still seems like a feasible option to ask users to run a server process that accepts DNS updates (and perhaps AXFR too, for fetching the latest authoritative zone, and perhaps send NOTIFY for detecting changes!).

For cases where people run their own DNS server, they would only have to configure DNS update and AXFR credentials, not run a separate tool or configure scripts to propagate changes. Only when people do custom stuff, like cloud SDKs, would they need to run a separate tool. But even that tool could just be a server that shells out to separate commands to do make updates or fetch the current zone. I don't have enough experience with automated DNS changes to decide what the best way forward is.

Some questions:

DanielG commented 4 months ago

Hi Michiel,

On Thu, May 09, 2024 at 04:03:06AM -0700, Mechiel Lukkien wrote:

If you use nsdiff, would you also make the updates by running nsupdate? If I'm understanding nsupdate correctly, it also just uses the DNS update protocol?

Right, the nsdiff approach would entail sending DDNS updates using one of the nsupdate commandline tools.

I'm wondering what other kind of nsupdate-like tools are being used to propagate updates.

I'm pretty sure there's as many tools and APIs as there are managed DNS providers. :)

At this moment, it still seems like a feasible option to ask users to run a server process that accepts DNS updates (and perhaps AXFR too, for fetching the latest authoritative zone, and perhaps send NOTIFY for detecting changes!).

That process could be an instance of BIND/knot that either serves the zone directly or forwards it further using NOTIFY+XFR or nsnotifyd to do some custom scripting.

Do note however that standards based DDNS suport is extremely rare with managed DNS providers so going that route is going to make things hard for people not wanting to self-host their DNS.

BTW, why do you think mox would need to AXFR the zone? I mean it could be useful for checking the setup is correct but it adds complications.

Only when people do custom stuff, like cloud SDKs, would they need to run a separate tool. But even that tool could just be a server that shells out to separate commands to do make updates or fetch the current zone. I don't have enough experience with automated DNS changes to decide what the best way forward is.

Yeah, that's where something like nsnotifyd comes in for people that like simple scripting, but there's other "scriptable" DDNS update receiving servers out there. I know PowerDNS has lua-dnsupdate-policy-script which may help here. I checked coredns but it doesn't support DDNS unfortunately.

We should be able to point users that don't want to deal with all that to at least some DNS providers that support DDNS. I did a quick search though some DNS providers I know and the only one I came up with that supports RFC2136 is dynv6.com. I suppose standards-based DDNS is somewhat uncommon since providers would want their precious vendor lock-in.

Some questions:

  • How are records exchanged? As zone files as text blobs (that we would have to parse), or as DNS records that are in parsed form with the Go dnsmessage package. It seems nsdiff expects zone files. DNS updates and axfr deal with packed DNS records. Dealing with packed DNS records seems easier to me.

I was proposing a write-only approach for zone files actually. Which seems to me would be easier than dealing with wire-format DNS. Looking at the library situation it seems support for what we need already exists so perhaps direct DNS wire format support would actually not be so hard either. I do think the zonefile approach has merit still see below.

https://github.com/miekg/dns - the library coredns seems to uses looks good

  • How easy is to to configure DNS updates in a DNS server, and AXFR credentials?

Erm. It's not hard as-such, just fiddly. Due to DNS's vulnerability to amplification attacks it's standard practice to lock XFR queries down using source IP ACLs and TSIG. Then there's lots of crypto options you have to get right and if you don't you get no feedback.

When (dynamic) IPs change or source IP selection subtly changes this can cause issues too and this should be monitored for or zones will go stale. However that is something where mox is in a good position to help by checking such things occationally and notifiying the admin if things are amiss.

And how are dynamic updates merged with zone files that admins can edit?

That's determined by the commands you send to nsupdate. See RFC 2136 Section 2.5, you can:

RFC> (1) Add RRs to an RRset. RFC> (2) Delete an RRset. RFC> (3) Delete all RRsets from a name. RFC> (4) Delete an RR from an RRset.

In case you're not familiar with the nomenclature: An "RR" (resource record) is what are colloquially known as a "DNS record". An "RRset" is all such records for a particular record type, say "TXT".

  • Is it common to give credentials only permissions to update a configured set of records (only mail-related records, not others? The question applies to both standard DNS updates and custom cloud APIs.

Common? Probably not, people are lazy. I like doing that where possible but it's a fiddly affair to get the ACL just right.

  • How do cloud DNS APIs handle notify of updates? Do they have webhooks instead of DNS notify, or do they implement regular DNS notify? Or neither?

No idea about any of the cloudy stuff sorry. Are you sure mox users want to host their DNS with a cloud provider? I would expect traditional hosting providers to be more popular with mox's userbase.

If what you're looking for maximum ease of use I'd think about integrating an auth. DNS server into mox, keeping the manual initial DNS setup and have users delegate only the _domainkeys subdomain to mox :)

That's certainly no harder for the user than the current DNS record dance and removes the need for ongoing changes due to DKIM rotation.

  • Should we call shell scripts to propagate DNS updates and get AXFR, or should we use DNS updates and AXFR directly? Is DNS updates actually working well enough to make all changes we need? (seems like people do use it from a quick web search)

I think it comes down to this: If I'm already self-hosting DNS then receiving DDNS updates is easy, but if a user is depending on a managed provider turning those DDNS updates into something they can stuff into their provider's API turns into an infrastructure task in it's own right, the thing they were trying to avoid by having someone else take care of their DNS infrastructure :)

Sure mox can depend on one of those DNS provider abstraction libraries/tools but as you've seen those are huge and incomplete by definition. I did find https://packages.debian.org/bookworm/lexicon which doesn't look too huge, but I'm sure it doesn't support everyone's favorite provider.

I continue to suggest the write-only zone-file plus hook approach since it makes self-hosting possible but custom scripting easy. This is essential since DNS providers are all special snowflakes with their own custom APIs with fairy dust on top.

In my mind we could still default the scripting hook to a mox internal command that handles a couple of popular providers or some such. Hell, we could even autodetect based on NS records at the zone apex :)

The proposed DNS update flow then looks like this:

Notice: thre's no need for mox to parse any zonefiles, only write them -- which it already does for quickstart ;)

--Daniel

bwbroersma commented 2 months ago

I recently found out about Domain Connect (github, expired draft RFC).

One can easily check support via a _domainconnect.domain.example TXT lookup.

It is supported by:

And under development at:

I think it will need a manual confirmation (e.g. initiated via email). It might not be the best security practice to roll out, and I'm unsure if a decentralized setup is possible or that there needs to be a central registered key / returnURI at each DNS provider. But the idea is very interesting, especially since I see Cloudflare, GoDaddy, NameSilo, Plesk and cPanel.

mjl- commented 2 months ago

That's interesting to keep an eye on, hadn't heard about it before. Indeed it's a question if we could roll it out decentrally. It seems to have a notion of record templates, and operators needing to opt in to your service. Also, mox may want to set more or fewer records per case (e.g. domain being dnssec-protected, user wanting acme account binding).

In other news, I finally tried using dynamic dns/dns update for my own setup (bind9). It was easy to set up and use. Authentication with a TSIG key, automatic DNSSEC signing, and propagation to secondary. I haven't set up finegrained update policies, but this can be configured (restricting which records (type/name) a key can change). So I still think that's an option. Won't have time to work on that any time soon though.

bwbroersma commented 4 weeks ago

Just to add, https://ip.addr.tools (see brianshea2/addr.tools) also supports RFC 2136 - DNS Update, with or without TSIG to deploy an ACME challenge to their system.