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.com
and10.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
dnscache
in 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.1
for queries (will be asked bydnscache
)dnscache
: listens on the external interface for queries and will forward to OpenDNS or127.0.0.1
depending 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.