Add DNSSEC signing, key backups and Porkbun support
Zones get a DNSSEC setting: named signs them with BIND's default (or a custom) dnssec-policy and inline signing, with keys in a per-view key directory. Jarl shows the keys, the DS record and when it is safe to publish it, checks the parent's DS records (check-ds, which also tells named), and refuses steps that would break a signed domain: switching straight to unsigned, finishing a turn-off too early, deleting or moving a signed zone, TTLs above max-zone-ttl. Turning off goes through the insecure policy; wipe starts over once the parent has no DS. Backups store the DNSSEC keys and restore puts them back for named; the backup unit gets CAP_DAC_READ_SEARCH to read them. Jarl raises a zone's serial above named's whenever it writes an older one (after a restore). The serials check compares the signed serial. With a Porkbun API key in jarl.yaml, the registrar's name servers, glue records and DS records can be shown and changed, each change checked first (dry run, force to override). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
293e6ddd33
commit
5630e7a848
72 files changed
+5760
-153
No files matched your search
@@ -62,7 +62,8 @@ type Span struct {
|
||||
// Output is the result of rendering.
|
||||
type Output struct {
|
||||
Files []*File
|
||||
// Dirs are directories named needs to exist (for secondary zone files).
|
||||
// Dirs are directories named must be able to write to (secondary zone
|
||||
// files, DNSSEC key directories).
|
||||
Dirs []string
|
||||
// Spans maps file paths to source spans.
|
||||
Spans map[string][]Span
|
||||
@@ -103,6 +104,22 @@ func ViewDir(viewName string) string {
|
||||
return dnsname.FileSafe(dnsname.Fqdn(strings.ToLower(viewName)))
|
||||
}
|
||||
|
||||
// KeysDir is the directory under which named keeps the DNSSEC keys of the
|
||||
// zones Jarl signs, one subdirectory per view.
|
||||
func (p Paths) KeysDir() string { return filepath.Join(p.ZoneDir, "dnssec-keys") }
|
||||
|
||||
// KeyDir returns the key-directory of zone z: one per view, because named
|
||||
// refuses one directory for zones of the same name in two views. It is
|
||||
// named after the view's ID, which unlike its name never changes: a new
|
||||
// directory would make named create new keys, which the parent's DS
|
||||
// record wouldn't match.
|
||||
func KeyDir(s *model.State, z *model.Zone, p Paths) string {
|
||||
if z.ViewID == 0 {
|
||||
return filepath.Join(p.KeysDir(), "_default")
|
||||
}
|
||||
return filepath.Join(p.KeysDir(), fmt.Sprintf("view-%d", z.ViewID))
|
||||
}
|
||||
|
||||
// ZoneFilePath returns the file named loads for zone z, or "" for zone
|
||||
// types without files.
|
||||
func ZoneFilePath(s *model.State, z *model.Zone, p Paths) string {
|
||||
@@ -387,6 +404,14 @@ func renderZone(w *confWriter, s *model.State, z *model.Zone, p Paths, out *Outp
|
||||
}
|
||||
}
|
||||
}
|
||||
if z.Signed() {
|
||||
// named signs the zone itself (inline signing): it keeps the
|
||||
// signed copy next to Jarl's file (.signed) and its keys in dir.
|
||||
dir := KeyDir(s, z, p)
|
||||
w.write(fmt.Sprintf("%s\tdnssec-policy %s;\n", tabs, namedconf.QuoteString(z.DNSSECPolicy)))
|
||||
w.write(fmt.Sprintf("%s\tkey-directory %s;\n", tabs, namedconf.QuoteString(dir)))
|
||||
out.Dirs = appendUnique(out.Dirs, dir)
|
||||
}
|
||||
for _, o := range s.ScopedOptions(model.ScopeZone, z.ID) {
|
||||
w.begin("option", o.ID, label+" option "+o.Name)
|
||||
w.write(optionText(o, indent+1))
|
||||
|
||||
Reference in new issue
Block a user