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,68 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
|
||||
"jarl/internal/model"
|
||||
"jarl/internal/nscheck"
|
||||
"jarl/internal/render"
|
||||
"jarl/internal/store"
|
||||
)
|
||||
|
||||
// viewKeySeedMeta is where the seed of the view keys is kept.
|
||||
const viewKeySeedMeta = "view_key_seed"
|
||||
|
||||
// ViewKeySeed returns the seed the view keys' secrets are derived from
|
||||
// (render/viewkeys.go), creating it the first time.
|
||||
func ViewKeySeed(ctx context.Context, st *store.Store) ([]byte, error) {
|
||||
v, err := st.GetMeta(ctx, viewKeySeedMeta)
|
||||
if err == nil {
|
||||
return base64.StdEncoding.DecodeString(v)
|
||||
}
|
||||
if !errors.Is(err, store.ErrNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
seed := make([]byte, 32)
|
||||
if _, err := rand.Read(seed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := st.SetMeta(ctx, viewKeySeedMeta, base64.StdEncoding.EncodeToString(seed)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return seed, nil
|
||||
}
|
||||
|
||||
// extras returns what jarld's configuration adds to the rendered
|
||||
// configuration, with the view keys' seed.
|
||||
func (e *Engine) extras(ctx context.Context) render.Extras {
|
||||
x := e.cfg.Extras
|
||||
e.seedMu.Lock()
|
||||
defer e.seedMu.Unlock()
|
||||
if e.seed == nil {
|
||||
seed, err := ViewKeySeed(ctx, e.store)
|
||||
if err != nil {
|
||||
e.log.Warn("view keys: reading their seed failed", "err", err)
|
||||
return x
|
||||
}
|
||||
e.seed = seed
|
||||
}
|
||||
x.ViewKeySeed = e.seed
|
||||
return x
|
||||
}
|
||||
|
||||
// ViewKey returns the TSIG key that selects a zone's view on this server
|
||||
// (nil without views).
|
||||
func (e *Engine) ViewKey(ctx context.Context, st *model.State, z *model.Zone) *nscheck.Key {
|
||||
v := st.Views[z.ViewID]
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
x := e.extras(ctx)
|
||||
if len(x.ViewKeySeed) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &nscheck.Key{Name: render.ViewKeyName(v.Name), Algorithm: render.ViewKeyAlgorithm, Secret: render.ViewKeySecret(x.ViewKeySeed, v.ID)}
|
||||
}
|
||||
Reference in new issue
Block a user