Give every view a TSIG key that selects it

Each view's match-clients starts with "key jarl-view-NAME" (after
"!key" for the views listed after it), so a query signed with a view's
key lands in that view from any address. The secrets are derived from a
random seed in Jarl's database. The delegation check signs its queries
to this server's own addresses with the key of the zone's view: before,
they got whichever view matched the server's address, usually internal.
jarl view key / GET /views/{id}/key show a key for dig -k (tsig:write,
audited). jarl-view- key names are reserved; the importer drops view
keys from a configuration Jarl wrote.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-11 18:19:10 +01:00
1 parent c696d02513
commit 384f6b3060
26 files changed
+463 -16

No files matched your search

+1
View File
@@ -386,6 +386,7 @@ Disabling a record that is already disabled is a 409; with `missing_ok=1`
| GET / PATCH / DELETE | `/views/{id}` | DELETE `?cascade=1` also deletes the view's zones |
| POST | `/views/order` | `{"ids": [3, 1, 2]}` |
| PUT | `/views/{id}/color` | `{"color": "green"}`; `""` for the default |
| GET | `/views/{id}/key` | `{name, algorithm, secret}`: the TSIG key that selects the view (docs/views.md); needs `tsig:write`, audited |
`adopt_zones` moves zones that are not in a view into the new view. named
requires every zone to be in a view as soon as one view exists.
+1
View File
@@ -368,6 +368,7 @@ jarl view order internal external
jarl view update external --match-clients "!homelab,any"
jarl view delete old [--cascade]
jarl view color internal green # web UI colour; "default" resets it
jarl view key external [--named-conf] # the key that selects the view (dig -k), docs/views.md
jarl acl create homelab 192.168.1.0/24 10.10.0.0/16 localhost
jarl acl update homelab --add 10.20.0.0/16 --remove 10.10.0.0/16
+1 -1
View File
@@ -71,7 +71,7 @@ showing them.
| `acl_ref_missing`, `key_ref_missing` | error | Every ACL or TSIG key that is referenced must exist |
| `acl_cycle` | error | ACLs that include each other |
| `acl_duplicate`, `acl_name_invalid`, `acl_empty`, `acl_shadowed` | error / warning | Unique names, built-in names reserved, elements that can never match |
| `key_*` | error / warning | Valid key names, a supported algorithm, a base64 secret of a sensible length. hmac-md5/sha1 are flagged as weak |
| `key_*` | error / warning | Valid key names, a supported algorithm, a base64 secret of a sensible length. hmac-md5/sha1 are flagged as weak. Names starting with `jarl-view-` are reserved for the view keys (`key_name_reserved`) |
| `view_duplicate`, `view_name_invalid`, `view_acl_invalid` | error | Valid, unique views and match lists |
| `view_unreachable` | warning | A view that comes after one matching every client is never used |
| `option_unknown`, `option_scope_invalid` | error | The option must exist in BIND 9.20 and be allowed where it is set (server, view, zone of this type, top level) |
+41 -2
View File
@@ -116,7 +116,44 @@ dig @<public-ip> example.com # from outside: status R
jarl check
```
On the server, `dig -b <source address>` picks which view you test.
On the server, `dig -b <source address>` picks which view you test, or,
from anywhere, sign the query with the view's key (below):
```sh
jarl view key external --named-conf > external.key
dig -k external.key @ns1.adbraun.lol nas.adbraun.lol +short # 203.0.113.20, even from the LAN
```
## View keys
named picks a view by where a query comes from, so a query this server
sends to its own public address usually lands in the internal view: its
source is one of your own networks. That makes checks of the external view
from the server itself (such as Jarl's delegation check) see the wrong
view.
So Jarl gives every view a TSIG key, `jarl-view-NAME`, and puts it at the
front of the view's `match-clients`:
```
view "internal" {
match-clients { !key jarl-view-external; key jarl-view-internal; homelab; };
...
view "external" {
match-clients { key jarl-view-external; any; };
```
A query signed with a view's key lands in that view, whatever its source;
the `!key` stops an earlier view from taking a later view's queries.
Clients don't send these keys, so for them nothing changes. Jarl signs its
own queries to this server's addresses with the key of the zone's view.
The keys live in `keys.conf` with your other keys, never in diffs or in
`jarl tsig list`; their secrets are derived from a random value in Jarl's
database (and in its backups), so they stay the same from one apply to
the next. `jarl view key VIEW`
shows one (it needs `tsig:write` and is audited). Key names starting with
`jarl-view-` are reserved, and the view keys can be used in your own
options (`allow-transfer { key jarl-view-external; }`).
## Living with views
@@ -130,7 +167,9 @@ On the server, `dig -b <source address>` picks which view you test.
`jarl view list` shows it.
* Deleting a view is refused while it still has zones, unless you pass
`--cascade`.
* Give each view a colour (Views → Edit → Colour, or
* Each view has its own page (Views → the view's name): its settings,
options and zones.
* Give each view a colour (the view's page → Settings → Colour, or
`jarl view color internal green`) to tell the zones of each view apart
in the zone list. The colour only affects the web UI, so it applies at
once, with no changeset or deploy. `jarl view color internal default`