doc: update README according to rewrite

This commit is contained in:
xaos 2023-03-22 19:20:48 +01:00
parent d7c6d1fd39
commit e36782c04b

View file

@ -19,6 +19,8 @@ Pros:
- no need to connect to another server to complete the challenge - no need to connect to another server to complete the challenge
- no propagation delays - no propagation delays
- no separate DNS server process to manage - no separate DNS server process to manage
- server only listens for DNS queries when it has records to serve
- can also serve arbitrary DNS records defined in the configuration
Cons: Cons:
- requires one additional DNS record in the requested zone - requires one additional DNS record in the requested zone
@ -26,16 +28,30 @@ Cons:
- ACME CA (i.e. Let's Encrypt) needs to connect to your server (like the [HTTP](https://caddyserver.com/docs/automatic-https#http-challenge) & [TLS-ALPN](https://caddyserver.com/docs/automatic-https#tls-alpn-challenge) challenge) - ACME CA (i.e. Let's Encrypt) needs to connect to your server (like the [HTTP](https://caddyserver.com/docs/automatic-https#http-challenge) & [TLS-ALPN](https://caddyserver.com/docs/automatic-https#tls-alpn-challenge) challenge)
- can't have another public DNS server running on the same IP (see [below](#already-running-a-dns-server)) - can't have another public DNS server running on the same IP (see [below](#already-running-a-dns-server))
- can't have multiple Caddies running on different hosts authenticate for the same domain - can't have multiple Caddies running on different hosts authenticate for the same domain
- relies on deprecated & undocumented Caddy behavior (for now)
- doesn't support [`dns_challenge_override_domain`](https://caddyserver.com/docs/caddyfile/directives/tls#dns_challenge_override_domain) / [`override_domain`](https://caddyserver.com/docs/modules/tls.issuance.acme#challenges/dns/override_domain) (yet)
## Limitations & Bugs
## Required DNS Record - non-`IN` class records are not supported
- no DNSSEC or EDNS
- UDP-only
- currently, only one DNS server can be defined, and it can only listen on a single address
- not optimized
- reloading / changing the configuration while attempting to solve the DNS challenge will probably cause it to fail
The DNS needs to be set up to direct all DNS queries for the `_acme-challenge` subdomain of the zone you're trying to authenticate to the server that's running Caddy. Currently, solving the DNS challenge seems to require disabling the propagation checks.
This may or may not be a bug with the implementation.
In any case, there is no reason to wait & check for propagation, since the server will start listening immediately.
A potentially serious issue exists with the way records are parsed from the configuration (Caddyfile & JSON): because it is possible to `$INCLUDE` a *file*, it may be possible for an attacker *who has control over the configuration* (and perhaps even a rogue ACME CA, though this is seems unlikely) to get Caddy to read files it shouldn't.
Parts of those files could then be exposed in the Caddy logs and, if they happen to look like zone files, get served over DNS.
## Required DNS Record for the ACME challenge
To solve the ACME DNS challenge, the DNS zone needs to be set up to direct all DNS queries for the `_acme-challenge` subdomain of the zone you're trying to authenticate to the server that's running Caddy.
This should be pretty simple, but you still need to be careful and make sure **only** queries for the `_acme-challenge` subdomain get sent to your server. This should be pretty simple, but you still need to be careful and make sure **only** queries for the `_acme-challenge` subdomain get sent to your server.
Otherwise, you would need to configure the DNS app to serve all the records for your zone.
So, let's say you have this record (could be an `AAAA` record too): So, let's say you have this record (could be an `AAAA` record too) wherever you bought your domain/host your DNS:
``` ```
example.com. A 192.0.2.123 example.com. A 192.0.2.123
``` ```
@ -54,11 +70,11 @@ The second record, which you need to add for this module to work, means "if you
This will cause the client (e.g. Let's Encrypt or another ACME CA) to look up the first record as well, since it now knows it has to *connect to* `example.com` (though not to make an HTTP request like your browser would) to complete the query, and then it will get the `TXT` record for the challenge directly from your server. This will cause the client (e.g. Let's Encrypt or another ACME CA) to look up the first record as well, since it now knows it has to *connect to* `example.com` (though not to make an HTTP request like your browser would) to complete the query, and then it will get the `TXT` record for the challenge directly from your server.
## Configuration ## Configuration
The provider requires only one configuration value: the (local) IP address and port to serve the DNS on. The provider does not require any configuration value, but it might be necessary to configure the server with the (local) IP address and port to serve the DNS on.
If the IP is not specified, like in `:53`, DNS will be served on *all* of the addresses assigned to the machine, and this may work for you. If no address is configured, the server will default to `:53`.
In this case, when the IP is not specified, DNS will be served on *all* of the addresses assigned to the machine, and this may work for you.
However, many systems already have a DNS server running for local use: for instance, [systemd-resolved](https://wiki.archlinux.org/title/Systemd-resolved) listens on `127.0.0.53` by default. However, many systems already have a DNS server running for local use: for instance, [systemd-resolved](https://wiki.archlinux.org/title/Systemd-resolved) listens on `127.0.0.53` by default.
In this case, it will not be possible to bind to the wildcard address, since it would overlap with systemd-resolved, and the provider will fail with `bind: address already in use`. In this case, it will not be possible to bind to the wildcard address, since it would overlap with systemd-resolved, and the provider will fail with `bind: address already in use`.
To avoid this, specify the (externally accessible) IP address you want to use. To avoid this, specify the (externally accessible) IP address you want to use.
@ -66,13 +82,17 @@ To avoid this, specify the (externally accessible) IP address you want to use.
For the port, you'll need to use `53` since that is the DNS port, and that's where Let's Encrypt (or whatever ACME CA you use) will query for the challenge. For the port, you'll need to use `53` since that is the DNS port, and that's where Let's Encrypt (or whatever ACME CA you use) will query for the challenge.
Still, this isn't hard-coded to allow for more complicated setups and forwarding. Still, this isn't hard-coded to allow for more complicated setups and forwarding.
Note: It is technically possible to specify a protocol before the address (as in `udp/127.0.0.1:53`).
Do not do this.
Only UDP is supported, specifying other protocols will either cause an error, or worse, get silently ignored.
### Already running a DNS server? ### Already running a DNS server?
If you're already hosting a DNS server on the machine that's running Caddy, you'll need to do some additional configuration. If you're already hosting a DNS server on the machine that's running Caddy (and you don't want to make Caddy serve all its records), you'll need to do some additional configuration.
The issue essentially boils down to not having enough IP addresses to host DNS on (you can create as many subdomains as you like, but they all have to point to an IP address in the end), and it can be resolved in two ways. The issue essentially boils down to not having enough IP addresses to host DNS on (you can create as many subdomains as you like, but they all have to point to an IP address in the end), and it can be resolved in two ways.
The first and arguably *cleaner* solution is to figure out a way to get your DNS server to forward / recurse queries for the `_acme-challenge` subdomain to some internal address & port and have `stub_dns` listen on that. The first and arguably *cleaner* solution is to figure out a way to get your DNS server to forward / recurse queries for the `_acme-challenge` subdomain to some internal address & port and have this module listen on that.
Note though that DNS has two kinds of "forwarding": one where the server will tell the client where to go to make their query ("iterative") and one where the server will do it on behalf of the client ("recursive"). Note though that DNS has two kinds of "forwarding": one where the server will tell the client where to go to make their query ("iterative") and one where the server will do it on behalf of the client ("recursive").
Obviously, if you use an internal address that the client (e.g. LE) can't reach itself, you'll need to get your server to do the second kind. Obviously, if you use an internal address that the client (e.g. LE) can't reach itself, you'll need to get your server to do the second kind.
If you manage to get this to work, please let people know how! If you manage to get this to work, please let people know how!
@ -80,14 +100,22 @@ If you manage to get this to work, please let people know how!
The second solution is to just use IPv6 since you probably have tons of IPv6 addresses you can use anyway, and Let's Encrypt has supported it for many years. The second solution is to just use IPv6 since you probably have tons of IPv6 addresses you can use anyway, and Let's Encrypt has supported it for many years.
This is (arguably, again) less clean than the first because you'll need to set up another `NS` record and also an `AAAA` record to point it to, but it may be easier if you already have a reasonable setup for IPv6 (e.g. firewall rules). This is (arguably, again) less clean than the first because you'll need to set up another `NS` record and also an `AAAA` record to point it to, but it may be easier if you already have a reasonable setup for IPv6 (e.g. firewall rules).
## Caddy module name ## Caddy module names
App:
``` ```
dns.providers.stub_dns dns
```
Provider:
```
dns.providers.internal
``` ```
## Config examples ## Config examples
### ACME DNS Provider
To use this module for the ACME DNS challenge, [configure the ACME issuer in your Caddy JSON](https://caddyserver.com/docs/json/apps/tls/automation/policies/issuer/acme/) like so: To use this module for the ACME DNS challenge, [configure the ACME issuer in your Caddy JSON](https://caddyserver.com/docs/json/apps/tls/automation/policies/issuer/acme/) like so:
```json ```json
@ -96,8 +124,7 @@ To use this module for the ACME DNS challenge, [configure the ACME issuer in you
"challenges": { "challenges": {
"dns": { "dns": {
"provider": { "provider": {
"name": "stub_dns", "name": "internal"
"address": "IP_AND_PORT_TO_SERVE_DNS_ON"
} }
} }
} }
@ -109,8 +136,48 @@ or with the Caddyfile:
``` ```
# one site # one site
tls { tls {
dns stub_dns ... dns internal
# disable propagation checks, may be necessary
propagation_timeout -1
} }
``` ```
Unlike other providers, global configuration with `acme_dns` does *not* work! Unlike other providers, global configuration with `acme_dns` does *not* work!
### DNS Server
The server can be configured with an address to bind to, and records to serve.
Both are optional.
```json
{
"apps": {
"dns": {
"address": "192.0.2.123:53",
"records": [
"example.com.\t3600\tIN\tA\t192.0.2.123"
]
}
}
}
```
Or, in the [global options](https://caddyserver.com/docs/caddyfile/options) block at the beginning of the Caddyfile:
```
{
dns 192.0.2.123:53
}
```
And to define a record to serve:
```
{
dns 192.0.2.123:53 {
record "example.com. A 192.0.2.123"
}
}
```
The syntax for defining records is pretty straight-forward, at least for simple record types.
It uses the [`miekg/dns.NewRR()`](https://pkg.go.dev/github.com/miekg/dns#NewRR) function to parse the definitions.
The linked page has (some) more information: "full [zone file](https://en.wikipedia.org/wiki/Zone_file) syntax" is supported.