define-simple slave-dir "/var/cache/bind"
define-simple dir-mode 2775
define-simple zone-file "%v/%z.zone"
+ define-simple soa-format increment
define-list views *
+ define-list sign-views {}
+ define-list signzone-command {
+ /usr/sbin/dnssec-signzone
+ -g
+ -S
+ -K/var/lib/bind/key
+ -d/var/lib/bind/ds
+ -s-3600 -e+176400
+ -N%q
+ -o%z
+ -f%o
+ %f
+ }
+ define-simple auto-dnssec off
define-list reload-command {/usr/sbin/rndc reload %z IN %v}
+ define-list autosign-command {/usr/sbin/rndc sign %z IN %v}
define-list checkzone-command {
/usr/sbin/named-checkzone
- -i full
- -k fail
- -M fail
- -n fail
- -S fail
- -W fail
+ -ifull
+ -kfail
+ -Mfail
+ -nfail
+ -Sfail
+ -Wfail
%z
%f
}
define-simple max-zone-size [expr {512*1024}]
define-list reconfig-command {/usr/sbin/rndc reconfig}
- define scope {body} { preserving-config ZONECFG { uplevel 1 $body } }
+ define scope {body} {
+ preserving-config ZONECFG { uplevel 1 $body }
+ }
define zone {name {body {}}} {
global ZONES
}
}
- ## Main dispatch for zone categorization.
- switch -exact -- $zone(config-type) {
- master {
- switch -exact -- $zone(type) {
- static {
- set zone(file-name) \
- [file join $zone(master-dir) \
- [zone-file-name $zone(mapped-view) $config]]
- }
- dynamic {
- set zone(file-name) [file join $zone(slave-dir) \
- [zone-file-name $view $config]]
- }
- }
+ ## Work out the file names.
+ switch -glob -- $zone(config-type):$zone(type) {
+ master:static {
+ set dir $zone(master-dir)
+ set nameview $zone(mapped-view)
}
- slave {
- set zone(file-name) [file join $zone(slave-dir) \
- [zone-file-name $view $config]]
+ default {
+ set dir $zone(slave-dir)
+ set nameview $view
+ }
+ }
+ set zone(file-name) [file join $dir \
+ [zone-file-name $nameview $config]]
+
+ ## Find out whether this zone wants signing.
+ set zone(sign) false
+ switch -glob -- $zone(config-type):$zone(type) {
+ master:static {
+ foreach sview $zone(sign-views) {
+ if {[string match $zone(mapped-view) $sview]} { set zone(sign) true }
+ }
}
}
+ if {$zone(sign)} {
+ set zone(server-file-name) "$zone(file-name).sig"
+ } else {
+ set zone(server-file-name) $zone(file-name)
+ }
## Done.
return [array get zone]
puts $chan "${prefix}};"
}
+proc sign-zone-file {info input soafmt} {
+ ## Sign the zone described by INFO. The input zone file is INPUT; the SOA
+ ## should be updated according to SOAFMT.
+
+ global QUIS
+
+ array set zone $info
+ set cmd [build-command $zone(signzone-command) \
+ "%z" $zone(name) \
+ "%f" $zone(file-name) \
+ "%o" $zone(server-file-name) \
+ "%q" $soafmt]
+ set rc [catch {
+ set out [eval exec -ignorestderr $cmd]
+ } msg]
+ if {$rc} { set out $msg }
+ set out "| [string map [list "\n" "\n| "] $out]"
+ set ident "zone `$zone(name)' in view `$zone(mapped-view)'"
+ if {$rc} {
+ puts stderr "$QUIS: signing zone $ident failed..."
+ puts stderr $out
+ return false
+ } else {
+ puts "$QUIS: signing zone $ident output..."
+ puts $out
+ return true
+ }
+}
+
proc write-zone-stanza {view chan config} {
## Write a `zone' stanza to CHAN for the zone described by the CONFIG
## plist in the given VIEW.
switch -glob -- $zone(config-type) {
master {
puts $chan "\ttype master;"
- puts $chan "\tfile \"$zone(file-name)\";"
+ puts $chan "\tfile \"$zone(server-file-name)\";"
switch -exact -- $zone(type) {
dynamic { write-ddns-update-policy "\t" $chan $config }
}
foreach host $zone(masters) { lappend masters [host-addr $host] }
puts $chan "\tmasters { [join $masters {; }]; };"
puts $chan "\tfile \"$zone(file-name)\";"
+ if {![string equal $zone(auto-dnssec) off]} {
+ puts $chan "\tauto-dnssec $zone(auto-dnssec);"
+ }
switch -exact -- $zone(type) {
dynamic { puts $chan "\tallow-update-forwarding { any; };" }
}
} {
global ZONECFG ZONES CONFFILE
+ ## Read the configuration.
confspc-eval toplevel [list source $CONFFILE]
+
+ ## Safely update the files.
set win false
unwind-protect {
+
+ ## Work through each server view.
foreach view $ZONECFG(all-views) {
+
+ ## Open an output file.
set out($view) [output-file-name $view]
set chan($view) [open "$out($view).new" w]
+
+ ## Write a header.
set now [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S"]
puts $chan($view) "### -*-conf-javaprop-*-"
puts $chan($view) "### Generated at $now: do not edit"
+
+ ## Now print a stanza for each zone in the view.
foreach zone $ZONES {
write-zone-stanza $view $chan($view) $zone
}
}
+
+ ## Done: don't delete the output.
set win true
} {
+
+ ## Close the open files.
foreach view $ZONECFG(all-views) { close $chan($view) }
+
+ ## If we succeeded, rename the output files into their proper places;
+ ## otherwise, delete them.
if {$win} {
foreach view $ZONECFG(all-views) {
file rename -force -- "$out($view).new" $out($view)
defcmd install {user view name} {
help-text "Install a new zone file.
-The file is for the given zone NAME and the \(user-side) VIEW. The file is
+The file is for the given zone NAME and \(user-side) VIEW. The file is
provided by the named USER"
} {
global QUIS ZONECFG ZONES CONFFILE errorInfo errorCode
+ ## Read the configuration.
confspc-eval toplevel [list source $CONFFILE]
+ ## Make sure there's a temporary directory.
file mkdir [file join $ZONECFG(master-dir) "tmp"]
+ ## Keep track of cleanup jobs.
set cleanup {}
unwind-protect {
+ ## Find out which server views are affected by this update.
set matchview {}
foreach iview $ZONECFG(all-views) {
foreach info $ZONES {
array unset zone
array set zone $matchinfo
+ ## Make a new temporary file to read the zone into.
set pid [pid]
for {set i 0} {$i < 1000} {incr i} {
set tmp [file join $ZONECFG(master-dir) "tmp" \
if {![info exists chan]} { error "failed to create temporary file" }
set cleanup [list file delete $tmp]
+ ## Read the zone data from standard input into the file.
set total 0
while {true} {
set stuff [read stdin 4096]
if {$rc} {
puts stderr "$QUIS: zone check failed..."
puts stderr $out
+ eval $cleanup
exit 1
} else {
puts "$QUIS: zone check output..."
puts $out
}
+ ## If the zone wants signing, better to do that now.
+ if {![sign-zone-file $matchinfo $tmp keep]} {
+ eval $cleanup
+ exit 2
+ }
+
+ ## All seems good: stash the file in the proper place and reload the
+ ## necessary server views.
file rename -force -- $tmp $zone(file-name)
set cleanup {}
foreach view $matchview {
}
}
+defcmd sign {} {
+ help-text "Sign DNSSEC zones."
+} {
+ global QUIS ZONECFG ZONES CONFFILE
+
+ set rc 0
+
+ ## Read the configuration.
+ confspc-eval toplevel [list source $CONFFILE]
+
+ ## Grind through all of the zones.
+ foreach iview $ZONECFG(all-views) {
+ foreach info $ZONES {
+ array unset zone
+ set compinfo [compute-zone-properties $iview $info]
+ array set zone $compinfo
+ if {![string equal $zone(config-type) master]} { continue }
+ if {[string equal $zone(type) static] && $zone(sign)} {
+ if {![sign-zone-file $compinfo $zone(file-name) $zone(soa-format)]} {
+ set rc 2
+ }
+ } elseif {[string equal $zone(type) dynamic] &&
+ ![string equal $zone(auto-dnssec) off]} {
+ set cmd [build-command $zone(autosign-command) \
+ "%z" $zone(name) \
+ "%v" $iview]
+ if {[catch { exec $cmd } msg]} {
+ puts stderr "$QUIS: failed to reload `$zone(name)'"
+ puts stderr "| [string map [list "\n" "\n| "] $msg]"
+ set rc 2
+ }
+ }
+ }
+ }
+ exit $rc
+}
+
###--------------------------------------------------------------------------
### Main program.