zdns -- DNS zones creator helper
-This is very simple zsh-based helper functions to create DNS zones.
+This is very simple Tcl-based helper functions to create DNS zones.
Many things are hardcoded there. Basically you just write ordinary
-zsh script, sourcing the rc.zsh, containing various helper functions.
+Tcl script, sourcing the rc.tcl, containing various helper functions.
It expects DOMAIN variable to be set.
+Initially that utility was written on zsh, but later it was replaced
+with Tcl.
+
* fqdn(domain) -- prints fully-qualified domain name, taking either
"domain.", or "@", or "subdomain" names
-* shortened(domain) -- prints $DOMAIN-relative shortened name, printing
+* shorten(domain) -- prints $DOMAIN-relative shortened name, printing
only subdomain parts or "@"
-* zone_start(serial) -- prints SOA record with two predefined (hardcoded)
+* zone(serial) -- prints SOA record with two predefined (hardcoded)
nameservers and none DMARC policy
-* add_mx(domain) -- add predefined MX records for given domain, with
+* mx(domain) -- add predefined MX records for given domain, with
predefined redirect-based SPF policy
-* add_dane(domain) -- add DANE records for given domain. You have to
- have tls/ subdirectory, containing zeasypki's state
+* dane(domain, [port=443]) -- add DANE records for given domain. You
+ have to have tls/ subdirectory, containing zeasypki's state
(http://www.git.stargrave.org/?p=zeasypki.git;a=blob;f=README)
It looks in each CA's subdirectory if keypair exists for the domain,
printing necessary CAA and TLSA records
-* add_ssh(domain) -- searches for corresponding public key in ssh/
+* ssh(domain) -- searches for corresponding public key in ssh/
subdirectory and (if it exists) prints corresponding SSHFP record
-* add_subdomain(domain, addresses) -- adds specified domain with
- provided space-separated addresses. It automatically calls add_dane
- and add_ssh helpers. Unless $NOSPF=1 is specified, it prints "-all"
- SPF policy. If $Y=1 is specified, then it adds "y.domain" address with
- predefined $Y6 address and "-all" SPF policy
-* add_pgp(keyid, uid) -- prints _openpgpkey DANE record for given
- OpenPGP key of desired UID. "uid" is optional and useful only if your
- key have got multiple UIDs and you need to add only the single
- specified one
+* subdomain(domain, addrs, [y nospf dane:PORT]) -- adds specified domain
+ with provided space-separated addresses. It automatically calls dane
+ and ssh helpers. Third argument is a list containing three optional
+ elements. Unless "nospf" is specified, it prints "-all" SPF policy. If
+ "y" is specified, it adds "y.domain" address with predefined $Y6
+ address and "-all" SPF policy
+* pgp(keyid, uid) -- prints _openpgpkey DANE record for given OpenPGP
+ key of desired UID. "uid" is optional and useful only if your key have
+ got multiple UIDs and you need to add only the single specified one
-To omit burden of sourcing rc.zsh, setting $DOMAIN and rebuilding zones
+To omit burden of sourcing rc.tcl, setting $DOMAIN and rebuilding zones
after its change, there is default.zone.do redo (http://cr.yp.to/redo.html)
-target, expecting your script in $domain.zsh file.
+target, expecting your script in $domain.tcl file.
For example the zone for nncpgo.org domain with mail-capabilities, WWW
subdomain (available via Yggdrasil network), OpenPGP DANE key,
$ mkdir -p ssh
$ print ssh-ed25519 AAAA... > ssh/www.nncpgo.org
- $ cat > nncpgo.org.zsh <<EOF
- zone_start 2012011633
- add_mx @
- Y=1 add_subdomain www "$GW4 $GW6 $VPS4 $VPS6"
- Y=1 add_subdomain openpgpkey "$GW4 $GW6"
- add_pgp releases@nncpgo.org
+ $ cat > nncpgo.org.tcl <<EOF
+ zone 2012011633
+ mx @
+ subdomain www "$GW4 $GW6 $VPS4 $VPS6" y
+ subdomain openpgpkey "$GW4 $GW6" y
+ pgp releases@nncpgo.org
EOF
$ redo nncpgo.org.zone
_443._tcp.www TLSA 3 1 1 0a77...d187
www CAA 0 issue "cagost.cypherpunks.ru"
_443._tcp.www TLSA 3 1 1 9b98...7b3a
+ www SSHFP 4 2 e72b...c53f
www TXT "v=spf1 -all"
y.www AAAA 21a:af91:8d0e:b05:9645:e4e9:12be:3c39
y.www TXT "v=spf1 -all"
--- /dev/null
+zone 2012011623
+mx @
+subdomain @ "$GW4 $GW6 $VPS4 $VPS6" {y nospf}
+++ /dev/null
-zone_start 2012011622
-add_mx @
-Y=1 NOSPF=1 add_subdomain @ "$GW4 $GW6 $VPS4 $VPS6"
-export DOMAIN=$2
-src="rc.zsh $2.zsh"
-redo-ifchange $src
-cat $src | zsh -e
+redo-ifchange rc.tcl $2.tcl
+PATH="$HOME/work/zeasypki:$PATH" tclsh8.6 <<EOF
+set DOMAIN $2
+source rc.tcl
+source $2.tcl
+EOF
--- /dev/null
+zone 2012011634
+mx @
+subdomain www "$GW4 $GW6 $VPS4 $VPS6" y
+subdomain openpgpkey "$GW4 $GW6" y
+pgp releases@nncpgo.org
+++ /dev/null
-zone_start 2012011633
-add_mx @
-Y=1 add_subdomain www "$GW4 $GW6 $VPS4 $VPS6"
-Y=1 add_subdomain openpgpkey "$GW4 $GW6"
-add_pgp releases@nncpgo.org
--- /dev/null
+set GW4 91.211.5.21
+set GW6 2a03:e2c0:2663:1::1
+
+set VPS4 45.10.110.72
+set VPS6 2a04:ac00:a:146::25
+
+set Y6 21a:af91:8d0e:b05:9645:e4e9:12be:3c39
+
+set NS1 uz544mqwggqbf3z4utlhfqn45vpbpq78nc63hpg5u2ut29stkt0pkr.ns7.stargrave.org.
+set NS2 uz5nulnd504gp3s7sdmdl5l2gxc762hpw926t90k39ltxp67flbccn.ns5.stargrave.org.
+
+set NoSPF {"v=spf1 -all"}
+set ReSPF {"v=spf1 redirect=_spf.stargrave.org"}
+
+proc zone {serial} {
+ global DOMAIN NS1 NS2
+puts "\$TTL 21600
+\$ORIGIN $DOMAIN.
+$DOMAIN. 21600 IN SOA $NS1 admin.$DOMAIN. (
+ $serial ; Serial
+ 12h ; Refresh
+ 2h ; Retry
+ 2w ; Expire
+ 6h ; TTL
+)
+@ NS $NS1
+@ NS $NS2
+_dmarc TXT \"v=DMARC1; p=none\""
+}
+
+proc fqdn {dn} {
+ global DOMAIN
+ switch -glob -- $dn {
+ @ { return $DOMAIN. }
+ *. { return $dn }
+ * { return $dn.$DOMAIN. }
+ }
+}
+
+proc shorten {dn} {
+ set dn [fqdn $dn]
+ global DOMAIN
+ if { $dn == "$DOMAIN." } { return @ }
+ return [join [lrange [split $dn .] 0 end-[expr [llength [split $DOMAIN .]] + 1]] .]
+}
+
+proc mx {dn} {
+ set dn [shorten $dn]
+ global ReSPF
+ puts "$dn MX 10 mailfake0.stargrave.org."
+ puts "$dn MX 20 mail2.stargrave.org."
+ puts "$dn MX 30 mailfake1.stargrave.org."
+ puts "$dn TXT $ReSPF"
+}
+
+proc dane {dn {port ""}} {
+ set dn [fqdn $dn]
+ if {$port == ""} { set port 443 }
+ set dirname [string trimright $dn .]
+ set was [pwd]
+ cd tls
+ foreach ca [lsort [glob ca/*/*]] {
+ set ca [join [lrange [split $ca /] 1 end] /]
+ if {![file exists [file join ee $ca $dirname]]} { continue }
+ puts "[shorten $dn] CAA 0 issue \"[lindex [split $ca /] end]\""
+ puts "[shorten _$port._tcp.$dn] TLSA 3 1 1 [exec zeasypki dane ee/$ca/$dirname]"
+ }
+ cd $was
+}
+
+proc ssh {dn} {
+ set dn [fqdn $dn]
+ set fn ssh/[string trimright $dn .]
+ if {![file exists $fn]} { return }
+ set fd [open "| ssh-keygen -f $fn -r $dn"]
+ while {[gets $fd line] >= 0} {
+ if {[string first "SSHFP 4 2" $line] == -1} { continue }
+ puts "[shorten $dn] [lrange [split $line " "] 2 end]"
+ }
+ close $fd
+}
+
+proc subdomain {dn addrs {flags {}}} {
+ global Y6 NoSPF
+ set short [shorten $dn]
+ foreach addr $addrs {
+ if {[string first : $addr] == -1} { set atyp A } { set atyp AAAA }
+ puts "$short $atyp $addr"
+ }
+ dane $dn [lindex [split [lindex $flags [lsearch $flags dane:*]] :] end]
+ ssh $dn
+ if {[lsearch $flags nospf] == -1} { puts "$short TXT $NoSPF" }
+ if {[lsearch $flags y] != -1} {
+ set short [shorten y.[fqdn $dn]]
+ puts "$short AAAA $Y6"
+ puts "$short TXT $NoSPF"
+ ssh $short
+ }
+}
+
+proc pgp {keyid {uid ""}} {
+ if {$uid == ""} { set uid $keyid }
+ set fd [open "| gpg --export-options export-dane --export $keyid"]
+ while {[gets $fd line] >= 0} {
+ if {[string first $uid $line] != -1} { break }
+ }
+ puts $line
+ set line [split [gets $fd]]
+ set line [lreplace $line 0 0 "[lindex $line 0]._openpgpkey"]
+ puts [join $line]
+ while {[gets $fd line] >= 0} {
+ if {$line == ""} { break }
+ puts $line
+ }
+ catch {close $fd}
+}
+++ /dev/null
-path=(~/work/zeasypki $path)
-
-NoSPF='"v=spf1 -all"'
-ReSPF='"v=spf1 redirect=_spf.stargrave.org"'
-
-GW4=91.211.5.21
-GW6=2a03:e2c0:2663:1::1
-VPS4=45.10.110.72
-VPS6=2a04:ac00:a:146::25
-Y6=21a:af91:8d0e:b05:9645:e4e9:12be:3c39
-
-NS1=uz544mqwggqbf3z4utlhfqn45vpbpq78nc63hpg5u2ut29stkt0pkr.ns7.stargrave.org.
-NS2=uz5nulnd504gp3s7sdmdl5l2gxc762hpw926t90k39ltxp67flbccn.ns5.stargrave.org.
-
-zone_start() {
- local serial=$1
- cat <<EOF
-\$TTL 21600
-\$ORIGIN ${DOMAIN}.
-${DOMAIN}. 21600 IN SOA $NS1 admin.${DOMAIN}. (
- ${serial} ; Serial
- 12h ; Refresh
- 2h ; Retry
- 2w ; Expire
- 6h ; TTL
-)
-@ NS $NS1
-@ NS $NS2
-_dmarc TXT "v=DMARC1; p=none"
-EOF
-}
-
-fqdn() {
- case $1 in
- @) print ${DOMAIN}. ;;
- *.) print $1 ;;
- *) print $1.${DOMAIN}. ;;
- esac
-}
-
-shortened() {
- local dn=`fqdn $1`
- case $dn in
- ${DOMAIN}.) print @ ;;
- *) print ${dn%%.${DOMAIN}.} ;;
- esac
-}
-
-add_mx() {
- local shorten=`shortened $1`
- cat <<EOF
-$shorten MX 10 mailfake0.stargrave.org.
-$shorten MX 20 mail2.stargrave.org.
-$shorten MX 30 mailfake1.stargrave.org.
-$shorten TXT $ReSPF
-EOF
-}
-
-add_dane() {
- local domain=`fqdn $1`
- local dirname=${domain%%.}
- pushd tls
- for ca (ca/*/*(on)) {
- ca=(${(s#/#)ca})
- ca=${(j:/:)ca[2,-1]}
- [[ -d ee/$ca/$dirname ]] || continue
- print "`shortened $domain` CAA 0 issue \"${ca:t}\""
- print "`shortened _${DANE_PORT:-443}._tcp.$domain` TLSA 3 1 1 `zeasypki dane ee/$ca/$dirname`"
- }
- popd
-}
-
-add_ssh() {
- local domain=`fqdn $1`
- local fn=ssh/${domain%%.}
- [[ -r $fn ]] || return 0
- print "`shortened $1` SSHFP 4 2 `ssh-keygen -f $fn -r $domain |
- sed -n 's/^.*SSHFP 4 2 \(.*\)$/\1/p'`"
-}
-
-add_subdomain() {
- local shorten=`shortened $1`
- local atyp
- for addr (${=2}) {
- [[ $addr =~ : ]] && atyp=AAAA || atyp=A
- print "$shorten $atyp $addr"
- }
- add_dane $1
- [[ -n $NOSPF ]] || print "$shorten TXT $NoSPF"
- [[ -z $Y ]] || {
- shorten=$(shortened y.$(fqdn $1))
- print "$shorten AAAA $Y6"
- print "$shorten TXT $NoSPF"
- add_ssh $shorten
- }
- add_ssh $1
-}
-
-add_pgp() {
- local what=$2
- [[ -n "$what" ]] || what=$1
- what=${what:s/@/./}
- gpg --export-options export-dane --export $1 |
- perl -ne "next unless /${what}/../^$/ ; s/ TYPE61/._openpgpkey TYPE61/ ; print"
-}