This document describes how to create a small self-contained DNS service
suitable for testing dynamic DNS operations.

The idea is to run a local DNS server as a non-root user on a
non-standard port for testing purposes.  This configuration will not
forward requests and will not interact with regular system DNS
lookups.

With a little modification this service can act as a local DNS service
for development as well.  You would switch the port back to the
default (53), enable recursion, use the nameserver values in the
initial resolv.conf to set forwarders, and then replace the
resolv.conf with one which points to localhost.

## Install required software
# Install BIND daemon and tools
sudo yum install bind

## create a workspace for the daemon and control files
# Create a space to run the local service
mkdir ~/ddns

# Create a space for temporary files and logs
mkdir ~/ddns/tmp

# Copy the stock/default named configuration files
cd ~/ddns
sudo cp /etc/named.* .
sudo cp /var/named/named.{ca,empty,localhost,loopback} .
sudo chown `id -u`:`id -g` *

# comment IPv6 root servers (unless you have IPv6 configured)
perl -p  -i -e '/AAAA/ && s/^/;;/' named.*

## Enable secure updates
# generate update keys: may need enough randomness.  Log in and type stuff
dnssec-keygen -a HMAC-MD5 -b 512 -n USER example.com

# extract the key value
perl -n -e '/Key: / && s/Key: // && print' Kexample.com.*.private

-- example.com.key --
key example.com {
  algorithm HMAC-MD5;
  secret "H6NDDnTbNpcBrUM5c4BJtohyK2uuZ5Oi6jxg3ME+RJsNl5Wl2B87oL12 YxWUR3Gp7FdZQojTKBSfs5ZjghYxGw==";
};
--

# Create the test configuration file
# 
# This file is a limited configuration.  It runs on a non-standard
# high-numbered port.  It runs from a single directory and stores
# run-time files in a temporary directory so they can be cleaned up
# and repopulated easily

-- named.conf --
// named.conf

options {
	// listen-on port 53 { 127.0.0.1; }; // low port requires root
	listen-on port 10053 { 127.0.0.1; }; // avoid possible conflict
	directory 	".";                 // assume start from CWD
	allow-query     { localhost; };
        recursion no;

	pid-file "tmp/named.pid";
	session-keyfile "tmp/named.session.key";
        managed-keys-directory "tmp";

	// uncomment and replace the marker with a nameserver IP address
	// forward first ; forwarders { __NAMESERVER__ ; } ;
	// then replace nameserver line in /etc/resolv.conf with 127.0.0.1
};

// disable remote controls
controls {};

logging {
        channel default_debug {
                file "tmp/named.log";
                severity dynamic;
        };
};

// define the root zone
zone "." IN {
	type hint;
	file "named.ca";
};

// define standard loopback zones
// uses: named.localhost, named.loopback, named.empty
include "named.rfc1912.zones";

//
// Local customization
//

// load the update key
include "example.com.key";

zone "example.com" IN {
     type master;
     file "tmp/example.com.db";
     allow-update { key example.com ; };
}; 

zone "1.168.192.in-addr.arpa" {
     type master;
     file "tmp/1.168.192-rev.db";
     allow-update { key example.com ; } ;
};
--

== Zone files ==

Create the template zone files in the main directory.  You will copy them to
the tmp directory for test runs.  Changes to the running service will
cause changes to the zone files.

--- example.com.db ---
; initial data for testing DDNS using BIND
$ORIGIN .
$TTL 1	; 1 seconds (for testing only)
example.com		IN SOA	ns1.example.com. hostmaster.example.com. (
				2011112904 ; serial
				60         ; refresh (1 minute)
				15         ; retry (15 seconds)
				1800       ; expire (30 minutes)
				10         ; minimum (10 seconds)
				)
			NS	ns1.example.com.
			MX	10 mail.example.com.
$ORIGIN example.com.
mail			A	127.0.0.1
master			A	192.168.1.1
ns1			A	127.0.0.1
node                    A       192.168.1.10

; test records
testns1			TXT	"reserved namespace testns1"
;testns2		TXT	"to be added by tests"
testns3                 TXT     "reserved to add apps"
testns4                 TXT     "reserved to delete apps"
testapp4-testns4        CNAME   node.example.com.
---

--- 1.168.192-rev.db.init ---
$TTL 1 ; short for testing
$ORIGIN 1.168.192.IN-ADDR.ARPA.
@ 	1	IN	SOA	ns1.example.com. hostmaster.example.com. (
	2011112902 ; serial
	300 ; refresh
	15 ; retry
	1800 ; expire
	10 ; minimum
	)

	IN	NS	ns1.example.com.

1	IN	PTR     master.example.com.
---

== testing ==

# Go to the ddns working directory:

cd ~/ddns

# Clear the tmp directory: 
rm -f tmp/*

# copy the initial zone files
for FILE in  *.init ; do cp $FILE tmp/`basename $FILE .init` ; done

# start the named: log to stdout, no fork
/usr/sbin/named -c named.conf -g

# add an A record and try to retrieve it
nsupdate -y HMAC-MD5:example.com:`perl -n -e '/secret "([^"]+)"/ && print $1;' example.com.key` <<EOF
server localhost 10053
update add foo.example.com 1 A 192.168.1.2
send
EOF

# check the logs for the entry record
grep foo tmp/named.log

# check that the named returns the new record
dig -p 10053 @localhost foo.example.com

# stop the named
kill `cat tmp/named.pid`


== References ==

http://linux.yyz.us/dns/ - sample DDNS setup

== See Also ==

 named(8)
 named.conf(5)
 nsupdate(1)
 dig(1)
 host(1)

