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:
1 parent
c696d02513
commit
384f6b3060
26 files changed
+463
-16
No files matched your search
@@ -0,0 +1,51 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"jarl/internal/model"
|
||||
)
|
||||
|
||||
func TestViewKeys(t *testing.T) {
|
||||
s := model.NewState()
|
||||
s.Views[1] = &model.View{ID: 1, Name: "internal", Position: 0, MatchClients: []string{"homelab", "localhost"}}
|
||||
s.Views[2] = &model.View{ID: 2, Name: "external", Position: 1}
|
||||
p := Paths{ConfigDir: "/etc/bind/jarl", ZoneDir: "/var/lib/bind/jarl", CacheDir: "/var/cache/bind/jarl"}
|
||||
seed := []byte("0123456789abcdef0123456789abcdef")
|
||||
files := func(x Extras) (conf, keys string) {
|
||||
for _, f := range Render(s, p, x).Files {
|
||||
switch f.Kind {
|
||||
case KindConfig:
|
||||
conf = string(f.Content)
|
||||
case KindKeys:
|
||||
keys = string(f.Content)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
conf, keys := files(Extras{ViewKeySeed: seed})
|
||||
for _, want := range []string{
|
||||
// An earlier view mustn't take a later view's key.
|
||||
"match-clients {\n\t\t!key jarl-view-external;\n\t\tkey jarl-view-internal;\n\t\thomelab;\n\t\tlocalhost;\n\t};",
|
||||
// A view without a list matched everyone: it still does.
|
||||
"match-clients { key jarl-view-external; any; };",
|
||||
} {
|
||||
if !strings.Contains(conf, want) {
|
||||
t.Errorf("named.conf lacks\n%s\n---\n%s", want, conf)
|
||||
}
|
||||
}
|
||||
sec := ViewKeySecret(seed, 2)
|
||||
if !strings.Contains(keys, "key \"jarl-view-external\" {\n\talgorithm hmac-sha256;\n\tsecret \""+sec+"\";\n};") || !strings.Contains(keys, "jarl-view-internal") {
|
||||
t.Errorf("keys.conf:\n%s", keys)
|
||||
}
|
||||
// The same seed gives the same secret; another seed doesn't.
|
||||
if ViewKeySecret(seed, 2) != sec || ViewKeySecret([]byte("other"), 2) == sec || ViewKeySecret(seed, 1) == sec {
|
||||
t.Error("secrets aren't derived from the seed and the view")
|
||||
}
|
||||
// Without a seed (or without views), nothing changes.
|
||||
conf, keys = files(Extras{})
|
||||
if strings.Contains(conf, "jarl-view-") || strings.Contains(keys, "jarl-view-") {
|
||||
t.Errorf("view keys without a seed:\n%s", conf)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user