DNSSEC TSIG BIND9

From WTFwiki
Jump to navigation Jump to search

Overview

I recently had some time to get an item off my long-term TODO list: setting up
DNSSEC signing on my zones, validation on my resolvers, and TSIG XFER between
masters/slaves. The latter I've done before, the two former items were a total
mystery to me. Less so, now.

I'm using ISC BIND 9.9.x on OpenBSD 5.4 for these, installed from ports/packages.
Seems like times change quickly in DNSSEC land, so this may or may not become
irrelevant in short order, like so much of the web documentation on DNSSEC.

OpenBSD Challenges/Prerequisites

As of OpenBSD 5.4, an in-tree copy of BIND 9.4.2 is shipped, and it doesn't do any
DNSSEC related bits. However, someone was kind enough to build a port/package for
a more modern version that does: package name is 'isc-bind', and can be installed
as a binary package in the normal ways.

Configuration for it uses the shipped /var/named/ tree, and if you have a previously
configured copy in use on your system, the conversion is pretty easy.

Edit /etc/rc.conf.local and comment out the following line, if it exists:

named_flags=

Add the following to your "pkg_scripts" line. If you don't have that line, now is a
fine enough time to add one. It enables the "new" rc.d style pkg start scripts. The
line should look something like one of the below:

pkg_scripts="isc_named"
pkg_scripts="mysql munin-node isc_named"

Once that's finished, simply kill the old BIND and start the new one:

$ sudo pkill -9 named
$ sudo /etc/rc.d/isc_named start

Have a look in /var/log/daemon to see if everything is as expected. If so, you now have
an upgraded copy of BIND9 capable of DNSSEC signing/validation.

Keep in mind, you'll still have some system-shipped 'dnssec-*' binaries hanging out
in /usr/sbin that will probably be preferred by your $PATH -- Easy solution is to
rename them:

$ sudo mv /usr/sbin/dnssec-signzone /usr/sbin/dnssec-signzone.dist
$ sudo mv /usr/sbin/dnssec-keygen /usr/sbin/dnssec-keygen.dist

DNSSEC validating resolvers

Easy enough, right? Well, I'll leave the nonsense chain-of-trust explanations to
the rest of the web, thats a detail thats consistantly explained by everyone, and
in a few cases fairly well. This is more of a how-to than a why-for.

I'll use '/var/named' as the BIND root for this; if you follow my OpenBSD crap above
you'll see that is the system I'm running on primarily.

First, edit /var/named/etc/named.conf and add a few lines inside "options { ... }":

dnssec-enable yes;
dnssec-validation yes;
dnssec-lookaside auto;

managed-keys-directory "dynamic";

Then, add a line outside options, in the 'global' scope:

include "etc/bind.keys";

Then, you'll probably need to actually retrieve the most current bind.keys
file for your version of BIND. A brief explanation of what this file is can be found at
the above URL. It's a transition shim, ultimately a temporarily permanent fixture.
Place the file on your system as /var/named/etc/bind.keys so the above 'include' can find
it. Relative paths (inside /var/named) worked for me, you may not be so lucky.

Now, you'll need to create the 'dynamic' directory and fix permissions:

$ sudo mkdir /var/named/dynamic
$ sudo chown named:named /var/named/dynamic

At this point, restart BIND and pay attention to /var/log/daemon to see how it went.
You probably have a usable DNSSEC validating resolver now. If not, good luck.

TSIG XFER between servers

It's nice to have some level of security and confidence in your zone transfers,
and IP based allow/deny is a bit weak. For this reason, I chose to setup TSIG
for authenticating servers attempting to transfer (XFER) zones.

The drawback of this is that you need to generate and distribute keys to your
other nameservers; but it's a one time deal.

Generating Keys

We'll generate a key pair on the master, let's say "ns1.example.com". Our other
nameserver is "ns2.example.com", setup as a slave for all zones involved. We're
going to use a key naming convention that I like, because this is my documentation.
Pick something you like, but stick with it for your own sanity.

My naming scheme is like this: master-slave. For this doc, we're using this like so:
"ns1.example.com-ns2.example.com". It's long, but you don't type it *THAT* much,
and the clarity is great later.

We're going to make a directory specifically for all of our keys. I use the same
one for DNSSEC signing keys and TSIG keys.

ns1$ sudo mkdir /var/named/etc/keys

Now let's generate the TSIG host key:

ns1$ cd /var/named/etc/keys
ns1$ dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST -r /dev/urandom ns1.example.com-ns2.example.com

This generates a pair of files in the current directory, one ending in '.key' (public)
and the other in '.private' (...private, wouldn't have guessed it, right?). You need
to grab the contents of one field in the private key:

ns1$ grep '^Key' Kns1.example.com-ns2.example.com.+XXX+YYYYY.private

The relevant data is what's after ": ", a hash string. You need to add it to your BIND
config, which we'll do in a separate file, because clarity, mhm.

ns1$ vi /var/named/etc/tsig.keys

Make the contents of the file look like this:

key "tsig-ns1.example.com-ns2.example.com" {
      algorithm HMAC-SHA512;
      secret "rEAllybiglongencryptedhashstringhmacsha512etcetcg==";
};

Now include this in your named.conf, in the global scope. If you went through with
the procedure way above of adding bind.keys, you can add it right around that.

include "etc/tsig.keys";

Now you can pick one of your zones and set it up with the new keys. Don't worry
about reloading the BIND config yet, we'll get there. Don't be hasty.
Find the zone config in named.conf, and find the "allow-transfer" section. There
should be an IP address there for ns2.example.com, and we're going to replace it
with the new TSIG key.

allow-transfer {
      key "tsig-ns1.example.com-ns2.example.com";
};

Now you may be hasty: restart BIND9 (or 'rndc reconfig'). The logs should show no
obvious errors, unless they were preexisting or you botched something just now.

Let's move on to ns2.example.com's config. We need to add a similar "tsig.keys"
file there, with the contents looking like this:

key "tsig-ns1.example.com-ns2.example.com" {
      algorithm HMAC-SHA512;
      secret "rEAllybiglongencryptedhashstringhmacsha512etcetcg==";
};

/* 1.2.3.4 == ns2.example.com */
server 1.2.3.4 {
      keys { tsig-ns1.example.com-ns2.example.com; };
};

The astute observationinst will notice the "secret" is the same one we put in the
config file on ns1: this is no coincidence, it is the entire idea.

Now add the same line for the tsig.keys file into named.conf:

include "etc/tsig.keys";

You need make no changes to your slave zone definitions: the IP address must remain
there, but should match the "server" bit in tsig.keys that we defined earlier.

Now restart/reconfig BIND and pay attention to the logs:

03-Mar-2014 23:06:42.237 general: zone example.com/IN: transferred serial 2014030301: TSIG 'tsig-ns1.example.com-ns2.example.com'

This indicates successful work: anything else probably doesn't!

Zone Signing

Scripts

'zonekeys'

#!/bin/sh

if [ "$1" = "" ]; then
        echo "Syntax: zonekeys <zonename>"
        exit
fi

ZONE=$1

cd /var/named/etc/keys
dnssec-keygen -f KSK $ZONE
KEYFILE1=`ls K${ZONE}*.key`

dnssec-keygen $ZONE
KEYFILE2=`ls K${ZONE}*.key | grep -v $KEYFILE1`

echo "Now add the following to /var/named/master/$ZONE (after '\$ORIGIN ${ZONE}.')"
echo ""
echo "\$include ../etc/keys/$KEYFILE1 ; KSK"
echo "\$include ../etc/keys/$KEYFILE2 ; ZSK"
echo ""
echo "Then 'signzone $ZONE'"

'signzone'

#!/bin/sh
################################################################################
#
# 2014-12-08 -- jontow@zenbsd.net
#
# Sign [or re-sign] a given DNSSEC enabled zone.
#
################################################################################

if [ "$1" = "" ]; then
        echo "Syntax: signzone <zonefile>"
        exit
fi

ZONE=$1

cd /var/named/master
/usr/local/sbin/dnssec-signzone -K /var/named/etc/keys $ZONE

# Hidden argument, for non-interactive use.
if [ "$2" != "-q" ]; then
        echo ""
        echo "Now edit named.conf and change '$ZONE' to '${ZONE}.signed' in the file clause."
        echo "Then 'rndc reconfig'"
        echo ""
fi

'cron.signzone'

This script requires that the serial number in the zonefile be specified with a
comment after it like this:

                     2014122100 ; AUTOSERIAL

This script is meant to be run from a crontab(5) file rarely, on an interval such
as once per month.

#!/bin/sh
################################################################################
#
# 2014-12-08 -- jontow@zenbsd.net
#
# Meant to be run every $interval (~1month?) via cron from the primary
# nameserver for a group of zones.  Relies on 'signzone' script,
# found in this same directory.
#
################################################################################

ZONEDATA=/var/named/master

NEWSERIAL=`date +"%Y%m%d00"`

cd $ZONEDATA

ZONELIST=`ls | grep '.signed' | sed '1,$s/\.signed//g'`

for ZONE in $ZONELIST; do
        if [ -f $ZONE.$NEWSERIAL ]; then
                echo "Backup $ZONE.$NEWSERIAL already exists.. Bailing before we do anymore damage."
                exit
        fi

        if [ ! -f $ZONE ]; then
                echo "Missing zonefile $ZONE"
        else
                # Backup zonefile
                cp $ZONE $ZONE.$NEWSERIAL

                # Bump serial number
                echo "Bumping serial number for zone $ZONE to $NEWSERIAL"
                TMPFILE=`mktemp /tmp/$ZONE.XXXXXX`
                sed "1,\$s/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]\ \;\ AUTOSERIAL/$NEWSERIAL\ \;\ AUTOSERIAL/g" $ZONE >$TMPFILE
                mv $TMPFILE $ZONE

                /usr/local/sbin/je.signzone $ZONE
        fi
done

rndc reload

Procedure

  1. zonekeys
  2. edit zonefile, add includes
  3. signzone
  4. edit named.conf, rename zone zone.signed
  5. rndc reload

Obtain DS Record

dnssec-dsfromkey -f zonefile

Install DS Record at Registrar

Godaddy

placeholder, mention formatting difference

Testing

placeholder.

External Resources

placeholder.