DNS over TLS and HTTPS set up in one step
jarl encrypted-dns setup (and the Encrypted DNS page) finds or obtains a certificate for the host name, checks that nothing else listens on the DoT and DoH ports at the chosen addresses (offering a free DoH port), and stages named's tls statement and listeners as one change, keeping port 53 where it was when there was no listen-on, plus the host name's records in the private views (local to the view in a linked zone). Status shows what to give Android, Linux, Firefox, Chrome and Windows clients. A tls statement with missing files is refused (tls_file_missing), and a certificate named uses can't be deleted. docs/encrypted-dns.md, e2e with real DoT and DoH queries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ebd3a3dfbe
commit
66dcf53684
21 files changed
+1790
-8
No files matched your search
@@ -0,0 +1,131 @@
|
||||
# DNS over TLS and HTTPS
|
||||
|
||||
named (BIND 9.20) can answer over encrypted connections itself: DNS over
|
||||
TLS (DoT, port 853) and DNS over HTTPS (DoH, at `/dns-query`). Your
|
||||
phones, laptops and browsers then ask this server without anyone on the
|
||||
network seeing or changing their queries, and they get exactly what plain
|
||||
DNS gives them: the same views, the same records and the same blocking.
|
||||
|
||||
Jarl sets it up in one step: a certificate for the host name clients
|
||||
connect to (obtained and renewed by Jarl, [certificates.md](certificates.md)),
|
||||
named's `tls` statement and listeners on the addresses you choose, and the
|
||||
host name's address records in your private views.
|
||||
|
||||
In the web UI: **Encrypted DNS** (administrators). In the CLI: `jarl
|
||||
encrypted-dns`. In the API: `/api/v1/encrypted-dns`.
|
||||
|
||||
## Setting it up
|
||||
|
||||
```sh
|
||||
jarl encrypted-dns setup --hostname dns.adbraun.lol --address 10.0.10.5 --address 2a02:8013:7f3d:10::5
|
||||
```
|
||||
|
||||
This:
|
||||
|
||||
1. **finds or obtains the certificate**: one Jarl manages whose names cover
|
||||
the host name (`--certificate NAME` to pick one), or a new one named
|
||||
after the host name's first label, which Jarl obtains from the CA (the
|
||||
command waits). The certificate gets the `dns` use, so it is written
|
||||
where named can read it, and named rereads it on every renewal.
|
||||
2. **checks the ports**: nothing else may listen on the chosen addresses
|
||||
on port 853 or the DoH port. Jarl looks at the host's listening sockets
|
||||
(it binds nothing) and says what is in the way, e.g. `jarld (Jarl's web
|
||||
UI and API) listens on 10.0.10.5 port 443`, and which port is free
|
||||
instead (`--doh-port 8443`).
|
||||
3. **stages named's configuration**, as one change you can review:
|
||||
|
||||
```
|
||||
tls jarl-dns { cert-file ".../tls/dns/fullchain.pem"; key-file ".../tls/dns/privkey.pem"; protocols { TLSv1.2; TLSv1.3; }; };
|
||||
options {
|
||||
listen-on port 853 tls jarl-dns { 10.0.10.5; };
|
||||
listen-on port 443 tls jarl-dns http default { 10.0.10.5; };
|
||||
listen-on-v6 port 853 tls jarl-dns { 2a02:8013:7f3d:10::5; };
|
||||
...
|
||||
```
|
||||
|
||||
named listens on port 53 on every address only while there is no
|
||||
`listen-on` statement at all. The first one, for DoT, would end that, so
|
||||
when you have none the setup adds `listen-on { any; };` (and the same
|
||||
for IPv6) to keep plain DNS where it was.
|
||||
4. **adds the host name's A and AAAA records** with the chosen addresses,
|
||||
in the private views that have a zone for it (`--view` to choose). In a
|
||||
linked zone they are local to the view, so the name doesn't reach your
|
||||
external view.
|
||||
|
||||
Running it again replaces the earlier setup (other addresses, ports, DoT
|
||||
or DoH only with `--no-doh` / `--no-dot`). `jarl encrypted-dns disable`
|
||||
takes the listeners and the `tls` statement out; the certificate and the
|
||||
records stay.
|
||||
|
||||
| Option | Default | |
|
||||
|--|--|--|
|
||||
| `--hostname` | | The name clients connect to; the certificate is for it |
|
||||
| `--address` | | This server's addresses to listen on (repeat). Choose your own networks' addresses: a listener on a public address answers anyone who reaches it, like port 53 there |
|
||||
| `--certificate` | one for the host name, obtained if needed | A certificate Jarl manages |
|
||||
| `--no-dot`, `--no-doh` | both on | |
|
||||
| `--dot-port` | 853 | Clients expect 853 |
|
||||
| `--doh-port` | 443 | Any free port; clients take it in the URL |
|
||||
| `--view` | private views with the zone | Where the host name's records go |
|
||||
|
||||
In the web UI the same form checks the ports as you choose, offers the
|
||||
free port, and obtains the certificate before setting up.
|
||||
|
||||
## Which view clients get
|
||||
|
||||
A DoT or DoH client gets the view its address matches, as over plain DNS:
|
||||
your phone at home gets the internal view, with its records and blocking.
|
||||
Choosing a view by key (`match-clients { key ...; }`) doesn't work over
|
||||
DoT and DoH, as clients don't sign their queries.
|
||||
|
||||
## Clients
|
||||
|
||||
`jarl encrypted-dns` (and the page) shows what to give them:
|
||||
|
||||
```
|
||||
For DoT clients: dns.adbraun.lol
|
||||
For DoH clients: https://dns.adbraun.lol/dns-query
|
||||
```
|
||||
|
||||
| Client | Where |
|
||||
|--|--|
|
||||
| Android 9+ | Settings → Network → Private DNS → Private DNS provider hostname: `dns.adbraun.lol` (DoT) |
|
||||
| Linux (systemd-resolved) | `/etc/systemd/resolved.conf`: `DNS=10.0.10.5#dns.adbraun.lol` and `DNSOverTLS=yes` (or `opportunistic`, which falls back to plain DNS) |
|
||||
| Firefox | Settings → Privacy & Security → DNS over HTTPS → Increased protection → Custom: the DoH URL |
|
||||
| Chrome, Edge | Settings → Privacy and security → Security → Use secure DNS → custom: the DoH URL |
|
||||
| Windows 11 | Settings → Network → your connection → DNS server assignment → Edit: the server's address, DNS over HTTPS on (manual template), the DoH URL |
|
||||
|
||||
Test from a machine on your network:
|
||||
|
||||
```sh
|
||||
kdig +tls-ca +tls-host=dns.adbraun.lol @10.0.10.5 example.com
|
||||
dig +https @10.0.10.5 example.com
|
||||
curl -s -H 'accept: application/dns-json' 'https://dns.adbraun.lol/dns-query?name=example.com'
|
||||
```
|
||||
|
||||
**Android away from home.** Private DNS in hostname mode applies on every
|
||||
network. Away from home, your server can't be reached, and Android then
|
||||
resolves nothing at all ("Private DNS server cannot be accessed"). Use a
|
||||
VPN home (WireGuard) when you're out, or switch Private DNS to
|
||||
*Automatic* when you leave. Firefox's *Increased protection* and
|
||||
systemd-resolved's `opportunistic` fall back to normal DNS by themselves.
|
||||
|
||||
Clients check the certificate against the host name, so the name must
|
||||
resolve to this server for them: the setup adds it to your private views.
|
||||
Open the ports in the server's firewall for your own networks only.
|
||||
|
||||
## What Jarl checks
|
||||
|
||||
* A `tls` statement whose files don't exist is an error
|
||||
(`tls_file_missing`): named won't load its configuration without them.
|
||||
* A certificate named uses can't be deleted, nor lose its `dns` use: turn
|
||||
encrypted DNS off first.
|
||||
* Listeners you wrote yourself are left alone; the page notes them.
|
||||
|
||||
## API
|
||||
|
||||
| Method | Path | Body |
|
||||
|--------|------|------|
|
||||
| GET | `/encrypted-dns` | `enabled`, `certificate`, `hostname`, `dot` and `doh` (address and port), `dot_host`, `doh_url`, `listeners`; for the form: `addresses` (this server's, with `private`), `views` (with `public`, and `has_zone` for `?hostname=`), `certificates` |
|
||||
| POST | `/encrypted-dns/check` | `{"addresses", "dot", "doh", "dot_port", "doh_port"}`: `conflicts` (address, port, what) and `free_doh_port` |
|
||||
| POST | `/encrypted-dns` | `{"hostname", "certificate", "addresses", "dot", "doh", "dot_port", "doh_port", "views"}`: staged or applied like any change; 409 while the certificate hasn't been obtained |
|
||||
| DELETE | `/encrypted-dns` | Turns it off |
|
||||
Reference in new issue
Block a user