Dyn, the company behind the widely known dynamic DNS service DynDNS, will shut down it’s free service effectively May 7th, 2014. Of course there are plenty of other free or freemium services out there, but history will repeat itself and these services will vanish over time, or change their business modell.
A while ago I spent an afternoon to implement my own dynamic DNS service.
What’s required:
- A DNS server which understands updates, I’m using Bind 9 here
- A domain (just
domain
in this blog post) - Optional: a webserver (for the “what is my IP” service)
For my case, I created an updates
subdomain in my domain, and pointed it to my DNS server, from /etc/bind/named.conf.local
:
zone "updates.mydomain" {
type master;
file "/etc/bind/updates.mydomain.zone";
};
In the zone for domain
, I created a new origin entry and pointed it back to my nameserver:
; dynamic ip addresses
$ORIGIN updates.domain.
@ IN NS my.nameserver.
Finally, the zone file for the updates
subdomain:
$TTL 86400
$ORIGIN updates.domain.
@ IN SOA my.nameserver. postmaster.domain. (
2013090907 ; Serial
2h ; Refresh
15M ; Retry
604800 ; Expire
2h ) ; Minimum
IN NS my.nameserver.
; dynamic ip addresses
$ORIGIN entry1.updates.domain.
@ IN NS my.nameserver.
$ORIGIN entry2.updates.domain.
@ IN NS my.nameserver.
I define two entries for host entry1
and entry2
, and point them back to my nameserver. The reason for this: I can configure extra keys for each zone, to allow proper authentication and encryption. From /etc/bind/named.conf.local
:
zone "entry1.updates.domain" {
type master;
file "/etc/bind/dynamic-updates/entry1.updates.domain.zone";
allow-transfer {
key "entry1-transfer";
};
allow-update {
key "entry1-transfer";
};
};
Please move the zone into a separate file, this file and the directory must be writable by bind (hence I moved these zones into a separate directory: /etc/bind/dynamic-updates
). The initial zone for entry1
:
$ORIGIN .
$TTL 86400 ; 1 day
entry1.updates.domain IN SOA my.nameserver. postmaster.domain. (
2013091050 ; serial
7200 ; refresh (2 hours)
900 ; retry (15 minutes)
604800 ; expire (1 week)
7200 ; minimum (2 hours)
)
NS my.nameserver.
$TTL 60 ; 1 minute
A 1.2.3.4
Once you submit updates to this zone, bind will write these changes into a .jnl
file in the same directory.
Ok, what about the entry1-transfer
key? I define this key in /etc/bind/entry1-transfer.key
:
key "entry1-transfer" {
algorithm hmac-md5;
secret "put secret key here";
};
And the line is included in /etc/bind/named.conf.local
:
include "/etc/bind/entry1-transfer.key";
The key can be created using dnssec-keygen
:
dnssec-keygen -a HMAC-MD5 -b 512 -n USER entry.updates.domain
This creates two files in the current directory, the filenames are a bit cryptic, but one file ends on .key
(contains the public key), the other one with .private
(contains the private key). For HMAC-MD5
the public and the private key are the same.
Ok, enough server configuration - what about the client?
The client must use the same key - that’s the reason why I generate different keys for each client, and let every client only authenticate it’s own subdomain. For clients without nsupdate
see this posting. To make things more easy and exchangeable I wrote a small Perl script (dns-update.pl
) which can use the very same key file as input, and update the zone on the DNS server:
|
|
This script is called with three parameters:
- The key file (same file used in the DNS server)
- The hostname which shall be updated, as example: entry1.updates.domain
- The new IP address
Example:
|
|
To make things more easy for me, I also wrote a small script which is hosted on one of my servers, which returns the current IP address of the website visitor. My cron job for automatic updates of the zone looks as follow:
0-59/15 * * * * root /root/dns-update.pl /etc/bind/entry1-transfer.key entry1.updates.domain `lynx -source -dump http://updates.domain/my_ip.php` > /dev/null 2> /dev/null
Voila, every 15 minutes the zone is updated (if the IP is changed).