milestone 13 discrepancies: redact credentials from urls in logs, metrics and cli output
This commit is contained in:
@@ -88,8 +88,8 @@ before writing to it.
|
||||
|
||||
## Restore over an existing database
|
||||
|
||||
`import` refuses a database that already has content, so a plain `import` can
|
||||
never clobber a configured server by accident:
|
||||
`import` refuses a database that already holds configuration, so a plain
|
||||
`import` can never clobber a configured server by accident:
|
||||
|
||||
```sh
|
||||
nxdns import /tmp/nxdns-lab/backup.zon --data-dir /tmp/nxdns-lab/data
|
||||
@@ -112,6 +112,13 @@ imported /tmp/nxdns-lab/backup.zon
|
||||
Stop the server first. `import` replaces the whole configuration underneath a
|
||||
process that has already read it, and a running server will not notice.
|
||||
|
||||
What `--force` does to the client list is worth knowing before you restore an
|
||||
old backup. A client the backup does not name is removed, and its first-seen
|
||||
and last-seen go with it — restoring a month-old file drops the devices you
|
||||
named since. Devices the server discovered from traffic are kept, and a device
|
||||
the backup does name keeps the first-seen and last-seen the database already
|
||||
held, so a restore does not restamp your whole network as newly arrived.
|
||||
|
||||
On a real install that is the systemd unit:
|
||||
|
||||
```sh
|
||||
@@ -185,8 +192,9 @@ The data directory layout is in
|
||||
## A backup before every upgrade
|
||||
|
||||
There is no downgrade path. Schema migrations run forward automatically at
|
||||
startup and before `check`, `export` and `import`; nothing walks them back. Take
|
||||
an export before installing a new binary — see [upgrade](upgrade.md).
|
||||
startup and before `export` and `import`; nothing walks them back, and `nxdns
|
||||
check` does not run them at all. Take an export before installing a new binary —
|
||||
see [upgrade](upgrade.md).
|
||||
|
||||
Every command on this page was executed on this host as written, except the
|
||||
`systemctl` block marked **Not verified on this host** above.
|
||||
|
||||
@@ -75,23 +75,40 @@ nxdns check --data-dir /tmp/nxdns-lab/data --config /tmp/nxdns-lab/etc/config.zo
|
||||
|
||||
```
|
||||
checking configuration file /tmp/nxdns-lab/etc/config.zon
|
||||
OK https://cloudflare-dns.com/dns-query
|
||||
OK upstreams[0] https://cloudflare-dns.com
|
||||
OK: no problems found
|
||||
```
|
||||
|
||||
`check` reads both endpoints' certificate and key. An unreadable file is a
|
||||
failure and exits 2:
|
||||
`check` loads both endpoints' certificate and key the same way the listeners do,
|
||||
so what passes here will start. An unreadable file is a failure and exits 2:
|
||||
|
||||
```
|
||||
FAIL doh_server.cert_path: '/tmp/nxdns-lab/etc/cert.pem' is not readable
|
||||
FAIL dot_server.cert_path: '/tmp/nxdns-lab/etc/cert.pem' is not readable
|
||||
FAIL doh_server.cert_path: '/tmp/nxdns-lab/etc/cert.pem': certificate file is not readable
|
||||
FAIL dot_server.cert_path: '/tmp/nxdns-lab/etc/cert.pem': certificate file is not readable
|
||||
```
|
||||
|
||||
A key readable by anyone but its owner is a warning, and does not change the
|
||||
exit code, because the service still starts:
|
||||
So is a key that does not belong to the certificate, which is the mistake worth
|
||||
catching before a restart — the two files are individually valid and only their
|
||||
pairing is wrong. mbedTLS writes its own line to stderr as it rejects the pair:
|
||||
|
||||
```
|
||||
warning(tls_server): mbedtls_pk_check_pair failed: RSA - Key failed to pass the validity check of the library (-16896)
|
||||
warning(tls_server): mbedtls_pk_check_pair failed: RSA - Key failed to pass the validity check of the library (-16896)
|
||||
checking configuration file /tmp/nxdns-lab/etc/mismatch.zon
|
||||
FAIL doh_server.key_path: '/tmp/nxdns-lab/etc/other.pem': private key does not belong to the certificate
|
||||
FAIL dot_server.key_path: '/tmp/nxdns-lab/etc/other.pem': private key does not belong to the certificate
|
||||
OK upstreams[0] https://cloudflare-dns.com
|
||||
```
|
||||
|
||||
A key readable by anyone but its owner is a warning instead. It does not change
|
||||
the exit code, because the service still starts, and the summary line counts it
|
||||
rather than claiming nothing was found:
|
||||
|
||||
```
|
||||
WARN doh_server.key_path: '/tmp/nxdns-lab/etc/key.pem' is mode 644; a TLS key must be readable by its owner only
|
||||
WARN dot_server.key_path: '/tmp/nxdns-lab/etc/key.pem' is mode 644; a TLS key must be readable by its owner only
|
||||
OK upstreams[0] https://cloudflare-dns.com
|
||||
OK: no failures found, 2 warnings
|
||||
```
|
||||
|
||||
## 4. Start and confirm the listeners
|
||||
|
||||
@@ -73,13 +73,28 @@ database has nothing to forward to. The log is
|
||||
`no configuration file at '/etc/nxdns/config.zon'; using the database as it is`
|
||||
followed by `nxdns run failed: NoUsableUpstreams`.
|
||||
|
||||
A file that is present but rejected is a different failure and a different exit
|
||||
A file that is present but rejected is a different failure with the same exit
|
||||
code. No `default` group, no enabled upstream, a syntax error — `run` prints the
|
||||
diagnostic and exits **1**, for example `nxdns run failed: MissingDefaultGroup`.
|
||||
Both were run here against this image: a seed file whose only group was named
|
||||
`other` exited 1, and an empty `/etc/nxdns` exited 2 with `NoUsableUpstreams`.
|
||||
Under `restart: unless-stopped` either one is a restart loop, so read the exit
|
||||
code from `docker inspect` to tell them apart; see
|
||||
diagnostic and exits 2 as well. Both were run here against this image. A seed
|
||||
file whose only group was named `other`:
|
||||
|
||||
```
|
||||
FAIL groups: no group named 'default'; every unknown client is assigned to it
|
||||
nxdns run failed: MissingDefaultGroup
|
||||
run `nxdns check` to see the configuration in full
|
||||
```
|
||||
|
||||
and an empty `/etc/nxdns`:
|
||||
|
||||
```
|
||||
info(config_bootstrap): no configuration file at '/etc/nxdns/config.zon'; using the database as it is
|
||||
nxdns run failed: NoUsableUpstreams
|
||||
run `nxdns check` to see the configuration in full
|
||||
```
|
||||
|
||||
Under `restart: unless-stopped` either one is a restart loop, and the exit code
|
||||
alone no longer tells them apart: read the lines above the failure, which either
|
||||
name the diagnostic in the file or say there was no file at all. See
|
||||
[Troubleshoot nxdns](troubleshoot.md).
|
||||
|
||||
The container runs as uid 65532, and the mount is read-only, so the container
|
||||
|
||||
@@ -152,7 +152,7 @@ A good file prints the source it checked, one `OK` line per upstream, and
|
||||
|
||||
```
|
||||
checking configuration file /etc/nxdns/config.zon
|
||||
OK https://cloudflare-dns.com/dns-query
|
||||
OK upstreams[0] https://cloudflare-dns.com
|
||||
OK: no problems found
|
||||
```
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ nxdns export --data-dir /tmp/nxdns-lab/data | grep password
|
||||
.password_hash = "$argon2id$v=19$m=19456,t=2,p=1$kvlRj1tdGul3MlfbvzLncLKWirNpJRJ3howFA9/ysgg$7elW7PPQ3WXHwI4YOmOpZ/1KNEQo7ZDLRJhnYOPMjqw",
|
||||
```
|
||||
|
||||
`--force` is required because the database already has content. See
|
||||
`--force` is required because the database already holds configuration. See
|
||||
[back up and restore](back-up-and-restore.md).
|
||||
|
||||
## What happens with no password set
|
||||
|
||||
+74
-29
@@ -21,8 +21,9 @@ nxdns run failed: NoUsableUpstreams
|
||||
run `nxdns check` to see the configuration in full
|
||||
```
|
||||
|
||||
Exit 2 is reserved for a small set of faults `run` raises itself:
|
||||
`NoUsableUpstreams`, `BadBindAddress`, `BadRateLimit` and `BadCertificate`.
|
||||
Exit 2 means the configuration is wrong and you can fix it. Every subcommand
|
||||
uses the same definition, so a file `run` exits 2 on exits 2 from `check` and
|
||||
`import` too.
|
||||
|
||||
**Diagnosis.**
|
||||
|
||||
@@ -45,18 +46,18 @@ checked on its first line.
|
||||
certificate. `run` names both paths before it exits:
|
||||
`doh_server: '<cert>' + '<key>': certificate file is not readable`.
|
||||
|
||||
**`check` does not catch most of this.** It tests only that each file is
|
||||
readable, and warns when the key is readable beyond its owner; it never opens
|
||||
the PEM. Parsing and the key/certificate pairing happen when `run` builds the
|
||||
TLS context, so `check` can print `OK: no problems found` on a configuration
|
||||
`run` then refuses. Reproduced here with a self-signed pair and the key from a
|
||||
second, unrelated pair:
|
||||
`check` catches this without starting a listener. It loads both PEM files and
|
||||
tests the key against the certificate through the same code `run` uses, so it
|
||||
fails on exactly what `run` would fail on. Reproduced here with a self-signed
|
||||
pair and the key from a second, unrelated pair:
|
||||
|
||||
```
|
||||
$ nxdns check --config config.zon
|
||||
warning(tls_server): mbedtls_pk_check_pair failed: RSA - Key failed to pass the validity check of the library (-16896)
|
||||
checking configuration file config.zon
|
||||
OK https://cloudflare-dns.com/dns-query
|
||||
OK: no problems found # exit 0
|
||||
FAIL doh_server.key_path: 'mismatched-key.pem': private key does not belong to the certificate
|
||||
OK upstreams[0] https://cloudflare-dns.com
|
||||
# exit 2
|
||||
|
||||
$ nxdns run --config config.zon --data-dir ./data
|
||||
warning(tls_server): mbedtls_pk_check_pair failed: RSA - Key failed to pass the validity check of the library (-16896)
|
||||
@@ -64,59 +65,103 @@ checked on its first line.
|
||||
nxdns run failed: BadCertificate # exit 2
|
||||
```
|
||||
|
||||
A cert file containing `not a certificate` behaves the same way — `check`
|
||||
exits 0, `run` exits 2 with `certificate PEM could not be parsed`. So a
|
||||
successful `check` means the paths and permissions are right, not that the
|
||||
certificate is usable; the only test of that is starting the service. Fix the
|
||||
path, the ownership, or the pair; see
|
||||
The `warning(tls_server)` line comes from mbedTLS on stderr and can appear
|
||||
before the `checking` line, which is on stdout. A cert file containing
|
||||
`not a certificate` fails the same way, with
|
||||
`FAIL doh_server.cert_path: 'junk.pem': certificate PEM could not be parsed`.
|
||||
An unreadable file reads
|
||||
`FAIL doh_server.cert_path: '<path>': certificate file is not readable`.
|
||||
|
||||
Fix the path, the ownership, or the pair; see
|
||||
[Enable DoH and DoT](enable-doh-and-dot.md).
|
||||
- `BadRateLimit` — a rate limit or window is zero. `import` refuses such a
|
||||
configuration, so this only reaches a database that was edited by hand.
|
||||
- `BadBindAddress` — `dns.bind_ipv4` or `dns.bind_ipv6` is not an address of
|
||||
that family.
|
||||
|
||||
## The service exits with code 1 on a seed file you just wrote
|
||||
## A seed file you just wrote is rejected
|
||||
|
||||
**Symptom.** A first start against an empty database prints the validation
|
||||
problem and stops, but with exit code 1, not 2:
|
||||
problem and stops with exit 2:
|
||||
|
||||
```
|
||||
groups: no group named 'default'; every unknown client is assigned to it
|
||||
FAIL groups: no group named 'default'; every unknown client is assigned to it
|
||||
nxdns run failed: MissingDefaultGroup
|
||||
run `nxdns check` to see the configuration in full
|
||||
```
|
||||
|
||||
A syntax error behaves the same way:
|
||||
|
||||
```
|
||||
config: 2:42: error: expected ',' after initializer
|
||||
FAIL config: 3:16: error: expected ',' after initializer
|
||||
nxdns run failed: ParseZon
|
||||
run `nxdns check` to see the configuration in full
|
||||
```
|
||||
|
||||
So does a seed file whose upstream list is empty or all disabled:
|
||||
|
||||
```
|
||||
upstreams: at least one upstream must be enabled
|
||||
FAIL upstreams: at least one upstream must be enabled
|
||||
nxdns run failed: NoUpstreams
|
||||
run `nxdns check` to see the configuration in full
|
||||
```
|
||||
|
||||
`NoUpstreams` from a seed file is not the same fault as `NoUsableUpstreams`
|
||||
above: the first is a file `run` refused, the second is a database `run`
|
||||
accepted and found empty. Only the second is exit 2.
|
||||
accepted and found empty. Both are exit 2.
|
||||
|
||||
**Diagnosis.** Run the same file through `check`, which reports it as a
|
||||
configuration problem and exits 2:
|
||||
**Diagnosis.** Run the same file through `check`, which reports the same
|
||||
problems and exits 2:
|
||||
|
||||
```sh
|
||||
nxdns check --config /etc/nxdns/config.zon
|
||||
```
|
||||
|
||||
**Fix.** Correct the file the diagnostics name and start again. The database is
|
||||
still empty after a failed seed, so the next start re-reads the file. Note that
|
||||
`nxdns check` and `nxdns import` of the same bad file exit 2 while `nxdns run`
|
||||
exits 1 — the exit code differs by command, the diagnostics do not. All three
|
||||
commands were run here against a file missing its `default` group, one with a
|
||||
syntax error and one with no enabled upstream, and every pair came out that
|
||||
way.
|
||||
still empty after a failed seed, so the next start re-reads the file. The exit
|
||||
code no longer depends on which command read the file: all three of these files
|
||||
were run through `run`, `check` and `import` here, and every one of the nine
|
||||
combinations exited 2 with the same diagnostic.
|
||||
|
||||
## `nxdns check` fails on a server that is running fine
|
||||
|
||||
**Symptom.** The service is up and answering, but `nxdns check` on the same
|
||||
machine exits 2 with one long line about a write-ahead log:
|
||||
|
||||
```
|
||||
checking database /var/lib/nxdns/config.db
|
||||
FAIL /var/lib/nxdns/config.db: uncheckpointed changes are waiting in /var/lib/nxdns/config.db-wal, and reading without writing would answer from the older settings in the main file; `nxdns run` applies them. A running nxdns normally holds this log, which is the usual reason to see this line.
|
||||
```
|
||||
|
||||
Nothing is damaged. `check` opens `config.db` immutable so that it can never
|
||||
write to it, and an immutable open ignores the write-ahead log. When that log
|
||||
holds bytes, the newest settings are in it and the main file holds older ones,
|
||||
so `check` refuses rather than grade stale values.
|
||||
|
||||
The log holds bytes after a configuration write that has not been checkpointed
|
||||
yet, which on a running server means someone changed something through the web
|
||||
interface or the API. A server that has only been answering queries has an empty
|
||||
`config.db-wal` and `check` reads it normally — so this line comes and goes, and
|
||||
its absence is not proof that nothing is running.
|
||||
|
||||
**Fix.** Check the exported configuration instead of the live file:
|
||||
|
||||
```sh
|
||||
nxdns export --data-dir /var/lib/nxdns --out /tmp/current.zon
|
||||
nxdns check --config /tmp/current.zon
|
||||
```
|
||||
|
||||
`export` opens the database read/write and does see the log, so it renders the
|
||||
settings that are actually in force. Stopping the service and checking again
|
||||
works too: a clean shutdown checkpoints the log away.
|
||||
|
||||
> Reproduced here on a scratch data directory rather than `/var/lib/nxdns` —
|
||||
> that path is the only substitution in the output above. nxdns was started on
|
||||
> unprivileged ports; `config.db-wal` was 0 bytes and `check` exited 0; one
|
||||
> `POST /api/blocklists` took it to 8272 bytes and `check` then printed the line
|
||||
> above and exited 2; `export` from the same live directory succeeded and its
|
||||
> output checked clean; and after a clean shutdown `check --data-dir` exited 0
|
||||
> again.
|
||||
|
||||
## Port 53 is already taken
|
||||
|
||||
|
||||
+44
-22
@@ -108,43 +108,63 @@ configuration.
|
||||
|
||||
```sh
|
||||
nxdns version
|
||||
nxdns check
|
||||
nxdns export --out /tmp/after-upgrade.zon
|
||||
nxdns check --config /tmp/after-upgrade.zon
|
||||
dig @127.0.0.1 example.com A +short
|
||||
```
|
||||
|
||||
`nxdns check` with no `--config` checks the database, which is what you want
|
||||
after an upgrade — it names its source on the first line and migrates a
|
||||
database that is one schema version behind before checking it:
|
||||
The restart in step 3 is what migrated the database, so by now the schema is
|
||||
current and the service is answering. Confirming with `nxdns check` alone would
|
||||
not work here, and the reason is worth knowing: `check` opens `config.db`
|
||||
immutable so it can never write to it, and the migration you just performed is
|
||||
sitting in `config.db-wal` waiting to be checkpointed. Rather than read around
|
||||
the log and grade older settings, `check` reports it:
|
||||
|
||||
```
|
||||
checking database /var/lib/nxdns/config.db
|
||||
OK https://cloudflare-dns.com/dns-query
|
||||
FAIL /var/lib/nxdns/config.db: uncheckpointed changes are waiting in /var/lib/nxdns/config.db-wal, and reading without writing would answer from the older settings in the main file; `nxdns run` applies them. A running nxdns normally holds this log, which is the usual reason to see this line.
|
||||
```
|
||||
|
||||
`export` opens the database read/write and does see the log, so exporting and
|
||||
then checking the export validates what is actually in force:
|
||||
|
||||
```
|
||||
checking configuration file /tmp/after-upgrade.zon
|
||||
OK upstreams[0] https://cloudflare-dns.com
|
||||
OK: no problems found
|
||||
```
|
||||
|
||||
> Verified on this host for the first two commands, with `--data-dir` pointing
|
||||
> at the scratch data directory instead of `/var/lib/nxdns`:
|
||||
> Verified on this host for the first three commands, with `--data-dir`
|
||||
> pointing at a scratch data directory instead of `/var/lib/nxdns` — that path
|
||||
> is the only difference from the blocks above:
|
||||
>
|
||||
> ```
|
||||
> $ nxdns version
|
||||
> nxdns 0.1.0-dev (unknown)
|
||||
> zig 0.16.0
|
||||
> $ nxdns check --data-dir $SCRATCH/data
|
||||
> checking database /…/scratchpad/data/config.db
|
||||
> OK https://cloudflare-dns.com/dns-query
|
||||
> OK: no problems found
|
||||
> ```
|
||||
>
|
||||
> The database path on the first line is the only difference from the block
|
||||
> above. The `dig` line was not run in this round: nothing is listening on
|
||||
> 127.0.0.1:53 here, and port 53 needs root.
|
||||
> Against a running server whose database had just been migrated and seeded,
|
||||
> `nxdns check --data-dir` printed the uncheckpointed-log line above and exited
|
||||
> 2, while `nxdns export --out` followed by `nxdns check --config` on the result
|
||||
> exited 0 with `OK: no problems found`. The `dig` line was not run in this
|
||||
> round: nothing is listening on 127.0.0.1:53 here, and port 53 needs root.
|
||||
|
||||
## What happens to the database
|
||||
|
||||
Migrations run at startup, and also before `check`, `export` and `import`, so
|
||||
whichever of those you run first performs the upgrade. A fresh database is
|
||||
created at the current schema version; an older one is stepped up to it. The
|
||||
log line names both versions:
|
||||
Migrations run at startup, and also before `export` and `import`, so whichever
|
||||
of those you run first performs the upgrade. `nxdns check` is the exception: it
|
||||
opens the database immutable and never migrates, so on a database still one
|
||||
version behind it reports the mismatch and exits 2 rather than fixing it:
|
||||
|
||||
```
|
||||
FAIL /var/lib/nxdns/config.db: schema version 0, this nxdns expects 2; `nxdns run` migrates it, `check` will not
|
||||
```
|
||||
|
||||
That line was reproduced here against a database stamped at version 0; the path
|
||||
and the version numbers are what vary.
|
||||
|
||||
A fresh database is created at the current schema version; an older one is
|
||||
stepped up to it. The log line names both versions:
|
||||
|
||||
```
|
||||
info(migrations): config.db migrated from schema version 0 to 2
|
||||
@@ -194,9 +214,11 @@ nxdns import config-backup.zon --force
|
||||
systemctl start nxdns
|
||||
```
|
||||
|
||||
`--force` is required here. A plain `import` into a database that already has
|
||||
content fails with `import failed: DatabaseNotEmpty` and exits 2, so it cannot
|
||||
clobber a configured server by accident.
|
||||
`--force` is required here. A plain `import` into a database that already holds
|
||||
configuration fails with `import failed: DatabaseNotEmpty` and exits 2, so it
|
||||
cannot clobber a configured server by accident. What counts is what an operator
|
||||
set: client rows the DNS path materialised from traffic never trigger the
|
||||
refusal on their own.
|
||||
|
||||
> Verified on this host for the two `nxdns` lines, against a populated scratch
|
||||
> data directory:
|
||||
|
||||
Reference in New Issue
Block a user