DNS Security I : Dynamic Updates

Originally published in SysAdmin Magazine, September 2004

Contents

      Introduction
      Secure Dynamic Updates
      TSIG-secured zone transfers
      DHCP Dynamic Updates
      Conclusion
      References

Introduction

Of all of the many network protocols we use in computer networking, DNS is one of the most fundamental and important. The task of mapping domain names to IP addresses seems simple, and at first approach it is. However, issues arise when that protocol becomes extensively trusted by systems. Packets within the DNS protocol are all sent as clear text which means that they can be easily read and modified while in transit. DNS uses the UDP protocol which has no handshaking between clients and servers and is therefore quite susceptible to spoofing attacks. We no longer have a trusted Internet in which we can trust insecure services - there are malicious attackers that will do their very best to make users go to spoofed banking sites, to swamp everyone with spam, and generally wreak havoc.

This article is the first of two in which I will be looking at the use of cryptographic methods to secure DNS. In this article I will be covering secure dynamic updates and zone transfers using TSIG keys, in the second article I will cover the DNSSEC protocols for securing zone data.

In the examples below, I'm using an installed copy of ISC BIND version 9.3.0rc2 from www.isc.org, which has been setup to serve a test domain "domain.tld", and a reverse domain "1.168.192.in-addr.arpa".

I won't cover the installation of the BIND software here, but it is quite straightforward. Note that you will need to add the "--with-openssl" option to the ./configure command if you want to use the DNSSEC functionality.

Secure Dynamic Updates

Using dynamic updates is a great way of maintaining your DNS zone data. With some knowledge and a bit of practice you will probably find it easier and less mistake-prone than the old-fashioned way of editing zone files ( don't forget to update that serial number! ). Enabling dynamic updates is easy, but care must be taken to make sure it is secure. While you can restrict updates by their originating IP address this approach is not very secure at all because updates use UDP which is quite easily spoofed. The better method is by signing the updates with Transaction Signature ( TSIG ) keys and permitting only updates with authorized keys.

Keys are simply base-64 encoded random data which is shared by the DNS server and the client system sending updates. To generate a key for updates, use the dnssec-keygen command :

    dnssec-keygen -a HMAC-MD5 -b 512 -n HOST update_key
this generates a 512-bit key with the HMAC-MD5 algorithm, and places the result into 2 files : Kupdate_key.+157+59571.key and Kupdate_key.+157+59571.private for the public and private key data. The numbers in the key file name are for the key algorithm ( 157 for HMAC-MD5 ) and a 5-digit random key identifier ( which will be different on your system ). Store both key files in /var/named or wherever you keep DNS zone files on your DNS server. Now, configure your DNS server to accept updates signed with this key, edit /etc/named.conf, and add the definition for the key :
    key update_key {
        algorithm HMAC-MD5;
        secret "fxT7q+Kq8TUmZrN4edxAVeTeOmiX/3A6HQ40HcyI...g==";
    };
where the secret string is extracted from the private key file ( it has been truncated in this example ). You won't be able to include the key file directly into the named.conf file ( with the 'include' statement ) because the key files generated by dnssec-keygen are formatted to be included into zone files and the syntax is different to what the configuration file uses. Now, edit /etc/named.conf and configure a zone to receive updates with this key, edit your zone definition and add an allow-update option:
    zone "domain.tld" IN {
        type master;
        file "domain.tld";
        allow-update { key update_key; };
    };
don't forget the reverse zone as well :
    zone "1.168.192.in-addr.arpa" IN {
        type master;
        file "1.168.192.in-addr.arpa";
        allow-update { key update_key; };
    };
you can add a finer-grained level of control with the update-policy option, for example:
    zone "1.168.192.in-addr.arpa" IN {
        type master;
        file "1.168.192.in-addr.arpa";
        update-policy { grant update_key name * A; };
    };
this would grant changes to A ( Address ) records only for requests signed with the update_key key.

Now, restart you DNS server, check the syslog for any errors, and test the updates from the command line:

  /usr/local/bin/nsupdate -k Kupdate_key.+157+59751.key <
this example sends a single update to add an A record into the domain.tld zone. You can use either 'nslookup', 'dig' or the 'host' commands to check that the update has been made. The DNS server will also log an entry in syslog recording the update.

Naturally, we like to use perl to get the job done efficiently. Using the Net::DNS package, we can use perl to perform updates:

    #!/usr/bin/perl -w
    use Net::DNS;
    use strict;

    my $key_name = 'update_key';
    my $key = 'fxT7q+Kq8TUmZrN4edxAVeTeOmiX/3A6HQ40HcyI...g==';

    # Create the update packet.
    my $update = Net::DNS::Update->new('domain.tld');

    # Add a TXT record to the domain
    $update->push(update => rr_add('testrec.domain.tld 86400 IN TXT "A test record"'));

    # Send the update to the zone's primary master.
    my $res = Net::DNS::Resolver->new;
    $res->nameservers('192.168.1.3');

    # Sign the update message
    $update->sign_tsig($key_name, $key);

    my $reply = $res->send($update);

    # Check return code
    if ($reply) {
      if ($reply->header->rcode eq 'NOERROR' ) {
        print "Update succeeded\n";
      } else {
        print 'Update failed ', $reply->header->rcode, "\n";
      }
    } else {
      print 'Update failed', $res->errorstring, "\n";
    }
this example creates a new TXT record called "testrec.domain.tld". Note that we can make multiple updates within a single request packet - they can even include multiple add and delete operations, but only if they are all under the same domain. Also note that dynamic updates simply increment the zone serial number in the SOA record, it doesn't maintain the YYYYMMDD sort of format often used for serial numbers.

Also be aware that once you start using dynamic updates, you can't really use manual editing of the zones anymore. The BIND server maintains journal files which contain a record of dynamic updates and these need to be taken into account. If you do need to edit a zone manually, the only way is to shut down the DNS server, remove the zone journal ( .jnl ) file, edit the zone, and start the server again.

TSIG-secured zone transfers

Its also very useful to secure the transfers of zone data between master and slave DNS servers. This is particularly relevant if the transfers are taking place across untrusted networks such as the public Internet. As mentioned before, the data being transferred is all in plain-text and contains no checking mechanism, so there is a risk of data corruption or malicious interference of the data being transferred. We can easily use TSIG keys to sign the zone transfer data.

Firstly, generate a key to be used for zone transfers. This should not be the same as the key you created for dynamic updates. You could create a separate key for each slave server, but in the examples below I've created a single key for all slaves.

Generate a new key on the master, named "slave_xfers_key":

  dnssec-keygen -r /dev/urandom -a HMAC-MD5 -b 128 -n HOST slave_xfers_key
copy the key data string from the created key file into /etc/named.conf on the master server:
  key slave_xfers_key {
        algorithm HMAC-MD5;
        secret "mv0NxiVT4ho/TZbvPID+Kg==";
  };
and permit zone transfers on the master server for clients with the key by adding allow-transfer statements in /etc/named.conf:
  zone "domain.tld" IN {
        type master;
        file "domain.tld";
        allow-update { key update_key; };
        allow-transfer { key slave_xfers_key; };
  };

  zone "1.168.192.in-addr.arpa" IN {
        type master;
        file "1.168.192.in-addr.arpa";
        allow-update { key update_key; };
        allow-transfer { key slave_xfers_key; };
  };
now restart the master server and check there are no errors in the syslog ( it is quite easy to forget those semicolons in named.conf files! ).

On the slave server, configure the same key for the zone in /etc/named.conf:

  key slave_xfers_key {
        algorithm HMAC-MD5;
        secret "mv0NxiVT4ho/TZbvPID+Kg==";
  };
and add a "server" statement which will specify the TSIG key which this slave will use when communicating to the specified server:
  server 192.168.1.3 {
    keys { slave_xfers_key; };
  };
the definitions for the slave zones stay the same, there is no need to configure the key in them. The slave server will automatically use the TSIG key when requesting a transfer of any zone from the master server whose IP address was specified in the server statement.

Now, restart the slave server, and check syslog for errors. If the TSIG-secured zone transfer is successful you should see the following syslog messages on the slave server :

  named[3733]: zone domain.tld/IN: Transfer started.
  named[3733]: transfer of 'domain.tld/IN' from 192.168.1.3#53: connected using 192.168.1.2#32835
  named[3733]: zone domain.tld/IN: transferred serial 1: TSIG 'slave_xfers_key'
  named[3733]: transfer of 'domain.tld/IN' from 192.168.1.3#53: end of transfer
... and similar messages on the master server describing the transfer of the zone using the slave_xfers_key TSIG key.

DHCP dynamic updates

DHCP servers can also send dynamic updates to DNS servers as they allocate IP addresses to clients, and naturally you will want to secure these updates with TSIG signatures. This is relatively easy to configure when using ISC's DHCPD server as follows.

In this scenario, we have a client device ( such as a laptop ) which wants to connect to networks and be allocated an IP address ( otherwise known as a DHCP lease ). The client would also like to choose its own host name on the network to be inserted into DNS. In this example I'll configure my linux-based laptop to have the name "gorgon" allocated to it when it connects to networks with a DHCP server and dynamic DNS updates which permit client elected names.

There is 3 steps we need to take to achieve this:

  • Step 1 : configure the DHCP server and DNS server to use TSIG dynamic updates
  • Step 2 : configure the DHCP server to update the DNS server each time a lease is let for the subnet
  • Step 3 : configure the DHCP client to give the DHCP server a domain name to insert into DNS

Step 1. We've already configured our DNS server to permit signed updates above, so using the ISC DHCPD server we can configure the update_key by adding the following into /etc/dhcpd.conf:

  key update_key {
    algorithm HMAC-MD5;
    secret "fxT7q+Kq8TUmZrN4edxAVeTeOmiX/3A6HQ40HcyI...g==";
  }

  zone 1.168.192.in-addr.arpa {
    key update_key;
    primary 192.168.1.3;
  }

  zone domain.tld {
    key update_key;
    primary 192.168.1.3;
  }

  ddns-updates on;
  ddns-update-style interim;
  allow client-updates;

Step 2. when the DHCP server leases an address for a subnet, instruct it to also send a DNS update, in /etc/dhcpd.conf we add a couple of lines to the subnet statement:

  subnet 192.168.1.0 netmask 255.255.255.0 {
      option domain-name "domain.tld";
      option domain-name-servers 192.168.1.3;
      option routers 192.168.1.1;
      range 192.168.1.20 192.168.1.254;

      # Dynamic DNS updates:
      ddns-domainname "domain.tld";
      ddns-rev-domainname "1.168.192.in-addr.arpa";
  }

Step 3. Configure the DHCP-enabled clients to send their names to the DHCP server. Most Windows clients seem to do this automatically, but some *nix DHCP clients need configuration. For those using the ISC DHCP client software, this is in /etc/dhclient.conf and should look like the following example:

     send host-name "gorgon";
     send fqdn.fqdn "gorgon.domain.tld.";
     send fqdn.encoded on;
     send fqdn.server-update on;
Once the DHCP server has been restarted, and the client requests a DHCP lease, the server should send a TSIG signed dynamic DNS update to the master DNS server to add the name of the client as an A record, and the reverse PTR record.

There is a number of ways of integrating DHCP and dynamic DNS. An alternative to the above could be to have the clients call a script after they get a DHCP lease and use nsupdate to send the updates themselves. In this case each client would need a TSIG key in order to secure the update packets.

Conclusion

In this article I've shown the reader how to set up and use TSIG-signed DNS updates, TSIG secured zone transfers, and TSIG secured dynamic updates from a DHCP server. In the next article on DNS Security Protocols I will look at DNSSEC secured zones where the actual zone resource records are signed with cryptographic keys. This is used to ensure that the query from a DNS client are valid and come from the true server for the domain.

References

Secure Dynamic DNS HOWTO
http://ops.ietf.org/dns/dynupd/secure-ddns-howto.html

Dynamic DNS updates with isc-dhcpd
http://tinyurl.com/6lssd

BIND v9 ARM ( Administrators Reference Manual )
http://www.nominum.com/content/documents/bind9arm.pdf

DNSSEC Operations HOWTO
http://www.ripe.net/disi/Course/TechCourse.pdf

Nominum DNSSEC FAQ
http://www.nominum.com/getOpenSourceResource.php?id=8