Posts tagged subzone

DNS: subzone delegation for a subdomain for a dynamic ip

posted on 2015-05-01 01:16:37

As a sideproject I wanted a dynamical DNS, since seemingly all the free products out there all went out of business, started to charge money or have started having other bad habits like coercing you to periodically log into the service or your domain was turned off.

Since I already had a server plus a domain, an own DNS server was a nice idea. But changing the authorized nameserver for a domain leads to the need of having to update the settings of the domain at the registrar, which I did not want:

  • The primary DNS server for the main domain should stay with my hoster.

This is due to the server being a playground, and if something broke and the nameserver daemon would run on the server, the DNS would be out of order. Also I was kind of lazy to get my hoster to change the settings, and where would the fun be in the easy way anyway?

After looking around some time, I found out about subzone delegation, which needs some additional RR's/resource records in the config of the main domain, but no changes to the DNS Server which is authorative for it. Ain't that an idea? Just exactly what I needed.

So here a little howto on how to implement this, on an external CentOS server with a fixed IP, a Fritzbox router where a raspberry pi is behind, and a hosted domain at an ISP / Internet Service Provider. The raspberry is running a raspbian install as an OS / Operating System. Strictly speaking, the raspberry is not really necessary, but better 'for reasons'.

Example values in the following are:

  • for the domain, pointing to
  • is the subzone, which will serve the dynamically changing ip.
  • the authorative nameserver for the main domain is at
  •, where the main domain is pointed at, will also be the secondary DNS server which serves the dynamic domain as subzone of the main domain.
  • the mailaddress which is usually used, is called email@domain.tld

The dynamic ip will be denoted as 999.999.999.999 in the following.

change RR's of main domain at your hoster

Add these two:             IN NS          IN A

Don't forget to increment the serial number (the first in the list of numbers after the line of the SOA definition), else your setting will not become public!

If you are lucky, you can add these two lines in a web interface that your domain hoster provides, else you have to tell the guys over there to change the settings for you.

install dns server on remote machine

On CentOS the bind9 dns server is referred as named, 'name daemon`.

yum install named -y

domain configuration of subdomain, on remote CentOS server


; public zone master file
$TTL 1800
; provides minimal public visibility of external services  IN      SOA email.domain.tld. (
                              2015042800    ; se = serial number
                              10800         ; ref = refresh
                              1800          ; ret = update retry
                              604800        ; ex = expiry
                              1800          ; min = minimum
                              )        IN NS
;; next line is then domain name of the name server for
;; it also should have a FQDN, so you don't just pass the IP, but do a second A RR to the NS RR        IN NS     IN  A IN  A        IN  A       999.999.999.999

Also, doing changes here, you have to increment the serial as well so that the changes become known. Usually this number is YYYYMMDDSS if I remember correctly. Does not matter, it just has to be bigger after each change you do to it.

On the values of the other numbers following the serial, I don't exactly know what they do. I just remember someone telling me that the RIPE would not be amused if TTL's are lower than 1800s (0,5h), so all these are bigger than that.

At the line defining the SOA RR / start of authority resource record the second

Never forget the dots at the end of domain names when specifying the absolute names (not just the string of hot the subdomain is called), these denote the end of the current domain name. Else the current domain name will be appended and you will have some fun time figuring out why things do not work. NOT.

bind configuration for the subzone name server

On the external server, make sure bind listens on public interface for DNS requests, so bind the listen port also to the NIC with the external ip:


options {
        listen-on port 53 {;; };


Also add the zone entry for your subzone:

zone "" IN {
        type master;
        file "";
        allow-query { any; };

On a sidenote, the dnsroot folder is at /var/named/, so you just pass the file or folders above to the file directive in the config, as shown above.

Enable logging:

logging {
        channel default_debug {
                file "data/";
                severity dynamic;

And also create the file if it does not exist:

touch /var/named/data/
chown -R named.named /var/named/data

With this configuration in place and a restart of the server, you already should be able to dig / nslookup / host dig, the 'domain information groper' is the nicest one since it provides the most output, as long as you understand what you are doing. If you do, you know all this anyway.


  • Do you have a firewall in place?
  • Port 53 is open?
  • What does tail -f /var/named/data/ tell you, right when you connect?
  • If you have fail2ban, is your ip currently banned?
  • Try a tcpdump on the external server on the IF / interface which holds the external ip.
  • Have a look at the log where the dropped packets are logged on your system, if there's anything like that.

Try a debugging script like this: (chmod a+x on the file may help.)


## check if domains are globally available
## you can also ask the google DNS via @
## should print both ips, else you have something broken in your configuration...
## ... or it takes the internet time, to get to know your DNS, maximum 30 minutes due to TTL 1800
dig +short
echo dyn
dig +short

## check if domains are available via your domainhosters nameserver
## this should only serve the main domain
dig +short
echo dyn
dig +short

## check if domains are available via your own nameserver
## this should only serve your subdomain in our setup
echo OWN NS
dig +short
echo dyn
dig +short

If all works as expected, congratulations. Else good luck troubleshooting this.

What is still missing now is the configuration so that the DNS will updated once your dynamic ip changes.

update DNS for the dynamic ip once it has changed in theory

Every 24 hours I have a forced disconnect from my telecommunications company, thats when my home ip changes. On the router a scheduled reconnect can be set so this happens at a known time, I set it to 4am.

Now on the update of the DNS for the dynamic domain:
This has to be done from a machine which is behind your router, or from your router. Usually you do not have a router with a fully fledged operating system, or you do not want to open it up from the outside of your network due to security reasons, this is why this is done via the raspberry behind it.

The raspberry installation and network configuration here will be skipped, it is assumed that you have a working ssh client and server installed on it and your network works so you can access the internet from (and your external server) from it.

Via curl you can easily get the external ip your router currently has, another possibility is to get it somehow directly from your router. The former is just way easier and will be used in the following.

BIND nowadays has the nsupdate facility (since v8? v9?), which lets you update the DNS remotely. Doing it via shellscripts and SSH will not work as the zonefile will be locked. Running scripts as root via SUID will not work, as this is prohibited by the OS due to security reasons.

A workaround would be a compiled C binary wrapper for the bash script, but just because it works does not mean you have to use it. Stick with nsupdate.

dns update in actual practice

create keypair

On the machine from where you want to update the DNS, you have to create a keypair. Use a valid email, with a . instead of an @.

dnssec-keygen -a HMAC-SHA512 -b 512 -n USER email.domain.tld

make the key known to BIND

Put the public part onto your dns server, and integrate it into BIND. Easiest and cleanest this is done like this, after scp'ing the pubkey up onto your server and into /etc/named/:

Insert into /etc/named.conf:

include "/etc/named.keys.conf";

Create /etc/named.keys.conf, and insert:

key email.domain.tld {
    algorithm HMAC-MD5;
    secret "insert-last-two-random-part-from-your-generated-public-key-file-into-here";

You might try using another algorithm, as there are several others available. But I am not sure, if the setup will work then.

configure management rights for the new key on the nameserver

The key could be either given full access, which I did not need, so it was just given partial access:

/etc/named.conf, add to zone:

update-policy {
    grant email.domain.tld subdomain A;

The part after grant is the keyname. Highlevel it is:

grant <key> <type> <zone> <RR> [<RR>];

Restart the nameserver, even though this might be unnecessary, to be safe.

configure the update script, the helper file plus the cronjob on the updating host

There are two files needed:

  1. the acutual script, which is run through the cron job
  2. the dns statements which nsudpate will execute, located in a second file
  3. Plus, the cronjob, so stuff is actually run in the end.

On the raspberry, for simplified reasons this is done as the root user:

mkdir /root/bin
touch /root/bin/
chmod a+x /root/bin/
touch /root/bin/dns-update.statements

Contents dns-update.statements:

update delete A
update add 1800 A


DNS="$(dig +short)"
## next two lines used for testing
#echo $CURRENT
#echo $DNS
if [ "$CURRENT" == "$DNS" ]; then exit 0; 
    /bin/sed -i "s/\(update add 1800 A\).*/\1 $CURRENT/" /root/bin/dns-update.statements
    /usr/bin/nsupdate -k /etc/dns/Kemail.domain.tld.+157+26336.private -v /root/bin/dns-update.statements

When it's shown like this here, it should be obvious where you have to apply changes for your setup:

  • after the -k flag, where your private key's name has to be entered
  • generally where mydomain is in use

Take special care, so the sed command will work, remember to change the "statements" file, too.

Actual testing did take place through adding echo's in every branch of the if statement, and running it every 5 seconds via watch:

watch -n5 -d /root/bin/

That way I could identify errors easily. Once the update works, it will tell you then as the ip got changed. No need to restart or reload the BIND server.

If all is working as expected, remove the show from the statements file, we just needed it during testing.

Also add the cron in /etc/crontab:

*/15 * * * * root /root/bin/

Afterwards service cron restart and you should have an updated DNS tomorrow and the day after tomorrow. And the following ones, of course. :)

The cron job does the checking every 15 minutes, if the ip has changed. Usually it would suffice if the check was done and run when the router resets.

But what about power outages? Router resets because somebody had to use the power outlet for the vacuum cleaner?
Just kidding, but it actually makes sense to update this periodically.

For questions I can be reached via twitter, see link on top of the site.

This blog covers .csv, .htaccess, .pfx, .vmx, /etc/crypttab, /etc/network/interfaces, /etc/sudoers, /proc, 10.04, 14.04, 16.04, AS, ASA, ControlPanel, DS1054Z, GPT, HWR, Hyper-V, IPSEC, KVM, LSI, LVM, LXC, MBR, MTU, MegaCli, PHP, PKI, PS1, R, RAID, S.M.A.R.T., SNMP, SSD, SSL, TLS, TRIM, VEEAM, VMware, VServer, VirtualBox, Virtuozzo, XenServer, acpi, adaptec, algorithm, ansible, apache, apache2.4, apachebench, apple, applet, arcconf, arch, architecture, areca, arping, asa, asdm, autoconf, awk, backup, bandit, bar, bash, benchmarking, binding, bitrate, blackarmor, blockdev, blowfish, bochs, bond, bonding, booknotes, bootable, bsd, btrfs, buffer, c-states, cache, caching, ccl, centos, certificate, certtool, cgdisk, cheatsheet, chrome, chroot, cisco, clamav, cli, clp, clush, cluster, cmd, coleslaw, colorscheme, common lisp, configuration management, console, container, containers, controller, cron, cryptsetup, csync2, cu, cups, cygwin, d-states, database, date, db2, dcfldd, dcim, dd, debian, debug, debugger, debugging, decimal, desktop, df, dhclient, dhcp, diff, dig, display manager, dm-crypt, dmesg, dmidecode, dns, docker, dos, drivers, dtrace, dtrace4linux, du, dynamictracing, e2fsck, eBPF, ebook, efi, egrep, emacs, encoding, env, error, ess, esx, esxcli, esxi, ethtool, evil, expect, exportfs, factory reset, factory_reset, factoryreset, fail2ban, fakeroot, fbsd, fdisk, fedora, file, files, filesystem, find, fio, firewall, firmware, fish, flashrom, forensics, free, freebsd, freedos, fritzbox, fsck, fstrim, ftp, ftps, g-states, gentoo, ghostscript, git, git-filter-branch, gitbucket, github, gitolite, global, gnutls, gradle, grep, grml, grub, grub2, guacamole, hardware, haskell, hdd, hdparm, hellowor, hex, hexdump, history, howto, htop, htpasswd, http, httpd, https, i3, icmp, ifenslave, iftop, iis, imagemagick, imap, imaps, init, innoDB, innodb, inodes, intel, ioncube, ios, iostat, ip, iperf, iphone, ipmi, ipmitool, iproute2, ipsec, iptables, ipv6, irc, irssi, iw, iwconfig, iwlist, iwlwifi, jailbreak, jails, java, javascript, javaws, js, juniper, junit, kali, kde, kemp, kernel, keyremap, kill, kpartx, krypton, lacp, lamp, languages, ldap, ldapsearch, less, leviathan, liero, lightning, links, linux, linuxin3months, lisp, list, livedisk, lmctfy, loadbalancing, locale, log, logrotate, looback, loopback, losetup, lsblk, lsi, lsof, lsusb, lsyncd, luks, lvextend, lvm, lvm2, lvreduce, lxc, lxde, macbook, macro, magento, mailclient, mailing, mailq, make-jpkg, manpages, markdown, mbr, mdadm, megacli, micro sd, microsoft, minicom, mkfs, mktemp, mod_pagespeed, mod_proxy, modbus, modprobe, mount, mouse, movement, mpstat, multitasking, myISAM, mysql, mysql 5.7, mysql workbench, mysqlcheck, mysqldump, nagios, nas, nat, nc, netfilter, networking, nfs, nginx, nmap, nocaps, nodejs, numberingsystem, numbers, od, onyx, opcode-cache, openVZ, openlierox, openssl, openvpn, openvswitch, openwrt, oracle linux, org-mode, os, oscilloscope, overview, parallel, parameter expansion, parted, partitioning, passwd, patch, pct, pdf, performance, pfsense, php, php7, phpmyadmin, pi, pidgin, pidstat, pins, pkill, plasma, plesk, plugin, posix, postfix, postfixadmin, postgres, postgresql, poudriere, powershell, preview, profiling, prompt, proxmox, ps, puppet, pv, pveam, pvecm, pvesm, pvresize, python, python3, qemu, qemu-img, qm, qmrestore, quicklisp, quickshare, r, racktables, raid, raspberry pi, raspberrypi, raspbian, rbpi, rdp, redhat, redirect, registry, requirements, resize2fs, rewrite, rewrites, rhel, rigol, roccat, routing, rs0485, rs232, rsync, s-states, s_client, samba, sar, sata, sbcl, scite, scp, screen, scripting, seafile, seagate, security, sed, serial, serial port, setup, sftp, sg300, shell, shopware, shortcuts, showmount, signals, slattach, slip, slow-query-log, smbclient, snmpget, snmpwalk, software RAID, software raid, softwareraid, sophos, spacemacs, spam, specification, speedport, spi, sqlite, squid, ssd, ssh, ssh-add, sshd, ssl, stats, storage, strace, stronswan, su, submodules, subzone, sudo, sudoers, sup, swaks, swap, switch, switching, synaptics, synergy, sysfs, systemd, systemtap, tar, tcpdump, tcsh, tee, telnet, terminal, terminator, testdisk, testing, throughput, tmux, todo, tomcat, top, tput, trafficshaping, ttl, tuning, tunnel, tunneling, typo3, uboot, ubuntu, ubuntu 16.04, ubuntu16.04, udev, uefi, ulimit, uname, unetbootin, unit testing, upstart, uptime, usb, usbstick, utf8, utm, utm 220, ux305, vcs, vgchange, vim, vimdiff, virtualbox, virtualization, visual studio code, vlan, vmstat, vmware, vnc, vncviewer, voltage, vpn, vsphere, vzdump, w, w701, wakeonlan, wargames, web, webdav, weechat, wget, whois, wicd, wifi, windowmanager, windows, wine, wireshark, wpa, wpa_passphrase, wpa_supplicant, x11vnc, x2x, xfce, xfreerdp, xmodem, xterm, xxd, yum, zones, zsh

Unless otherwise credited all material Creative Commons License by sjas