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>
78 lines
2.3 KiB
Go
78 lines
2.3 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"jarl/internal/model"
|
|
"jarl/internal/render"
|
|
"jarl/internal/validate"
|
|
)
|
|
|
|
// namedCheckTTL is how long the result of checking named's running
|
|
// configuration is kept when nothing changed.
|
|
const namedCheckTTL = 10 * time.Minute
|
|
|
|
// namedCheck caches the warnings of named-checkconf on the configuration
|
|
// named runs, keyed by what it depends on.
|
|
type namedCheck struct {
|
|
mu sync.Mutex
|
|
key string
|
|
at time.Time
|
|
problems []validate.Problem
|
|
}
|
|
|
|
// namedProblems runs named-checkconf on the configuration named runs
|
|
// (without loading zones, which is quick) and returns what it reports as
|
|
// problems. An apply shows these too, but they are named's own checks,
|
|
// which Jarl doesn't repeat ("both recursion no and allow-recursion
|
|
// active"): the dashboard lists them with Jarl's so they don't go unseen
|
|
// after the apply. The result is reused while the deployed files and
|
|
// named's main file are unchanged.
|
|
func (e *Engine) namedProblems(ctx context.Context, st *model.State, deployed map[string]*model.DeployedFile) []validate.Problem {
|
|
if e.tools == nil || e.cfg.SkipCheck || e.cfg.MainConfig == "" || len(deployed) == 0 {
|
|
return nil
|
|
}
|
|
key := ""
|
|
if fi, err := os.Stat(e.cfg.MainConfig); err == nil {
|
|
key = fi.ModTime().String()
|
|
}
|
|
if f := deployed[e.cfg.Paths.MainConfig()]; f != nil {
|
|
key += "|" + f.SHA256
|
|
}
|
|
c := &e.namedCheck
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if c.key == key && e.Now().Sub(c.at) < namedCheckTTL {
|
|
return c.problems
|
|
}
|
|
res, err := e.tools.CheckConfig(ctx, e.cfg.MainConfig, false)
|
|
if err != nil {
|
|
return nil // named-checkconf couldn't run; an apply reports that
|
|
}
|
|
out := render.Render(st, e.cfg.Paths, e.extras(ctx))
|
|
var ps []validate.Problem
|
|
for _, m := range res.Messages {
|
|
p := validate.Problem{Severity: validate.Warning, Code: "named_warning", View: m.View, Zone: m.Zone}
|
|
if m.Error {
|
|
p.Severity, p.Code = validate.Error, "named_error"
|
|
}
|
|
where := ""
|
|
if m.File != "" {
|
|
where = fmt.Sprintf(" (%s:%d)", m.File, m.Line)
|
|
}
|
|
if span, ok := out.Lookup(m.File, m.Line); ok {
|
|
p.ObjectType, p.ObjectID = span.ObjectType, span.ObjectID
|
|
where = " (" + span.Label + ", " + where[2:]
|
|
}
|
|
p.Message = "named-checkconf: " + strings.TrimSpace(m.Text) + where
|
|
ps = append(ps, p)
|
|
}
|
|
c.key, c.at, c.problems = key, e.Now(), ps
|
|
return ps
|
|
}
|