Unbound Howto
This setup is build as an example to work as tier1 or tier2 server.
There is an update script which sets up all slave zones and does an tier1 anmd tiar2 server test after update.
The update script also checks dnssec basics and updates the files in git.
/etc/unbound is a git repository in my setup.
Base setup
- unbound.conf
server: verbosity: 1 log-queries: no port: 53 # ---------------------- # optimizations https://nlnetlabs.nl/documentation/unbound/howto-optimise/ # adjust for your needs # ---------------------- num-threads: 2 msg-cache-slabs: 2 rrset-cache-slabs: 2 infra-cache-slabs: 2 key-cache-slabs: 2 so-reuseport: yes key-cache-size: 8m # default 4m neg-cache-size: 2m # default 1m rrset-cache-size: 100m # rrset=msg*2 # default 4m msg-cache-size: 50m # default 4m # depends on number of cores: 1024/cores - 50 outgoing-range: 462 num-queries-per-thread: 231 # outgoing-range/2 so-rcvbuf: 4m so-sndbuf: 4m outgoing-num-tcp: 100 #default 10 incoming-num-tcp: 100 #default 10 stream-wait-size: 8m #default 4m # ---------------------- # can be set to 0 if you don't need # statistics-interval: 1200 # ---------------------- port: 53 interface: 0.0.0.0 interface: ::0 # # tls setup get ssl keys from letsencrypt # interface: 0.0.0.0@853 interface: ::0@853 tls-service-key: /etc/unbound/privkey.pem tls-service-pem: /etc/unbound/fullchain.pem # i don't like files :-) use-syslog: yes # ------------------------------------- # for the first start update files # named.cache.opennic and opennic.dnskey # manually # ------------------------------------- # drill . ns @161.97.219.84 > named.cache.opennic # dig -t DNSKEY . @161.97.219.84 | dnssec-dsfromkey -1 -f - . > opennic.dnskey # dig -t DNSKEY . @161.97.219.84 | dnssec-dsfromkey -2 -f - . > opennic.dnskey root-hints: "/etc/unbound/named.cache.opennic" trust-anchor-file: "/etc/unbound/opennic.dnskey" # -------------------------------------------------- # dnssec not working at the moment for all domains # -------------------------------------------------- harden-dnssec-stripped: no harden-glue: no aggressive-nsec: no # access control for everyone ai and ipv6 access-control: 0.0.0.0/0 allow access-control: ::0/0 allow # no identity needed hide-identity: yes identity: "pope.vatican.va" hide-version: yes version: "0.0" tls-system-cert: yes # DOS protection #ip-ratelimit-factor: 10 #ip-ratelimit: 60 ratelimit: 100 # --------------------------------------- # for start make am empty file # will be updated by refresh script # include: /etc/unbound/opennic_server.conf # # enable control via locahhost remote-control: control-enable: yes include: /etc/unbound/opennic_domains.conf
Refresh script
Gets the opennic root nameserver from the web
- getroot_opennic.pl
#!/usr/bin/perl use strict; use warnings; use XML::Parser; use Data::Dumper; my @bla = `wget --no-check-certificate -q -O - https://servers.opennic.org?tier=1`; my $done=0; my $res=""; while ( @bla ) { my $l = shift @bla; unless ($done) { if ( $l =~ /opennic\.glue/ ) { $done++; $res = $l; } } } $res =~ s/\<p\>//g; $res =~ s/\<\/p\>//g; $res =~ s/\<span\>//g; $res =~ s/\<\/span\>//g; $res =~ s/\<span\sclass=\'host\'[^\>]+\>//g; $res =~ s/\<a.+\>(.+)\<\/a\>//; $res =~ s/\<wbr\>/:/g; if ( $res =~ /\>(\d+\.\d+\.\d+\.\d+)\</ ) { print $1 . "\n"; } if ( $res =~ /\>([0-9A-Fa-f]+:\S+)\</ ) { print $1 . "\n"; }
Refreshes the files
- opennic.dnskey ( dnssec root key )
- named.cache.opennic ( dns root cache )
- opennic_server.conf ( allow unsecure dnssec queries for opennic domains )
- opennic_domains.conf ( opennic zones secondary )
The script locks for single usage and checks the serial of the root zone for changes.
- refresh_base.sh
#!/bin/bash # arch linux packets needed: # - ldns # - unbound # - gawk # - coreutils # - grep AWK=/usr/bin/awk CUT=/usr/bin/cut DIG="/usr/bin/drill -t -r /etc/unbound/named.cache.opennic" GREP=/bin/grep PRINTF=/usr/bin/printf SED=/bin/sed KF=opennic.dnskey CF=named.cache.opennic SF=opennic_server.conf DF=opennic_domains.conf MYIP='<please set me>' if [ "$MYIP" = "<please set me>" ] ; then echo "set varieble MYIP in this script" exit 1 fi cd `dirname $0` # Make sure only one copy runs at a time LOCK="refresh_base.lock" r=$($PRINTF %05d $RANDOM) sleep ${r:0:1}.${r:1:5} if [ -f $LOCK ]; then last_serial=$(cat $LOCK) dt=$((`date +%s` - `date -r $LOCK +%s`)) if [ $dt -lt 600 ]; then echo "Last run ${dt}s < 600s left" exit 0; else echo "Last run ${dt}s" fi fi touch $LOCK # first get any tier1 server to start NS=( $(./getroot_opennic.pl) ) echo -n "Opennic tier1 Server: " for ns in "${NS[@]}"; do echo -n "$ns " soa=$($DIG -Q SOA . @$ns) if [ "$soa" ]; then NS0=$(echo $soa | awk '{print $1}') ; break ; fi done if [ ! "$NS0" ]; then echo "No Opennic tier1 server could not be reached -- aborting!" >&2 exit 1 fi echo # get master server NS0=$(echo $NS0 | awk '{print $1}') echo -n "Master openic server: $NS0 (" # get ip from master server NS=( $(drill -Q @$ns ns0.opennic.glue.) ) for ns in "${NS[@]}"; do if [ "$soa" ]; then NS0=$ns ; break ; fi done if [ ! "$NS0" ]; then echo "could not be reached -- aborting!" >&2 exit 1 fi echo "$NS0)" soa=$($DIG -Q SOA . @$NS0) serial=$(echo $soa | awk '{ print $3}') refresh=$(echo $soa | awk '{ print $4}') echo "Serial: $serial" echo "Refresh: $refresh" echo $serial > $LOCK if [ -z "$last_serial" ] ; then last_serial=0 fi if [ $last_serial == $serial ]; then echo "No Update needed serial not changed" exit 0 fi dig . ns @${NS0} > $CF echo "Updated $CF" dig -t DNSKEY . @${NS0} | dnssec-dsfromkey -1 -f - . > $KF dig -t DNSKEY . @${NS0} | dnssec-dsfromkey -2 -f - . >> $KF cp $KF /etc/trusted-key.key echo "Updated $KF and /etc/trusted-key.key" # Start printing the new file ifs=$IFS # Collect list of TLDs TXT=(dns.opennic.glue $($DIG -Q @$NS0 TXT tlds.opennic.glue | tr -d '"')) IFS=$'\n' TLDS=($(sort <<<${TXT[*]})) IFS=$ifs if [ "${TLDS[*]}" == "dns.opennic.glue" ]; then echo "Failed to obtain list of TLDs" >&2 rm -f $LOCK exit 1 else echo "TLDS: ${TLDS[*]}" >&2 fi echo "#" > $DF echo "# OpenNIC zone config - file created by $HOSTNAME" >> $DF echo "# Generated on `date '+%A, %d %b %Y at %T'`" >> $DF echo "#" >> $DF echo "#" > $SF echo "# OpenNIC server config for opennic - file created by $HOSTNAME" >> $SF echo "# Generated on `date '+%A, %d %b %Y at %T'`" >> $SF echo "#" >> $SF for TLD in "${TLDS[@]}" ; do if [ $TLD != '.' ]; then echo -n 'domain-insecure: "' >> $SF echo -n $TLD >> $SF echo '"' >> $SF fi # Check if this zone is mastered by this server zone="$TLD.opennic.glue" if [ "$TLD" == "." ]; then zone="" ; fi master=($($DIG -Q TXT $zone. @$NS0 | sed 's/"//g' | $GREP ^ns) ns0.opennic.glue.) echo "TLD $TLD master = ${master[*]}" # Begin printing the zone config echo >> $DF echo "auth-zone:" >> $DF echo " name: $TLD" >> $DF if [ $TLD == '.' ]; then echo " zonefile: sec/root.zone" >> $DF else echo " zonefile: sec/${TLD}.zone" >> $DF fi echo " for-downstream: no" >> $DF # Collect a list of master nameservers for the zone for mm in "${master[@]}" ; do mm=$(echo $mm | $SED 's/\.$//') ns=$(echo $mm | $CUT -d. -f1 | $SED 's/ns//') AT="@$myDNS" if [ "$ns" == "0" ]; then AT="@$NS0"; fi if [ "$AT" == "@" ]; then AT=""; fi # If this is an unknown NS, query its IPs if [ ! "${RES[$ns]}" ]; then A4=$($DIG -Q A $mm $AT) A6=$($DIG -Q AAAA $mm $AT) RES[$ns]="${A4[@]} ${A6[@]}" fi read -a IP <<< ${RES[$ns]} # Print all IPs for all nameservers for addr in "${IP[@]}" ; do echo " master: $addr" >> $DF done done echo >> $DF done echo "Updated $DF $SF" unbound-control reload_keep_cache echo "Restarted unbound" sleep 10 wget -q --no-check-certificate -O test.txt "https://report.opennicproject.org/t2log/t1.php?ip_addr=$MYIP" if [ $(cat test.txt | perl -n -e 'if ( $p == 1 ) { /Passed/ && print "OK\n" ; $p = 0; } else { if ( /Test results:/ ) { $p=1; } }') != 'OK' ] ; then echo "Opennic Tier1 TEST FAILED" rm -f test.txt exit 1 else echo "Opennic Tier1 TEST OK" rm -f test.txt fi sleep 10 wget -q --no-check-certificate -O test.txt "https://servers.opennicproject.org/srvtest3/test.php?ip=$MYIP&ns=$MYIP" if [ $(cat test.txt | perl -n -e '/Server\sstatus(.*)$/ && print $1;' | perl -n -e '/\[(.+)\]/ && print $1;') != '100%' ] ; then echo "Opennic Tier2 TEST FAILED" rm -f test.txt exit 1 else echo "Opennic Tier2 TEST OK" rm -f test.txt fi if [ -z "$($DIG -TD . soa | grep '^\[T\]' | grep SOA)" ] ; then echo "DNSSEC Test FAILED" exit 1 else echo "DNSSEC Test OK" fi git commit -am "Serial: $serial"