How to setup a simple self-hosted dynamic DNS server

I have a small internal network of physical machines running hypervisors, which in turn run several KVM Ubuntu virtual machines. How would I setup an internal dynamic DNS server so that when I run a script to create a new virtual machine, that VM could automatically register itself in the DNS server?

Bind seems to be the standard DNS server for Linux, but it seems designed for a much more "static" DNS model. Dynamically updating this would require a complicated script that would have to SSH into the DNS server, edit configuration files, and then restart the server. This doesn't seem like a very elegant solution. Are there better options?

I saw a similar question, although they're asking for a solution for a public setting on Amazon. My servers are entirely private, and I don't want to rely on an external VM host or Dynamic DNS provider.


Actually Bind is capable of doing dynamic dns updates through RFC 2136 standard messages. Using the nsupdate tool and the right configuration (not terribly hard but not completely trivial either).

Your options for authenticating these update messages are 1) allowing only certain IPs to send update messages 2a) TSIG symmetrical encryption 2b) SIG(0) based public key cryptography or a combination of 1 and 2*. Among other places instructions can be found here

In implementing this personally I found the biggest problem was with giving named sufficient write privileges in /var/named. It needs to be able to create files in the directory as well as having write permissions to the files for the dynamic zones.

Sig(0) keys are generated with the dnssec-keygen utility with nametype HOST and keytype KEY. For exmaple (may not be exact)(RSAMD5 covers nearly every bind package):

dnssec-keygen -a RSAMD5 -b 1024 -n HOST -f Key host.domain.tld

The resulting .key file will be added to your zonefile, the .private will be specified on the commandline with nsupdate.


I did setup bind with dynamic updates via nsupdate (as CarbonLifeForm described), and combined this with a simple Perl-Script which gets called via encrypted HTTPS REST-Request, checks subdomain/password combination and then calls nsupdate with the IP of the request.

Example Update-Call from any client (which will automatically set the appropriate subdomain to the public IP of that client)

Just visit with any Browser (in this example https was made available via Port 12345):

https://ddns.YOURDOMAIN.net:12345/update-my-ip.pl?subdomain=***&password=***

or via the command-line (e.g. for updates every 15 minutes via cronjob):

www-browser -dump 'https://ddns.YOURDOMAIN.net:12345/update-my-ip.pl?subdomain=***&password=***'

Here is the perl-script update-my-ip.pl:

#!/usr/bin/perl
use strict;
use CGI;
use Digest::SHA1 qw(sha1_hex);

my %accounts = (
# create via: sha1sum, then type password, then press Ctrl-D to calculate checksum (echo     with pipe gives wrong result!)
# update via: www-browser -dump 'https://ddns.YOURDOMAIN.net:12345/update-my-ip.pl?    subdomain=***&password=***'
    'YOURSUBDOMAIN1' =>     '93485720985720394853452345235-fake-sha1-checksum1',
    'YOURSUBDOMAIN2' =>     '93485720985720394853452345235-fake-sha1-checksum2', 
    'YOURSUBDOMAIN3' =>     '93485720985720394853452345235-fake-sha1-checksum3',
);
my $cgi = new CGI;

print "Content-type: text/html\n\n";
my $subdomain = $cgi->param('subdomain');
my $password = $cgi->param('password');
if( !$password || !$subdomain || length($password) <= 3 || length($subdomain) <= 1)
{
    print "ERROR\n";
    exit 0;
}
my $sha1 = sha1_hex($password);
my $ip = $ENV{'REMOTE_ADDR'};
my $should_be_sha1 = $accounts{$subdomain};
if( $sha1 && length($sha1) > 10 && $sha1 eq $should_be_sha1)
{
    print "START: ddns.YOURDOMAIN.net DNS updating <a href=\"http://$subdomain.YOURDOMAIN.net\">http://$subdomain.YOURDOMAIN.net</a> (<a href=\"https://$subdomain.YOURDOMAIN.net\">SSL</a>) to IP <a href=\"http://$ip\">$ip</a>.<br/>\n";
    my $out = `echo "update delete $subdomain.YOURDOMAIN.net A\n\n" | nsupdate 2>&1`;
    print "STEP1 $out<br/>\n";
    $out = `echo "update add $subdomain.YOURDOMAIN.net 60 A $ip\n\n" | nsupdate 2>&1`;
    print "STEP2 $out<br/>\n";
    print "DONE<br/>\n";
}
else
{
    print "ERROR\n";
}

Since your servers are all private I'm assuming your not wanting to publish the IP addresses to the greater internet. This actually simplifies things.

I know for my small networks I've had success with dnsmasq, a dead simple DNS and DHCP server. It should allow clients to add themselves to the DNS server when they get a DHCP lease.

Further reading: https://superuser.com/questions/312515/dnsmasq-without-altering-etc-hosts-file-manually


How are IP addresses assigned to your VMs? Assuming DHCP, you can configure it to dynamically update BIND. Here's the Ubuntu documentation on dhcpd.conf setup.

http://manpages.ubuntu.com/manpages/lucid/man5/dhcpd.conf.5.html#contenttoc14

On the BIND side, named.conf will specify the local zone to be updated dynamically. You would use keys, as outlined by user115014.