Ubuntu VPS Switchover:
Ubuntu 10.04 Lucid Lynx LTS, daemontools, tinydns, axfrdns, tinydns-notify, CurvedDNS, iptables, and only one public IP address

Setting up a DNS server for my domains was one of the first things that needed to be done in the process of switching everything from one VPS to another.

My previous VPS was an OpenVZ one, and my temporary workaround on that was not elegant, as it was impossible to allow AXFR requests and DNSCurve requests on the same public IP. I use tinydns-notify to notify the backup nameservers of changes, as my backup DNS provider appears to use a version of BIND.

I opted for Xen mainly because of the greater control it allows, and the fact that I wanted to do some stuff with iptables that OpenVZ containers don't have the modules installed for on my VPS provider (nat and conntrack).

Because of the way I've set-up my DNS server, I'll make this warning right now: I am using a Xen VPS installed with the operating system Ubuntu Lucid Lynx 10.04 LTS. Iptables is installed, as is the iptables nat module. I am able to compile things without memory errors nor my VPS provider complaining about my VPS using too much CPU for ~10 minutes.

Theoretically, I can add DNSSEC support in the mix by using Phreebird, but since I'm not yet convinced of the need for DNSSEC and my registrar, 123-reg.co.uk, doesn't support DNSSEC yet, I haven't tested it.

With that out of the way, how I've set things up means that CurveDNS is used for all requests, excluding TCP requests from the IPs I allow AXFR requests from (those are handled by a separate copy of axfrdns).

  1. Before doing anything else, update the package index files & upgrade everything to the latest version.
    sudo apt-get update
    sudo apt-get upgrade
  2. Install daemontools.
    sudo apt-get install daemontools
  3. Edit the daemontools upstart script, so that it doesn't refuse to stop on reboot causing fscks and possible data corruption.
    sudo nano /etc/init/svscan.conf
    • Change:
      start on runlevel [123456]
      stop on shutdown
    • To:
      start on runlevel [2345]
      stop on runlevel [!2345]
  4. Install the djbdns package:
    sudo apt-get install djbdns
  5. Create the groups that will be needed:
    sudo addgroup --system dnslog
    sudo addgroup --system tinydns
    sudo addgroup --system axfrdns
    sudo addgroup --system curvedns
  6. Create the users that will be used:
    sudo adduser --no-create-home --disabled-login --shell /bin/false --system --ingroup dnslog dnslog
    sudo adduser --no-create-home --disabled-login --shell /bin/false --system --ingroup tinydns tinydns
    sudo adduser --no-create-home --disabled-login --shell /bin/false --system --ingroup axfrdns axfrdns
    sudo adduser --no-create-home --disabled-login --shell /bin/false --system --ingroup curvedns curvedns
  7. Create the directory everything will be installed under:
    sudo mkdir /var/lib/svscan
  8. Configure tinydns, so that it uses localhost 127.0.0.1 to listen on (or another IP in the 127.0.0.0/8 address block). Unlike the other programs, the port tinydns listens on cannot be changed, so use a loopback IP.
    sudo tinydns-conf tinydns dnslog /var/lib/svscan/tinydns 127.0.0.1
  9. Configure axfrdns, so that it uses the same loopback IP address to listen on as tinydns. This is because CurveDNS assumes the TARGET_IP is for both UDP and TCP.
    sudo axfrdns-conf axfrdns dnslog /var/lib/svscan/axfrdns 127.0.0.1
  10. Because axfrdns cannot chroot to the tinydns root directory, and env/ROOT is where the shared tinydns data file is located and where axfrdns thinks it should chroot to when using the -U option, it is probably best to leave axfrdns to run as root. Edit the axfrdns run file, so that it is run with the -U option (axfrdns-conf doesn't do this, so axfrdns would end up running as root):
    sudo nano /var/lib/svscan/axfrdns/run
    • Change:
      exec envuidgid axfrdns softlimit -d300000 tcpserver -vDRHl0 -x tcp.cdb -- "$IP" 53 /usr/bin/axfrdns
    • To:
      exec envuidgid axfrdns softlimit -d300000 tcpserver -U -vDRHl0 -x tcp.cdb -- "$IP" 53 /usr/bin/axfrdns
  11. Edit the axfrdns configuration file, so that requests over TCP are permitted.
    sudo nano /var/lib/svscan/axfrdns/tcp
    • Add the line:
      :allow,AXFR=""
  12. (Re)compile the tcp.cdb file:
    sudo make -C /var/lib/svscan/axfrdns/ -f /var/lib/svscan/axfrdns/Makefile
  13. Create an identical copy of /var/lib/svscan/axfrdns, which will be used for authorised zone transfers (CurveDNS does not pass on the source IP address to axfrdns/tinydns and AXFR requests don't work through it).
    sudo cp -ar /var/lib/svscan/axfrdns /var/lib/svscan/axfrdns-AXFR
  14. Change the port axfrdns(-AXFR) listens on (I'll use 8053):
    sudo nano /var/lib/svscan/axfrdns-AXFR/run
    • Change:
      exec envuidgid axfrdns softlimit -d300000 tcpserver -U -vDRHl0 -x tcp.cdb -- "$IP" 53 /usr/bin/axfrdns
    • To:
      exec envuidgid axfrdns softlimit -d300000 tcpserver -U -vDRHl0 -x tcp.cdb -- "$IP" 8053 /usr/bin/axfrdns
  15. Change the IP axfrdns(-AXFR) listens on to the public IP we'll be using:
    sudo echo 78.129.150.96 > /var/lib/svscan/axfrdns-AXFR/env/IP
  16. Create symbolic links in the directory svscan monitors:
    sudo ln -sf /var/lib/svscan/tinydns /etc/service
    sudo ln -sf /var/lib/svscan/axfrdns /etc/service
    sudo ln -sf /var/lib/svscan/axfrdns-AXFR /etc/service
  17. Start svscan:
    sudo initctl start svscan
    • If you get the following error:
      initctl: Job is already running: svscan
      then it is already running, and the following commands should be run instead.
      sudo svc -t /etc/service/*
      sudo svc -t /etc/service/*/log

At the moment, it is impossible for someone to query our DNS server for anything, unless they use TCP port 8053. Our current set-up is as follows:

  • tinydns listening on loopback IP 127.0.0.1 UDP port 53
  • axfrdns listening on loopback IP 127.0.0.1 TCP port 53
  • axfrdns(-AXFR) listening on public IP 78.129.150.96 TCP port 8053

Next, we'll install CurveDNS, which will add the following to our set-up:

  • curvedns listening on public IP 78.129.150.96 TCP/UDP port 53, forwarding requests to loopback IP 127.0.0.1 TCP/UDP port 53.

Finally, we'll set-up iptables, adding the following to our set-up:

  • TCP port 53 packets from the IPs we allow AXFR requests from redirected to TCP port 8053.
  • TCP port 53 packets from everyone else redirected to TCP port 8054.
  • UDP port 53 packets redirected to UDP port 8054.
  • DNS requests made by the server to it's own public IP and TCP port 53 DNAT'd to TCP port 8054.
  • DNS requests made by the server to it's own public IP and UDP port 53 DNAT'd to UDP port 8054.

To Be Continued