milestone 20: declarative configuration for iac
This commit is contained in:
+98
-36
@@ -9,8 +9,13 @@ delete the directory.
|
||||
Follow the steps in order. Each one says what it did.
|
||||
|
||||
Every command below was executed on x86_64 Linux with Zig 0.16.0, Node.js
|
||||
24.14.1, dig 9.20.26 and curl 8.21.0. The only thing substituted during that run
|
||||
was the tutorial directory.
|
||||
24.14.1, dig 9.20.26 and curl 8.21.0. Steps 4 to 11, 13 and 14 were re-run end
|
||||
to end for this revision, and the transcripts are that run's output with the
|
||||
tutorial directory substituted. Two things were not re-run: the browser page in
|
||||
step 12 — its endpoints were exercised, the page itself was not opened — and the
|
||||
two build commands in steps 1 and 2, which had already produced the binary under
|
||||
test. The ZON block at the end of step 14 was checked with `nxdns check
|
||||
--config` rather than started.
|
||||
|
||||
## What you need
|
||||
|
||||
@@ -30,7 +35,7 @@ cd web && npm ci && npm run build && cd ..
|
||||
```
|
||||
|
||||
This produces `web/dist`. Do not skip it. A plain `zig build` embeds
|
||||
`web/dist-placeholder`, a one-page status stub, and you would reach step 10 and
|
||||
`web/dist-placeholder`, a one-page status stub, and you would reach step 12 and
|
||||
find no admin interface there.
|
||||
|
||||
## 2. Build nxdns
|
||||
@@ -75,6 +80,11 @@ itself, so a configuration missing either one is rejected. Whichever command
|
||||
reads the file says the same thing and stops the same way: `run`, `nxdns check`
|
||||
and `nxdns import` all print the problem and exit 2.
|
||||
|
||||
This tutorial loads the file into the database once and then runs nxdns against
|
||||
the database, which is what the packaged systemd unit does. There is a second
|
||||
way to run nxdns, where the file itself stays the configuration; step 14 shows
|
||||
what changes.
|
||||
|
||||
## 4. Check the configuration before starting
|
||||
|
||||
```sh
|
||||
@@ -92,33 +102,44 @@ answers. It exits 2 when it found something that has to be fixed and 0
|
||||
otherwise. It writes nothing and starts no listener, so you can run it as often
|
||||
as you like.
|
||||
|
||||
## 5. Start the server
|
||||
|
||||
In your first terminal:
|
||||
## 5. Load it into the database
|
||||
|
||||
```sh
|
||||
zig-out/bin/nxdns run --data-dir ~/nxdns-tutorial/data --config ~/nxdns-tutorial/config.zon
|
||||
zig-out/bin/nxdns import ~/nxdns-tutorial/config.zon --data-dir ~/nxdns-tutorial/data
|
||||
```
|
||||
|
||||
```
|
||||
info(migrations): config.db migrated from schema version 0 to 2
|
||||
info(config_bootstrap): seeded the database from '/home/you/nxdns-tutorial/config.zon'
|
||||
imported /home/you/nxdns-tutorial/config.zon
|
||||
```
|
||||
|
||||
The data directory did not exist; nxdns created it at mode 0700 along with
|
||||
`config.db`. From here the database holds the configuration, and you will change
|
||||
it through the API rather than by editing the file again.
|
||||
|
||||
## 6. Start the server
|
||||
|
||||
In your first terminal:
|
||||
|
||||
```sh
|
||||
zig-out/bin/nxdns run --data-dir ~/nxdns-tutorial/data
|
||||
```
|
||||
|
||||
```
|
||||
info(querylog_schema): created querylog database '/home/you/nxdns-tutorial/data/querylog.db'
|
||||
info(blocklist_manager): blocklist snapshot generation 1: 0 of 0 sources loaded, 199 bytes
|
||||
info(nxdns): authority: database
|
||||
info(nxdns): nxdns <version> serving on udp [::1]:15353 udp 127.0.0.1:15353 tcp [::1]:15353 tcp 127.0.0.1:15353; 1 upstream(s); blocklist generation 1
|
||||
info(web_server): web interface listening on 127.0.0.1:8080
|
||||
```
|
||||
|
||||
The data directory did not exist; nxdns created it at mode 0700 along with
|
||||
`config.db` and `querylog.db`. The line that matters is `seeded the database
|
||||
from`: the configuration file was read into the database this once. From now on
|
||||
the database is the truth and the file is ignored on every later start, which
|
||||
you will see for yourself in step 11. Configuration changes go through the API,
|
||||
the web interface, or `nxdns import`.
|
||||
No `--config` here, and that is the point: `authority: database` says the
|
||||
database is the configuration and no file was opened at all. The file you wrote
|
||||
in step 3 has done its job.
|
||||
|
||||
Leave this terminal running and switch to the second one.
|
||||
|
||||
## 6. Resolve a name
|
||||
## 7. Resolve a name
|
||||
|
||||
```sh
|
||||
dig @127.0.0.1 -p 15353 example.com A +noall +answer
|
||||
@@ -131,10 +152,10 @@ example.com. 90 IN A 104.20.23.154
|
||||
|
||||
nxdns had no answer cached, so it forwarded the query to
|
||||
`https://cloudflare-dns.com/dns-query` over HTTPS and returned what came back.
|
||||
You now have a working resolver. It blocks nothing yet: the log line in step 5
|
||||
You now have a working resolver. It blocks nothing yet: the log line in step 6
|
||||
said `0 of 0 sources loaded`.
|
||||
|
||||
## 7. Add a blocklist source
|
||||
## 8. Add a blocklist source
|
||||
|
||||
```sh
|
||||
curl -s -X POST http://127.0.0.1:8080/api/blocklists \
|
||||
@@ -152,7 +173,7 @@ That is fine for a localhost tutorial and wrong for anything else; see
|
||||
|
||||
Note the `"id":1` in the response. You need it in the next step.
|
||||
|
||||
## 8. Attach the source to the `default` group
|
||||
## 9. Attach the source to the `default` group
|
||||
|
||||
A blocklist source belongs to the installation. Which groups use it is a
|
||||
separate decision, which is what lets one group get a strict list and another
|
||||
@@ -190,7 +211,7 @@ curl -s -X PUT http://127.0.0.1:8080/api/groups/1/sources \
|
||||
{"source_ids":[1]}
|
||||
```
|
||||
|
||||
## 9. Download the list
|
||||
## 10. Download the list
|
||||
|
||||
Adding a source registers it; it does not fetch it. Ask for a refresh:
|
||||
|
||||
@@ -199,25 +220,25 @@ curl -s -X POST http://127.0.0.1:8080/api/blocklists/update
|
||||
```
|
||||
|
||||
```json
|
||||
{"sources":[{"id":1,"state":"ok","loaded":true,"last_attempt":1785683777,"last_success":1785683777,"url":"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts","last_error":"","domains":99277,"wildcards":0,"skipped_regex":0}]}
|
||||
{"sources":[{"id":1,"state":"ok","loaded":true,"last_attempt":1786473715,"last_success":1786473715,"url":"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts","last_error":"","domains":99559,"wildcards":0,"skipped_regex":0}]}
|
||||
```
|
||||
|
||||
The download is about 3 MB and takes a few seconds. Watch the first terminal
|
||||
until this appears:
|
||||
|
||||
```
|
||||
info(blocklist_manager): blocklist snapshot generation 5: 1 of 1 sources loaded, 3088703 bytes
|
||||
info(blocklist_manager): blocklist snapshot generation 6: 1 of 1 sources loaded, 3096006 bytes
|
||||
```
|
||||
|
||||
`1 of 1 sources loaded` is the line to wait for. nxdns builds each blocklist
|
||||
snapshot in full and swaps it in atomically, so queries keep being answered from
|
||||
the previous snapshot the whole time the new one is being built. After this, the
|
||||
domain count in the JSON above — 99277 on the day this was run — is live.
|
||||
domain count in the JSON above — 99559 on the day this was run — is live.
|
||||
|
||||
From here on, the list is on disk under `~/nxdns-tutorial/data/blocklists`.
|
||||
Restarting nxdns does not re-download it.
|
||||
|
||||
## 10. Watch a domain get blocked
|
||||
## 11. Watch a domain get blocked
|
||||
|
||||
```sh
|
||||
dig @127.0.0.1 -p 15353 doubleclick.net A +noall +answer
|
||||
@@ -249,7 +270,7 @@ yourself can, with a wildcard pattern such as `*.doubleclick.net`. So when you
|
||||
pick a domain to test, pick one that is literally in the file.
|
||||
`www.google-analytics.com` is another that is.
|
||||
|
||||
## 11. Open the web interface
|
||||
## 12. Open the web interface
|
||||
|
||||
Visit <http://127.0.0.1:8080> in a browser. This is the single-page application
|
||||
you built in step 1, served out of the binary. The dashboard shows query and
|
||||
@@ -257,7 +278,7 @@ block counts, and the Blocklists page shows the source you added with its
|
||||
domain count. (The endpoints behind those two pages were checked while writing
|
||||
this; the browser page itself was not opened on the verification host.)
|
||||
|
||||
## 12. Stop it
|
||||
## 13. Stop it
|
||||
|
||||
In the first terminal, press Ctrl-C.
|
||||
|
||||
@@ -267,21 +288,61 @@ info(nxdns): shutting down
|
||||
|
||||
nxdns catches SIGINT and SIGTERM, stops serving and exits 0.
|
||||
|
||||
Start it again with the same command as in step 5 and read the first log line:
|
||||
Start it again with the same command as in step 6 and read the first log lines:
|
||||
|
||||
```
|
||||
info(config_bootstrap): configuration file ignored; the database is already configured
|
||||
info(blocklist_manager): blocklist snapshot generation 1: 1 of 1 sources loaded, 3088703 bytes
|
||||
info(blocklist_manager): blocklist snapshot generation 1: 1 of 1 sources loaded, 3096006 bytes
|
||||
info(nxdns): authority: database
|
||||
```
|
||||
|
||||
The configuration file was not even opened, and the blocklist came off disk
|
||||
rather than the network. The blocklist source, the group it is attached to, and
|
||||
the query log all survived the restart because they live in
|
||||
`~/nxdns-tutorial/data`.
|
||||
The blocklist came off disk rather than the network. The source you added, the
|
||||
group it is attached to, and the query log all survived the restart because they
|
||||
live in `~/nxdns-tutorial/data`, which is what `authority: database` means in
|
||||
practice.
|
||||
|
||||
Press Ctrl-C again to stop this second process. Nothing is listening on 15353 or
|
||||
8080 now, and nothing of nxdns is running.
|
||||
|
||||
## 14. See what the other mode does
|
||||
|
||||
You have been running in database mode. The alternative is to hand the same file
|
||||
back as the configuration, which is what `--config` means:
|
||||
|
||||
```sh
|
||||
zig-out/bin/nxdns run --data-dir ~/nxdns-tutorial/data --config ~/nxdns-tutorial/config.zon
|
||||
```
|
||||
|
||||
```
|
||||
reconciled '/home/you/nxdns-tutorial/config.zon': sources +0 ~0 -1; group_sources +0 ~0 -1;
|
||||
info(blocklist_manager): blocklist snapshot generation 1: 0 of 0 sources loaded, 199 bytes
|
||||
info(nxdns): authority: file (/home/you/nxdns-tutorial/config.zon)
|
||||
```
|
||||
|
||||
**Read that first line.** The blocklist source is gone. That is not a bug — it is
|
||||
the whole contract. The file you wrote in step 3 never mentioned a blocklist
|
||||
source, and in file mode the file is the complete statement of what the
|
||||
configuration is, so anything the database holds that the file does not name is
|
||||
removed at every start. The reconcile said so in one line before doing it.
|
||||
|
||||
The compiled list is still on disk and the query log is untouched; what changed
|
||||
is the configuration, and it now matches the file exactly.
|
||||
|
||||
Neither mode is the "advanced" one. Database mode suits a box someone
|
||||
administers through the web interface. File mode suits a file kept in git and
|
||||
deployed by a tool, where the deployed file being what is running matters more
|
||||
than clicking. To have kept the blocklist here, you would put it in the file:
|
||||
|
||||
```zon
|
||||
.blocklist_sources = .{
|
||||
.{ .url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts", .name = "StevenBlack hosts" },
|
||||
},
|
||||
.group_sources = .{
|
||||
.{ .group = "default", .source_url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" },
|
||||
},
|
||||
```
|
||||
|
||||
Ctrl-C to stop it.
|
||||
|
||||
## What you have now
|
||||
|
||||
A resolver that answers real queries, a real blocklist of about 99000 domains
|
||||
@@ -292,9 +353,10 @@ directory you can delete:
|
||||
rm -rf ~/nxdns-tutorial
|
||||
```
|
||||
|
||||
You also saw the two things that surprise people most: the configuration file
|
||||
seeds the database once and is ignored afterwards, and a blocklist source does
|
||||
nothing until a group uses it.
|
||||
You also saw the three things that surprise people most: a blocklist source
|
||||
does nothing until a group uses it, which authority a start runs under is
|
||||
printed rather than guessed, and in file mode anything the file does not name is
|
||||
removed at the next start.
|
||||
|
||||
## Where to go next
|
||||
|
||||
@@ -305,4 +367,4 @@ nothing until a group uses it.
|
||||
- [reference/configuration.md](../reference/configuration.md) — every field you
|
||||
did not set.
|
||||
- [explanation/configuration-model.md](../explanation/configuration-model.md) —
|
||||
why the file seeds and the database rules.
|
||||
why there are two authority modes and what each one is for.
|
||||
|
||||
Reference in New Issue
Block a user