Archives

Categories

Dynamic DNS

Table of Contents

The Problem

My SE Linux Play Machine has been down for a couple of weeks. I’ve changed to a cheaper Internet access plan which also allows me to download a lot more data, but I don’t have a static IP address any more – and my ISP seems to change the IP a lot more often than I’ve experienced in the past (I’m used to having a non-static IP address not change for months rather than hours). So I needed to get Dynamic DNS working. Naturally I wasn’t going to use one of the free or commercial Dynamic DNS solutions, I prefer to do things myself. So my Play Machine had to remain offline until I fixed this.

The Solution

dyn    IN      NS      ns.sws.net.au.
        IN      NS      othello.dycom.com.au.
play    IN      CNAME  play.dyn.coker.com.au.

The first thing I did was to create a separate zone file, I put the above records in my main zone file to make play.coker.com.au be a CNAME for play. and dyn.coker.com.au is a dynamic domain. I have SE Linux denying BIND the ability to write to the primary zone file for my domain to make it slightly more difficult for an attacker to insert fake DNS records (they could of course change the memory state of BIND to make it serve bogus data). The dynamic zone file is stored where BIND can write it – and therefore a BIND exploit could easily replace it (but such an attack is out of the scope of the Play Machine project so don’t get any ideas).

Another reason for separating the dynamic data is that BIND journals changes to a dynamic zone and therefore if you want to manually edit it you have to delete the journal, stop BIND, edit the file, and then restart BIND. One of the things that interests me is setting up dynamic DNS for some of my clients, as a constraint is that my client must be able to edit the zone file themself I have to keep the editing process for the main zone file relatively simple.

dnssec-keygen -a hmac-md5 -b 128 -n host foo-dyn.key

For newer versions of BIND use the following command instead:

tsig-keygen -a hmac-sha512 foo-dyn

I used the above command to create the key files. It created Kfoo-dyn.key.+X+Y.key and Kfoo-dyn.key.+X+Y.private where X and Y are replacements for numbers that might be secret.

key "foo" { algorithm hmac-md5; secret "XXXXXXXX"; };
zone "dyn.coker.com.au" {
  type master;
  file "/var/cache/bind/dyn.coker.com.au";
  allow-update { key "foo"; };
allow-transfer { key ns; };
};

I added the above to the BIND configuration to create the dynamic zone and allow it to be updated by this key. The value which I replaced with XXXXXXX in this example came from Kfoo-dyn.key.+X+Y.key. I haven’t found any use for the .private file in this mode of operation. Please let me know if I missed something.

Finally I used the following shell script to take the IP address from the interface that is specified on the command-line and update the DNS with it. I chose a 120 second timeout because i will sometimes change IP address often and because the system doesn’t get enough hits for anyone to care about DNS caching.

#!/bin/bash
set -e
IP=$(ip addr list $1|sed -n -e "s/\/.*$//" -e "s/^.*inet //p")
nsupdate -y foo:XXXXXXXX << END
update delete play.dyn.coker.com.au A
update add play.dyn.coker.com.au 120 A $IP
send
END

Update

It is supposed to be possible to use the -k option to nsupdate to specify a file containing the key. Joey’s comment gives some information on how to get it working (it sounds like it’s buggy).

rhesa pointed out another way of doing it, so I’ve now got a script like the following in production which solves the security issue (as long as the script is mode 0700) and avoids using other files.

#!/bin/bash
set -e
IP=$(ip addr list $1|sed -n -e "s/\/.*$//" -e "s/^.*inet //p")
nsupdate << END
key foo XXXXXXXX
update delete play.dyn.coker.com.au A
update add play.dyn.coker.com.au 120 A $IP
send
END

Update

Added a reference to the tsig-keygen command for newer bind.

7 comments to Dynamic DNS

  • I dunno about you, but -k works perfectly for me. This is a highly abridged version of what I’ve been running in my crontab for several years now:

    IP_ADDR=$(ssh root@192.168.0.1 /bin/ip addr | grep ‘pppoe-wan’ | grep ‘inet’ | awk ‘{print $2}’)
    $ cat <<EOF | nsupdate -k /etc/bind/Ksome_secret_key.private && echo “done”
    server azaroth.sunriseroad.net.
    update delete glenstorm.sunriseroad.net. A
    update add glenstorm.sunriseroad.net. 120 A $IP_ADDR
    send
    EOF

    Although as of last week I now have a static IP, so I have the opposite experience — the joy of never having to do dynamic DNS again.

  • Hi Russell,

    I actually run DHCP and BIND at home with a dynamic DNS setup. I was hit by your same predicament, I wanted to modify and add hosts/delete hosts that were not DHCP’d. So I wrote a python wrapper around the dnspython library.

    https://github.com/jforman/python-ddns

    I am not sure if you are partial to shell scripts, Perl or what not, but perhaps my beginning of work might fit what you are looking for.

    Cheers,
    Jeff

  • I have some experience with nsupdate as we use it to set up DNS for Branchable.com.
    The trick with nsupdate -k is that dnssec-keygen generates two key files, and nsupdate wants both — and it looks in the current directory for the implicitly specified one. I think it takes the basename of the explicitly specified one.
    We worked around this by doing a chdir to the directory containing the keys before calling nsupdate.

  • rhesa

    i use key {name} {secret} in the input to nsupdate, rather than the -y or -k switches. Don’t know if that’s more or less secure, but at least it works and doesn’t show up in ps.

  • foo

    /var/cache is the wrong place for that file.

  • etbe

    Jeremy and Jeffrey: Thanks for the suggestions.

    Joey and rhesa: Thanks for the information about how to solve this. I’ve updated the post and implemented rhesa’s suggestion as it works better with my configuration. Probably for a larger more complex configuration the -k option would have more benefits.

  • etbe

    foo: Correct, but it does work best there with the current SE Linux policy. /var/cache would be a bad idea for a zone that really matters, but for a zone that has relatively unimportant stuff it’s not such a big deal.

    We should probably have some standard directory under /etc/bind for the dynamically updatable zone files.