djbdns on SmartOS
Posted by Dave Eddy on Apr 27 2015 - tags: techdjbdns is a software package for running a secure, fast, and simple DNS server.
djbdns is not actually a program itself, but instead is a collection of programs
that can be used together to create a full caching, forwarding, and authoritative
DNS system - this post will show how to setup all the necessary programs on
SmartOS to mimic my home setup.
I use djbdns at home to give me:
- DNS lookups for my internal network:
rapture.comand10.X.X.X - DNS caching daemon for quick look ups: it forwards to OpenDNS and caches the results
Installation
To install the suite of tools, run
pkgin in djbdns gmake
Note: the GNU version of make isn’t needed specifically, any implementation will suffice.
This will install a lot of programs, but the most important are:
tinydns: a DNS server daemondnscache: a recursive DNS caching daemon
The next step is to create the configuration directory which will be used later
mkdir -p /opt/local/etc/djbdns
Configure tinydns
tinydns is the actual DNS server program - it will read a config file with a list of
A records, NS Records, CNAMEs, etc. and provide answers to DNS queries. This program
will provide answers for rapture.com and 10.0.0.0/8 while silently discarding
all other queries.
The machine we are configuring is named ns1.rapture.com and it has the IP address
10.0.1.2.
First, we create the directory for tinydns configuration
mkdir -p /opt/local/etc/djbdns/tinydns/root
Note: all djbdns programs call chroot(2) for added security, so the root directory
will be the directory tinydns locks itself into.
Next, a DNS record file is needed for tinydns called data
cat > /opt/local/etc/djbdns/tinydns/root/data <<-EOF
# NS servers
.rapture.com:10.0.1.2:ns1.rapture.com
.1.0.10.in-addr.arpa:10.0.1.2:ns1.rapture.com
# Forward and Reverse lookups
=ns1.rapture.com:10.0.1.2
=foo.rapture.com:10.0.1.3
EOF
A Makefile is needed to build the database needed by tinydns to run
cat > /opt/local/etc/djbdns/tinydns/root/Makefile <<-EOF
data.cdb: data
tinydns-data
EOF
Run make to create the database named data.cdb
# cd /opt/local/etc/djbdns/tinydns/root
# make
tinydns-data
# ls
data data.cdb Makefile
Finally, start the tinydns server using this SMF manifest
<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type='manifest' name='export'>
<service name='network/tinydns' type='service' version='0'>
<create_default_instance enabled='true'/>
<dependency name='dep0' grouping='require_all' restart_on='error' type='service'>
<service_fmri value='svc:/milestone/multi-user:default'/>
</dependency>
<exec_method name='start' type='method' exec='tinydns &' timeout_seconds='10'>
<method_context working_directory='/opt/local/etc/djbdns/tinydns/root'>
<method_credential user='root' group='root'/>
<method_environment>
<envvar name='PATH' value='/opt/local/gnu/bin:/opt/local/gnu/sbin:/opt/local/bin:/opt/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/opt/custom/bin'/>
<envvar name='IP' value='127.0.0.1'/>
<envvar name='ROOT' value='/opt/local/etc/djbdns/tinydns/root'/>
<envvar name='UID' value='60001'/>
<envvar name='GID' value='60001'/>
</method_environment>
</method_context>
</exec_method>
<exec_method name='stop' type='method' exec=':kill' timeout_seconds='30'/>
<template>
<common_name>
<loctext xml:lang='C'>tinydns DNS server</loctext>
</common_name>
</template>
</service>
</service_bundle>
# svccfg import manifest.xml
# svcs -p tinydns
STATE STIME FMRI
online Apr_24 svc:/network/tinydns:default
Apr_24 3870 tinydns
This manifest tells tinydns to:
- Start as
root, and step down permissions to user60001(nobody) chroot(2)to its root directory- Listen on
localhost
We can test that server is working with:
# nslookup foo.rapture.com 127.0.0.1
Server: 127.0.0.1
Address: 127.0.0.1#53
Name: foo.rapture.com
Address: 10.0.1.3
And lastly, to update the database, you only need to edit the data file and run make,
the file will be picked up by tinydns automatically without a service restart.
Configure dnscache
dnscache is the recursive, forwarding, and caching server that will be used to
handle requests on the network for any domain name.
Like with tinydns, we create the directories for dnscache configuration
mkdir -p /opt/local/etc/djbdns/dnscache/root/{dev,etc,ip,proc,servers}
./dev and ./proc directory
These directories are mounted using LOFS for the chroot.
First, we add them to the /etc/vfstab file
echo '/dev - /opt/local/etc/djbdns/dnscache/root/dev lofs - yes -' >> /etc/vfstab
echo '/proc - /opt/local/etc/djbdns/dnscache/root/proc lofs - yes -' >> /etc/vfstab
And then mount them
mount /opt/local/etc/djbdns/dnscache/root/dev
mount /opt/local/etc/djbdns/dnscache/root/proc
./etc directory
This directory holds various configuration parameters for dnscache, create the files
needed with
cat > /opt/local/etc/djbdns/dnscache/root/etc/netconfig <<-EOF
tcp tpi_cots_ord v inet tcp /dev/tcp -
udp tpi_clts v inet udp /dev/udp -
EOF
and
dd if=/dev/urandom of=/opt/local/etc/djbdns/dnscache/root/etc/seed bs=128 count=1
chmod 0400 /opt/local/etc/djbdns/dnscache/root/etc/seed
./ip directory
This directory contains a series of empty files that tells dnscache what IP addresses
are allowed to query it. Since all of my machines are on the 10.x net, all that’s needed
is:
touch /opt/local/etc/djbdns/dnscache/root/ip/10
./servers directory
This directory contains files that each have a newline separated list of DNS
servers to use for certain requests. We need dnscache to send requests to
10.x and rapture.com to localhost (where tinydns is listening), and to
forward all others to OpenDNS. To do this, we need to create the following
files:
cat > /opt/local/etc/djbdns/dnscache/root/servers/rapture.com <<-EOF
127.0.0.1
EOF
cat > /opt/local/etc/djbdns/dnscache/root/servers/1.0.10.in-addr.arpa <<-EOF
127.0.0.1
EOF
cat > /opt/local/etc/djbdns/dnscache/root/servers/@ <<-EOF
208.67.222.222
208.67.220.220
EOF
./ directory
And finally, the root directory will hold a script simply called run which
will be used by SMF to start dnscache:
cat > /opt/local/etc/djbdns/dnscache/root/run <<-EOF
#!/usr/bin/env bash
exec > /dev/null
exec 2> /dev/null
exec < ./etc/seed
exec dnscache
EOF
chmod +x /opt/local/etc/djbdns/dnscache/root/run
Note: in this file I purposefully discard stdout and stderr - I prefer not to log any of the requests made by myself or guests on my network for privacy reasons.
Start the service
Finally, start the dnscache server using this SMF manifest
<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type='manifest' name='export'>
<service name='network/dnscache' type='service' version='0'>
<create_default_instance enabled='true'/>
<dependency name='dep0' grouping='require_all' restart_on='error' type='service'>
<service_fmri value='svc:/milestone/multi-user:default'/>
</dependency>
<exec_method name='start' type='method' exec='/opt/local/etc/djbdns/dnscache/root/run &' timeout_seconds='10'>
<method_context working_directory='/opt/local/etc/djbdns/dnscache/root'>
<method_credential user='root' group='root'/>
<method_environment>
<envvar name='PATH' value='/opt/local/gnu/bin:/opt/local/gnu/sbin:/opt/local/bin:/opt/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/opt/custom/bin'/>
<envvar name='IP' value='10.0.1.2'/>
<envvar name='ROOT' value='/opt/local/etc/djbdns/dnscache/root'/>
<envvar name='UID' value='60001'/>
<envvar name='GID' value='60001'/>
<envvar name='CACHESIZE' value='1000000'/>
<envvar name='DATALIMIT' value='3000000'/>
<envvar name='IPSEND' value='0.0.0.0'/>
<envvar name='FORWARDONLY' value='1'/>
</method_environment>
</method_context>
</exec_method>
<exec_method name='stop' type='method' exec=':kill' timeout_seconds='30'/>
<template>
<common_name>
<loctext xml:lang='C'>dnscache DNS server</loctext>
</common_name>
</template>
</service>
</service_bundle>
# svccfg import manifest.xml
# svcs -p dnscache
STATE STIME FMRI
online Apr_24 svc:/network/dnscache:default
Apr_24 3870 dnscache
This manifest tells dnscache to:
- Start as
root, and step down permissions to user60001(nobody) chroot(2)to its root directory- Listen on
10.0.1.2 - Forward requests only - this means OpenDNS will be queried by
dnscachein the event the answer is not already cached
We can test that server is working with:
# nslookup foo.rapture.com 10.0.1.2
Server: 10.0.1.2
Address: 10.0.1.2#53
Non-authoritative answer:
Name: foo.rapture.com
Address: 10.0.1.3
# nslookup daveeddy.com 10.0.1.2
Server: 10.0.1.2
Address: 10.0.1.2#53
Non-authoritative answer:
Name: daveeddy.com
Address: 165.225.136.227
Conclusion
tinydns: listens on127.0.0.1for queries (will be asked bydnscache)dnscache: listens on the external interface for queries and will forward to OpenDNS or127.0.0.1depending on domain/ip
Simply point your machines /etc/resolv.conf to 10.0.1.2 and you will have
blazing fast DNS on your network.
