Dual chrooted  BIND/DNS servers 

Dave Lugo dlugo@etherboy.com
version 1.03 updated March 5, 2000

On a firewall system it may be desireable to run dual dns servers.    This allows internal clients to have a local view of a domain (mydomain.com for example), while external (Internet) clients have a different view, being served from a different name server daemon/zone file.

The servers are chrooted for security reasons.  It's a good practice on a firewall to run chrootable services chrooted, and with minimal priviliges.

The internal server  daemon  listens on the internal network interface.  It has whatever zone files that it needs to provide an intranet's local dns needs.  For internal dns queries that don't refer to a local host, it forwards the request to the external named daemon.

The external server daemon listens on the external network interface and the loopback interface.  It handles dns queries from external clients, as well as handling the forwarded  requests from the internal named daemon.

Please note that these instructions are for a Redhat 6.0 install. Minimal tweaking may be needed for other Linux distributions which is not covered here.



Download what you need

What you'll need to grab to do this:

The latest production source for BIND from www.isc.org

For getting syslogged data out of the chroot tree, you have 2 choices:



Building what you need and creating the chroot area
  1. First,  if you're using holelogd, extract and build the utils-1.0 package.  You may see some warning messages when you compile the package, but as long as holelogd itself compiles ok, we're happy.  Don't do a make install, as we'll be copying the newly-produced holelogd executable into the chrooted tree later.
  2. If you're planning on using syslogd , this would be a good time to look at the man page for syslogd, because in a few minutes you'll need to edit your /etc/rc.d/init.d/syslog script.
     

  3. Next,  extract and build BIND.  If you do a "make install" after building you _may_ want to go back and delete the newly-installed named and named-xfer daemons, as we'll be installing those in the chrooted tree.

  4.  
  5. Now we need to create the directory tree, for this example we'll set up everything under /usr/local/bind:
    1. mkdir /usr/local/bind
      cd /usr/local/bind
      mkdir dbfiles_external
      mkdir dbfiles_internal
      mkdir dev
      mkdir etc
      mkdir lib
      mkdir sbin
      
      
  6. The dev directory will need a null device file.  I originally didn't bother with this, but for some reason zone-xfers would fail if this was missing.
    1. mknod -m 666 /usr/local/bind/dev/null c 1 3
  7. The etc directory will need a few things: passwd and group files with only one entry each - the named user and named group.  You should make sure the UID and GID you use are unique.  Don't forget to add the chroot named user/group  to your non-chrooted /etc/passwd and /etc/group, so that an "ls -l" in the chroot area will show name and group instead of UID/GID. You'll also need copies of /etc/ld.so.cache and /etc/localtime.  At this point you should also chown the dbfiles_* dirs.
    1. cd /usr/local/bind/etc
      echo "named::22" > group
      echo "named:x:22:22:named:/:" > passwd
      echo "named::22" >> /etc/group
      echo "named:x:22:22:named:/:" >> /etc/passwd
      cp /etc/ld.so.cache .
      cp /etc/localtime .
      chown named.named ../dbfiles_*
      
      
  8. The lib directory will need libc and a few other things.  The list here is what works for me under RedHat 6.0, your mileage may vary.   You *can* use strace to assist in debugging, see  troubleshooting (at the end of this document).
    1. cd /usr/local/bind/lib
      cp /lib/ld-2.1.1.so .
      ln -s ld-2.1.1.so ld-linux.so.1.9.5
      cp /lib/libc-2.1.1.so .
      ln -s libc-2.1.1.so libc.so.6
      cp /lib/libnsl-2.1.1.so .
      ln -s libnsl-2.1.1.so libnsl.so.1
      cp /lib/libnss_compat-2.1.1.so .
      ln -s libnss_compat-2.1.1.so libnss_compat.so.2
      cp /lib/libnss_files-2.1.1.so .
      ln -s libnss_files-2.1.1.so libnss_files.so.2
      
      
  9. We'll need to copy over the holelogd (if you're using it instead of passing the "-a <socket>" option to syslogd), named, and named-xfer executables.  Depending on where you compiled them you may need to adjust the cp commands.  Copying holelogd to a different name (as below) makes things easier if you run multiple holelogd instances (as you might if you chroot other things).You can also strip the executables to save space:
    1. cd /usr/local/bind/sbin
      cp /usr/src/bind-8.2.2p5/src/bin/named/named .
      cp /usr/src/bind-8.2.2p5/src/bin/named-xfer/named-xfer .
      cp /usr/src/utils-1.0/holelogd holelogd.named    (or not, if using syslogd)
      strip named named-xfer holelogd.named
      
      
  10. We'll need a named.conf in each of the dbfiles directories.  The domain name we use (on both internal and external servers) will be identical, but because we run two named daemons, the external named can have different data for the same domain.

    We also lock things down a bit, using the allow-query and allow-transfer statements available as part of BIND.

    Disclaimer:  This document is not a primer on BIND.  You should buy the book, it's worth it.

    Disclaimer#2: The example files here have bogus domain name and address values.  You will need to provide correct ones.

    Our domain name will be somedomain.com

  11. Our internal IP address is 192.168.1.1
    Our external IP address is 172.16.10.1

    These conf files can also be downloaded: named.conf.internal  named.conf.external
     

      #======================================================
      # named.conf for dbfiles_internal directory.
      #
      # NOTE: Comments in this file begin with a # symbol.
      #
      # NOTE: Remember we're chrooted.  Don't break the paths
      #       below by forgetting that.
      #======================================================
      options {
              directory "/dbfiles_internal";
              pid-file "/dbfiles_internal/internal.pid";
              named-xfer "/sbin/named-xfer";
              #
              # specify the internal IP address of this box
              listen-on { 192.168.1.1; };
              #
              # specify the external IP address of this box
              forwarders { 172.16.10.1; };
              #
              # only allow queries from this source.
              allow-query { 192.168.1/24; };
      };
       
      controls{
              unix "/dbfiles_internal/ndc_internal"
              perm 0600
              owner 0
              group 0;
      };
      
      zone "somedomain.com" in {
              type master;
              file "db.somedomain.com"; 
      };
      
      zone "1.168.192.in-addr.arpa" in {
              type master;
              file "db.192.168.1"; 
      };
      
      zone "0.0.127.in-addr.arpa" in {
              type master;
              file "db.127.0.0"; 
      };
      
      zone "." in {
              type hint;
              file "db.cache"; 
      };

      #======================================================
      # named.conf for dbfiles_external directory.  
      #
      # NOTE: Comments in this file begin with a # symbol.
      #
      # NOTE: Remember we're chrooted.  Don't break the paths
      #       below by forgetting that. 
      #======================================================
      options {
              directory "/dbfiles_external";
              pid-file "/dbfiles_external/external.pid";
              named-xfer "/sbin/named-xfer";
              #
              # depending on how/if you packet filter, you may 
              # want this. AFAIK, it doesn't hurt.
              query-source address * port 53;
              #
              # global options set to only allow queries from
              # us.  We explicitly allow our served zones to be 
              # queried on a per-zone basis later in this file.
              allow-query { 192.168.1.0/24; 127.0.0.1; 172.16.10.1; };
              #
              # specify the external IP and loopback addresses here.
              listen-on { 172.16.10.1; 127.0.0.1; };
      };
       
      controls{
              unix "/dbfiles_external/ndc_external"
              perm 0600
              owner 0
              group 0;
      };
       
      zone "somedomain.com" in {
              type master;
              file "db.somedomain.com";
              allow-query { any; };
              allow-transfer { 172.16.12.10; 10.0.0.1; };
      };
       
      zone "10.16.172.in-addr.arpa" in {
              type master;
              allow-query { any; };
              file "db.172.16.10";
              allow-transfer { 172.16.12.10; 10.0.0.1; };
      };
       
      zone "0.0.127.in-addr.arpa" in {
              type master;
              allow-query { any; };
              file "db.127.0.0";
      };
       
      zone "." in {
              type hint;
              file "db.cache";
      };
      
      

  12. Once you've created all the zone files in your dbfiles_internal and dbfiles_external directories, you should end up with something like this when you do an ls (adjust zonefile names per your network):
  13. ls -lR /usr/local/bind/dbfiles_*
    
    /usr/local/bind/dbfiles_external:
    total 18
    -rw-r--r--   1 root     root         678 Nov 14 22:28 db.127.0.0
    -rw-r--r--   1 root     root         690 Nov 14 22:29 db.172.16.10
    -rw-r--r--   1 root     root        2769 Aug  1 12:55 db.cache
    -rw-r--r--   1 root     root        1508 Nov 14 22:46 db.somedomain.com
    -rw-r--r--   1 root     root        1425 Nov 19 22:29 named.conf
    /usr/local/bind/dbfiles_internal:
    total 18
    -rw-r--r--   1 root     root         669 Nov 14 22:30 db.127.0.0
    -rw-r--r--   1 root     root         800 Nov 14 22:30 db.192.168.1
    -rw-r--r--   1 root     root        2769 Aug  1 12:54 db.cache
    -rw-r--r--   1 root     root        1062 Nov 14 22:31 db.somedomain.com
    -rw-r--r--   1 root     root        1004 Nov 19 22:38 named.conf
     
  14. Modify/create an /etc/rc.d/init.d/dns startup script.  One is provided below that works on a Redhat 6.0 system.  You'll need to set up symlinks from the rc.* directories.    See the man page for chkconfig (for Redhat systems). Make sure the script is executable once you've copied it to the init.d directory.


  15. Don't forget to disable your old named startup script. Read the man page for chkconfig, or look at the symlinks in the /etc/rc.* dirs and rename or remove the ones that point to the old script. On a Redhat 6.0 system, the old script is /etc/rc.d/init.d/named

    You can also download this new script here
    #!/bin/sh
    #
    # dns          Start/Stop the internal and external name daemons
    #
    # description: dns is a script for starting/stopping/etc DNS servers
    #              version 1.02 
    # chkconfig: 345 14 58
    # processname: named
     
    # Source function library.
    . /etc/rc.d/init.d/functions
     
    # See how we were called.
    case "$1" in
      start)
            echo -n "Starting DNS services: "
            # 
            # uncomment the following line if you're using holelogd for logging.
            #daemon /usr/local/bind/sbin/holelogd.named /usr/local/bind/dev/log
            daemon chroot /usr/local/bind /sbin/named -b /dbfiles_internal/named.conf  -u named -g named
            daemon chroot /usr/local/bind /sbin/named -b /dbfiles_external/named.conf  -u named -g named
            echo
            ;;
      stop)
            echo -n "Stopping DNS services: "
            killall named
            # 
            # uncomment the following line if you're using holelogd for logging.
            #killproc holelogd.named
            echo
            ;;
      status)
            status named
            # 
            # uncomment the following line if you're using holelogd for logging.
            #status holelogd.named
            ;;
      restart)
            /etc/rc.d/init.d/dns stop
            /etc/rc.d/init.d/dns start
            ;;
      reload-ext)
            ndc -c /usr/local/bind/dbfiles_external/ndc_external reload
            ;;
      reload-int)
            ndc -c /usr/local/bind/dbfiles_internal/ndc_internal reload
            ;;
      reconfig-ext)
            ndc -c /usr/local/bind/dbfiles_external/ndc_external reconfig
            ;;
      reconfig-int)
            ndc -c /usr/local/bind/dbfiles_internal/ndc_internal reconfig
            ;;
      *)
            echo "Usage: dns {start|stop|status|restart|reload-ext|reload-int|reconfig-ext|reconfig-int}"
            exit 1
    esac
    
    exit 0
  16. If you decided to use syslogd, you'll need to edit the /etc/rc.d/init.d/syslog script and change one line.   Note that these are the changes that I did on my system, your system (depending on which distribution and other factors) may be different.
    1. Old line:        daemon syslogd -m 0
      
      New line:        daemon syslogd -a /usr/local/bind/dev/log -m 0
    Don't  forget to restart syslogging with the new options:
      /etc/rc.d/init.d/syslog restart
  17. Assuming you've done everything correctly, try starting the dns servers.
    1. /etc/rc.d/init.d/dns start
    The named daemons should create pid and ndc files (per the named.conf files) in each of your dbfiles directories. You should also notice a new file called "log" in the chrooted dev directory that holelogd/syslogd uses to pipe log data to /var/log/messages.
     
  18. You may also want to adjust your /etc/resolv.conf file so that clients on the same system use the internal server for dns resolution.  The "forwarders" line in the internal daemons's named.conf file will handle queries for non-local data.
    1. search somedomain.com
      nameserver 192.168.1.1


Troubleshooting

If the daemon doesn't start correctly, you can use strace as a diagnostic aid.  Change the daemon lines in the dns script so they are similar to the following (change internal to external as needed), and you'll have strace files in /tmp to help you figure out what's missing.

 daemon strace -o /tmp/dns.strace -f -ff chroot /usr/local/bind /sbin/named \
 -b /dbfiles_internal/named.conf -u named -g named
You can of course have named produce debug output.  RTFM



Comments and/or Suggestions?

Please send them to me at dnscomments@etherboy.com   Warning to spammers: If you use this address to send me unsolicited crap, you consent to my LARTing you.