It’s almost 2013 and most ISPs still don’t care about IPv6 – heavy sigh. They prefer to give you a dynamic IPv4 which changes every 24 hours. (By the way, some of them are even more evil and dare to route you through a 10/8 network!)

That means if you want to reach your home connection remotely, you’re screwed. Dynamic DNS providers like DynDNS help(ed) you with that but they seem to turn into paid services faster than mushrooms grow after the rain. And I don’t want to pay for this kind of service that should come built-in with my ISP subscription IMO.

Fortunately, if you happen to have your own DNS server, here’s a solution: TSIG.

Server configuration

Here, I’m using nsupdate that comes with BIND. Note that you only need your primary DNS server to support TSIG since the primary server will automatically propagate updates to the secondaries.

TSIG uses shared secret keys to authenticate with the server. Let’s say I want to update a resource record named toto.infertux.com. to match my current IP.

Generate the key pair:

dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST toto.infertux.com.

I’m using SHA512 since you don’t want to use the default HMAC-MD5 – quoting Wikipedia:

RFC 2845, which defines TSIG, specifies only one allowed hashing function HMAC-MD5, which is no longer considered to be highly secure. As of 2006, proposals are being circulated to allow RFC 3174 Secure Hash Algorithm (SHA1) hashing to replace MD5. The 160-bit digest generated by SHA1 should be more secure than the 128-bit digest generated by MD5.

This will generate the key pair, i.e. the public key named Ktoto.infertux.com.+NNN+NNNNN.key and the private key named Ktoto.infertux.com.+NNN+NNNNN.private.

Now we need to tell BIND about our new key.

I’m creating a new file /etc/bind/named.conf.keys and including it from /etc/bind/named.conf to keep things tidy.

include "named.conf.keys";

And here’s the contents of named.conf.keys:

key toto.infertux.com. {
    algorithm HMAC-SHA512;
    secret "dummy+x8vikzlFlg2utQLo4Fyv3x+Ozd4+TgnPmsey+9pcyowrxFopr+Z8WPYqwP+WzPt+Vp9aVY3Ip2NZD7Jg==";
};

Following the principle of least required privileges, I’m allowing the client to update only its attached resource record by using update-policy:

zone "infertux.com" {
    ...
    update-policy {
        grant toto.infertux.com. name toto.infertux.com. A;
    };
}

Double check and reload BIND:

$ named-checkconf
$ /etc/init.d/bind9 reload

Client configuration

First of, transfer (securely!) both keys from your server to the client.

Then, make sure the nsupdate command is available (package dnsutils on Debian).

Finally, you need a cron task to send IP updates to your server. Here’s what I’ve done:

# /etc/cron.d/update-dyndns
*/5 * * * * root /usr/local/bin/update-dyndns
#!/bin/bash -eu

# fetch our current IP
MY_IP="$(curl -s ifconfig.me)"

# make sure we don't get some garbage
echo -n "$MY_IP" | grep -qE '\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\b'

# update the RR
cat | nsupdate -v -k Ktoto.+NNN+NNNNN.private <<COMMANDS
server ns1.infertux.com
zone infertux.com
update delete toto.infertux.com. A
update add toto.infertux.com. 300 A $MY_IP
show
send
COMMANDS

Note that each new IP will increment the SOA serial. BIND is smart enough to not increment it if the IP has not changed. So, assuming your IP changes once a day, that means your serial will increment by one every day.

Test it

$ dig @ns1.infertux.com toto.infertux.com +noall +answer | tail -1
toto.infertux.com.        300   IN      A       MY.FUCKING.DYNAMIC.IPv4

Done! Damn you ISP!