guinix international

Computing Without Borders

 

guinix TechNote: "Dualling" Nameservers on OpenBSD

Configuring tinydns to serve both public and local networks from a single host

Introduction

Imagine you are ready to publish names for your own domain(s) for DNS queries coming in from the Internet. You have already read about Dan Bernstein's djbdns suite of nameserving tools, and thanks to this article have had a basic tutorial for getting started.

The question is, can you now publish domain name information for the Internet, as well as for your local network, from the same host?

Yes, very easily. The technique is known as "split-view" or "split-horizon" DNS. The example here will show:

  • one instance of tinydns publishing DNS records for your publicly accessible hosts, for public consumption
  • another instance of tinydns publishing DNS records for the hosts on your local network, for local consumption
  • plus dnscache, a caching resolver making and storing DNS lookups for clients on the local network

All running concurrently on a single OpenBSD host.

Background

The example here describes our own network at guinix.com. In this case we have a single public IP address, from which we host multiple domains with web and email services.

Our gateway host is called "nimba", running OpenBSD 3.2. It is dual homed on 192.168.0.254 and 10.0.1.254. The 192.168.0.1 address is considered the "external" interface, as it connects directly to a port-forwarding router with our public IP address. The 10.0.1.254 address is the "internal" interface, to our local 10.0.1.0/24 network.

On OpenBSD, packet forwarding is enabled in "/etc/sysctl.conf" with the line:

  net.inet.ip.forwarding=1

Our domain name registrar has been configured to point DNS queries for our domains to our public IP address; our external router then port-forwards these queries (port 53) on to "nimba". (For more information about our network configuration and packet filter setup, see this TechNote.)

The DNS services that we'll put up on "nimba" may then be summarized as follows:

Service Interface IP address
"tinydns-public" external 192.168.0.254
"tinydns-local" loopback 127.53.0.1
"dnscache" internal 10.0.1.254

That is, "tinydns-public" will publish DNS records for queries that are presented to the external interface. "tinydns-local" will run on a private loopback address, publishing DNS records for the local network, responding only to queries presented to it via "dnscache". "dnscache" will resolve nameserver queries coming from the local network, for either public or local DNS records, polling either the Internet or "tinydns-local" as appropriate. Clients on the local network may then all be configured to use 10.0.1.254 for their caching resolver.

Recall that on OpenBSD, to provide a private loopback address of 127.53.0.1 for the "tinydns-local" configuration, you can set up "/etc/hostname.lo1" with this entry:

  inet alias 127.53.0.1 netmask 0xffffffff

That just about takes care of all the preliminaries. Now we move on to the doing!

Installation and Configuration

First, download and install daemontools. We follow "the djb way" exactly here and use "/service" as the daemontools service directory. Swipe a modified start-up script from FreeBSD here and put it into "/usr/local/etc/rc.d/svscan.sh". Then add the following stanza to the bottom of "/etc/rc.local":

## daemontools startup:
## --this starts up djbdns, qmail, etc.
if [ -x /usr/local/etc/rc.d/svscan.sh ]; then
        /usr/local/etc/rc.d/svscan.sh start
        sleep 5
fi

Grab and build djbdns next. The tutorial found here may be helpful background. Create the unpriveleged users "tinydns", "dnscache" and "dnslog" as described in the article before proceeding.

Nameserver #1: tinydns-public

Now become super-user and configure the public nameserver:

  # tinydns-conf tinydns dnslog \
  #   /usr/local/etc/djbdns/tinydns-public 192.168.0.254 

Important: On OpenBSD you will need to increase the "softlimit" argument in the tinydns run script just installed in "/usr/local/etc/djbdns/tinydns-public/run". Edit this file and change the -d parameter to read:

  softlimit -d350000

Next create the DNS records that will be served out to the Real World, adding the public nameserver, mail exchanger, host and alias records as necessary. Change to "/usr/local/etc/djbdns/tinydns-public/root" and use "add-ns", "add-mx", "add-host", and "add-alias" to configure the database:

  # ./add-ns guinix.com  199.104.115.195
  # ./add-ns 115.104.199.in-addr.arpa 199.104.115.195
  # ./add-mx guinix.com  199.104.115.195
  # ./add-host nimba.guinix.com 199.104.115.195
  # ./add-alias www.guinix.com 199.104.115.195
  # ./add-alias dns1.guinix.com 199.104.115.195
  # ./add-alias dns2.guinix.com 199.104.115.195
  (...)
  # make

(In this puny network, every DNS record will resolve to our single lonely public IP address.)

Nameserver #2: tinydns-local

Continuing as super-user, configure the local nameserver:

  # tinydns-conf tinydns dnslog \
  #   /usr/local/etc/djbdns/tinydns-local 127.53.0.1

As described for tinydns-public above, edit the run script and increase the softlimit parameter.

Now change to "/usr/local/etc/djbdns/tinydns-local/root" and install the DNS records for the local network:

  # ./add-ns guinix.com 127.53.0.1
  # ./add-ns 1.0.10.in-addr.arpa 127.53.0.1
  # ./add-host nimba.guinix.com 10.0.1.254
  # ./add-alias www.guinix.com 10.0.1.254
  # ./add-alias smtp.guinix.com 10.0.1.254
  # ./add-alias pop.guinix.com 10.0.1.254
  # ./add-host rocky.guinix.com 10.0.1.8
  # ./add-host alloy.guinix.com 10.0.1.10
  # ./add-host slate.guinix.com 10.0.1.16
  (...)
  # make

You can see that some of the hosts are physically the same machines as those on the public network--such as "nimba" and "www"--but this nameserver will let us find them from the local network, with their local network addresses. Then, when a local client browses to

  http://www.guinix.com/

this client will see the very same website as a visitor from the Great Beyond.

Caching resolver: dnscache

Finally, install a DNS resolver for clients on the local network:

  # dnscache-conf dnscache dnslog \
  #   /usr/local/etc/djbdns/dnscache 10.0.1.254

Configure it to accept queries from clients on the local network:

  # cd /usr/local/etc/djbdns/dnscache/root/ip
  # touch 10.0.1

Point queries regarding the local network to the tinydns-local nameserver publishing on 127.53.0.1:

  # cd /usr/local/etc/djbdns/dnscache/root/servers
  # echo "127.53.0.1" > guinix.com
  # echo "127.53.0.1" > 1.0.10.in-addr.arpa

Here's the magic "glue" that points dnscache to tinydns-local for local DNS records. For all other lookups, dnscache will recursively seek over the Internet.

Start me up!

Now everything is configured and ready to go. To start up the servers, just link them into the daemontools service directory:

  # ln -s /usr/local/etc/djbdns/tinydns-public /service
  # ln -s /usr/local/etc/djbdns/tinydns-local /service
  # ln -s /usr/local/etc/djbdns/dnscache /service

daemontools will automatically start the new servers within 5 seconds. Clients may now be configured to access the dnscache resolver by editing "/etc/resolv.conf" (or equivalent) on local hosts to read:

  domain guinix.com
  nameserver 10.0.1.254

Try some queries with djbdns utilities:

  $ dnsip alloy.guinix.com
  10.0.1.10
  $ dnsip www.openbsd.org
  129.128.5.91
  $ dnsname 10.0.1.8
  rocky.guinix.com

Browse and bop and email and ssh, inside or out. That's it! "Dualling" nameservers on a single host.


ADDENDUM: Using a single nameserver with tagged records

It is possible to obtain similar results with a single instance of tinydns, by "tagging" the DNS records in the data file with a location code. The method is described here.

Step 1. Configure a single tinydns similar to Nameserver #1 above:

  # tinydns-conf tinydns dnslog \
  #   /usr/local/etc/djbdns/tinydns 192.168.0.254 

As before, increase the "softlimit" parameter in the daemontools run script, "/usr/local/djbdns/tinydns/run".

Step 2. Create/edit the data file in "/usr/local/djbdns/tinydns/root/data" with your favorite text editor. Enter DNS records tagged with location codes as follows:

# tinydns "data" file for split-view dns
#
# define external location code:
%ex
#
# define external records:
#
# NS:
.guinix.com:199.104.115.195:a:259200::ex
.115.104.199.in-addr.arpa:199.104.115.195:a:259200::ex
#
# HOST:
=nimba.guinix.com:199.104.115.195:86400::ex
#
# MX:
@guinix.com:199.104.115.195:a::86400::ex
#
# ALIAS:
+dns.guinix.com:199.104.115.195:86400::ex
+dns1.guinix.com:199.104.115.195:86400::ex
+dns2.guinix.com:199.104.115.195:86400::ex
+www.guinix.com:199.104.115.195:86400::ex
#
# -----
#
# define internal location code:
%in:192.168.0
%in:10.0.1
#
# define internal records:
#
# NS:
.guinix.com:192.168.0.254:a:259200::in
.0.168.192.in-addr.arpa:192.168.0.254:a:259200::in
#
# HOST:
=nimba.guinix.com:192.168.0.254:86400::in
#
# ALIAS:
+www.guinix.com:192.168.0.254:86400::in
+smtp.guinix.com:192.168.0.254:86400::in
+pop.guinix.com:192.168.0.254:86400::in
#
# MORE HOSTS:
=rocky.guinix.com:10.0.1.8:86400::in
=alloy.guinix.com:10.0.1.10:86400::in
=alloy.guinix.com:10.0.1.16:86400::in
# ...

Note here that in order to apply location codes, the data file must be edited manually. That is, the "./add-*" utilities by themselves will not be adequate. (See Bernstein's tinydns-data documentation page for the syntax of all tinydns record types.)

In the example data above, two location codes are defined. The syntax for a location code definition is:

%lo:ipnet

where "lo" is a location code identifier (one or two letters), and "ipnet" is an IP address prefix. When a DNS record is tagged with a location code lo in the last field, that record will be made visible only to client requests originating from addresses with an ipnet prefix. tinydns selects the location code for an incoming DNS query according to the definition that matches the longest ipnet prefix of the remote host.

The first location code in the example is "ex", to identify records to return from requests outside the local network. Here the ipnet parameter is empty. This expresses a default match with a request originating from any IP address.

The second location code is "in", defined here to match addresses originating from the local network. Now records tagged with "in" will match requests coming from the local network (prefixes 192.168.0 and 10.0.1), and records tagged with "ex" will match all other requests.

Step 3. After creating/editing the data file, convert it as before to the cdb format used by tinydns:

  # cd /usr/local/etc/djbdns/tinydns/root

... (create/edit data file) ...

  # make

Step 4. Set up the dnscache service as described above, except now point dnscache to the tinydns running on 192.168.0.254:

  # cd /usr/local/etc/djbdns/dnscache/root/servers
  # echo "192.168.0.254" > guinix.com
  # echo "192.168.0.254" > 1.0.10.in-addr.arpa

Step 5. Link the services into the daemontools "/service" directory:

  # ln -s /usr/local/etc/djbdns/tinydns /service
  # ln -s /usr/local/etc/djbdns/dnscache /service

The new services will automatically start under daemontools within 5 seconds. Queries from the local network will then be answered with DNS records tagged "in", while outside requests will be answered with DNS records tagged "ex".

Which method is better? That's for you to decide! I prefer the dual nameserver approach. It is easy to set up and maintain, the records are simpler and fewer. Moreover, it is a modular approach and "conceptually cleaner" than running a single nameserver, in that the segregation of services and DNS data matches our intentions. Also, the external addresses in our network are fairly static, whereas the internal network configuration changes more frequently. With a separate nameserver for the local network, we don't have to worry about unintentionally mucking up public records in the external nameserver.

Others may prefer to lump all DNS records in a single file. This is the more "monolithic" approach, and may suit users who are more accustomed to working with monolithic systems.

Still other networks may benefit from a combination of these methods. Whatever your own preference or network configuration, the cool thing here is that you do have the choice!


Copyright © 2002 - 2005, Wayne Marshall. All rights reserved.
Last edit 2005.03.07, wcm.