A slightly less simple Dynamic DNS server

So earlier in the week, I posted up the source of ddserver.pl, which is a Dynamic DNS server which I’m using to track a machine with a rapidly-changing IP out on the internet[1].

I’ve updated that post with a bit more detail about how to use it, so check it again if you tried to install it and get stymied by the lack of documentation supplied (it now has some sample apache config and the contents of the reload cronjob).

This entry is about some additional features that make the script slightly less simple, but slightly more useful, if this is the sort of thing that you think is useful.

Round-tripping zone files

So if you run ddserver.pl from a clean installation, and create a new A record for your computer called ‘test‘ in the ‘example.com‘ zone, you’ll get a bind file created in /etc/bind/dynamic/db.example.com that looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
;
; BIND data file for example.com zone
; this file is automatically generated by ddserver.pl.
;
$TTL    604800
@       IN      SOA     ns1.example.com. soaContact.example.com (
                2013071001         ; (YYYYMMDDNN) Serial INCREMENT THIS EVERY TIME FILE CHANGES
                                   ; (triggers zone transfer if secondary configured)
                      7200         ; Refresh
                     86400         ; Retry
                   2419200         ; Expire
                      7200 )       ; Negative Cache TTL
;
 
example.com.         7200    IN      NS      ns1.example.com.
example.com.         7200    IN      NS      ns2.example.com.
;
; === START OF GENERATED CONFIGURATION
test    300     IN      A       1.2.3.4
; === END OF GENERATED CONFIGURATION
 
; if you are manually modifying this file, anything added before or after
; the automatically generated markers above should be preserved.
; Except for the serial number, of course, because that's special.

If you’re running your own bind server, you probably have a raft of other DNS records for your domain, which are more fixed than the records being maintained by ddserver.pl.

ddserver.pl supports maintaining these manually by modifying the contents of the zone file directly, since only the text between the

; === START OF GENERATED CONFIGURATION

and

; === END OF GENERATED CONFIGURATION

markers is modified by the script during subsequent updates.

This means that other entries, formatting and comments are kept intact whenever the ddserver.pl script reads and writes the file, meaning you can roundtrip the file without discarding the non-machine-readable information.

Which is nice if your zone files are filled with crap that needs a reference to a change request someone raised in 1998.

Templates

While this is all well and good, manual changes are still, well, manual.

The templating mechanism allows you to deploy changes using whatever system you use to deploy configuration file changes (such as puppet, chef, sprinkle, stackful, salt[2], or my personal favourite, vmaint), and have those changes merged into the dynamic data already stored in the file.

Say I wanted to add a TXT record to the domain to prove to google analytics that I own the thing and wanted to make graphs showing the 30 hits or so a month that I get. I could either update the /etc/bind/dynamic/db.example.com file directly, restart bind, and this would work, or I could create/update a template file (in /etc/bind/dynamic/db.example.com.template) which looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
;
; BIND data file for example.com zone
; this file is automatically generated by ddserver.pl and db.example.com.template.
;
$TTL    604800
@       IN      SOA     ns1.example.com. soaContact.example.com (
                2013071001         ; (YYYYMMDDNN) Serial INCREMENT THIS EVERY TIME FILE CHANGES
                                   ; (triggers zone transfer if secondary configured)
                      7200         ; Refresh
                     86400         ; Retry
                   2419200         ; Expire
                      7200 )       ; Negative Cache TTL
;
 
example.com.         7200    IN      NS      ns1.example.com.
example.com.         7200    IN      NS      ns2.example.com.
example.com.         7200    IN      TXT     "google-site-verification=cLF9jhJ75wc3ITzLVr6tAH4ipXFX9_KqbETj3HgMoR8"
;
; === START OF GENERATED CONFIGURATION
<%= $bindrecords %>
; === END OF GENERATED CONFIGURATION
 
; if you are manually modifying this file, anything added before or after
; the automatically generated markers above should be preserved.
; although I recommend that you use the template file instead, because that
; will take precedence the next time someone merges this thing

perform a NOCHG update using the commandline with the forcetemplate parameter set to yes, and the zonefile would be updated to reflect the template, whilst still preserving the dynamic records maintained by ddserver.pl; i.e. something like:

1
2
3
4
5
perl /var/www/dyndns.example.com/cgi-bin/ddserver.pl /update? \
  username=admin password=admin \
  hostname=dummy.example.com \
  myip=NOCHG \
  forcetemplate=yes

The upshot of all this is that you can now ensure that the non-dynamic portions of the zonefile can be tracked via source control and deployed using some vaguely deterministic manner.

Propagating updates

In the example ddserver.json configuration file (which in this example would be located at /var/www/dyndns.example.com/cgi-bin/ddserver.json) contained in the previous blog post, the nameservers block appears as:

78
79
80
81
82
    /* the nameservers for these zones (array) */
    "nameservers" : [ 
        "ns1.example.com",
        "ns2.example.com"
    ]

If you despise axfr for some reason, this can be enhanced to include a URL for each nameserver, thusly:

78
79
80
81
82
    /* the nameservers for these zones (hash) */
    "nameservers" : {
      "ns1.example.com" : { "dyndns" : "http://ns1.example.com/ddserver.pl" },
      "ns2.example.com" : { "dyndns" : "http://ns2.example.com/ddserver.pl" }
    }

which will cause the ddserver.pl script to send any changes through to all other nameservers for your organisation. (The ddserver.pl script needs to be set up on these nameservers for this to work).

You’d probably use HTTPS as well, I’d imagine.

To prevent the script from sending changes to itself, you will need an extra configuration file in the directory holding the ddserver.pl script called ddserver-hostname.txt, containing the host name of that nameserver; i.e. something like this in /var/www/dyndns.example.com/cgi-bin/ddserver-hostname.txt:

1
ns1.example.com

You’d have thought that the script could work out it’s own hostname by itself, and you’d probably be right, but you’d also sometimes be wrong.

Keeping it in a separate file also means that every nameserver can have identical ddserver.json configuration files as well, which is nice.

If you don’t have multiple nameservers, then ignore this whole section.

Anything else?

You should probably be aware that ddserver.pl:

  • doesn’t perform locking around file updates
  • doesn’t yet support support dynamic AAAA records if you’re using ipv6
  • doesn’t support the mx parameters of the dyndns update protocol
  • doesn’t actually support the zoneedit update protocol properly either
  • hasn’t really been tested at all, and probably therefore has bugs and as-yet-undiscovered security holes like you wouldn’t believe
  • during normal execution, and more importantly, when it breaks, there’s a logfile created (in the sample ddserver.json configuration file this is set to /var/log/ddserver.log).

    If there’s no logfile, it probably means the user that the webserver is running under (usually www-data) doesn’t have access to the file, so you’d need to do something like this:

    sudo /bin/bash -c 'touch /var/log/ddserver.log; chown www-data:www-data /var/log/ddserver.log; chmod 664 /var/log/ddserver.log'

    Which also reminds me… your /etc/bind/dynamic directory should have group write permissions, and you should chgrp the folder to www-data, so something like this:

    sudo /bin/bash -c 'mkdir /etc/bind/dynamic; chown bind:www-data /etc/bind/dynamic; chmod 775 /etc/bind/dynamic'

    , although for some reason this isn’t what I’ve got running here.

    I may update this bit later, some time after I convert this all into a debian package.

    Possibly.

    It’s very long for a bullet point.

Also remember that unless you remembered to shorten the TTL (time-to-live) value just before you changed the IP address, which you can’t actually do unless you know when your IP is going to change or have a time machine, then any changes made to IP addresses will take a while to be visible to the outside world. I think the default is something like five minutes.

So is that it?

If you download any time in the next ten years, I’ll throw in a free Dynamic DNS client for Windows.

[1] well, it changes about once every 24 hours, but that’s more rapid than, say, not at all.
[2] bonus marks if you can work out why this site is called randomnoun.

2 Comments

Add a Comment

Your email address will not be published. Required fields are marked *