This post is based on my previous blog post, Updating DNS Entries (with nsupdate or alternative implementations) – Run Your Own DDNS, and on Cédric Félizard's post "Your Own Dynamic DNS".
Running your own DynDNS (DDNS) server is easy when you have your own domain registered and an own Linux server up and running. A .de
domain, for example, costs only about 6 EUR per year if you register at an inexpensive domain hoster in Germany. Let's say you have registered example.com. Now you can use a subdomain such as d.example.com to contain all your dynamic IPs. An individual host could then be named something like eric.d.example.com
. The advantage of using a subdomain .d.example.com
instead of example.com
itself is that you can refer the authority for this subdomain to your own server while the upper zone example.com
can still be managed by the DNS hoster.
The following sections will describe how to set this up in detail.
Start by installing the DNS server BIND9 on your Debian/Ubuntu server via apt-get update && apt-get install bind9
.
Next, set up /etc/bind/named.conf.local
with your local zone.
// ---***--- Own DynDNS
include "/etc/bind/ddns-keys.conf";
zone "d.example.com" IN {
type master;
file "/var/lib/bind/db.d.example.com";
// for Apple OS X 10.8 "dynamic global hostname":
//allow-update { key mac.d.example.com; };
update-policy {
grant *.d.example.com. selfsub d.example.com. A AAAA TXT;
//grant *.d.example.com. self d.example.com. A AAAA TXT;
//grant sb.d.example.com. name sb.d.example.com. A AAAA TXT;
//grant sb.d.example.com. subdomain d.example.com.;
};
notify no;
};
/var/lib/bind/db.d.example.com
$ORIGIN .
$TTL 10 ; 10 seconds
d.example.com. IN SOA ns1.d.example.com. hostmaster.example.com. (
2014080101 ; serial
120 ; refresh (2 minutes)
120 ; retry (2 minutes)
2419200 ; expire(4 weeks)
120 ; minimum (2 minutes)
)
NS ns1.d.example.com.
NS ns2.d.example.com.
$ORIGIN d.example.com.
$TTL 30 ; 30 seconds
ipv4 A 38.68.84.19
ipv4v6 A 38.68.84.19
AAAA 2001:0db8::2
ipv6 AAAA 2001:0db8::2
ns1 A 38.68.84.19
ns2 AAAA 2001:0db8::2
Fill /etc/bind/ddns-keys.conf
with all the keys you want to support. Generate them with dnssec-keygen -a HMAC-SHA512 -b 512 -n HOST sb.d.example.com.
(to be found in the .key file):
key "sb.d.example.com." {
algorithm HMAC-SHA512;
secret "5f2It/b/7wF0QmnFQ2DiTVIF6Z/cN8a8dxyIf149my61ihkgNHqn4KjG eTlbYQ7CKVskV4lmV1R0M1xAPj4Ipg==";
};
Double check and reload BIND:
named-checkconf
named-checkzone d.example.com /var/lib/bind/db.d.example.com
/etc/init.d/bind9 reload
Debug with
tail -f /var/log/syslog
If your DDNS server was running for some time and receiving dynamic updates, you will have a /var/lib/bind/db.d.example.com.jnl file along with the /var/lib/bind/db.d.example.com file. So change the zone file with care only after syncing in the journal file's changes. On Bind 9.9+ you can do this with rndc sync -clean
, see here. A complete shutdown and restart of Bind should also have the same effect.
Checks
To check the whole chain of DNS entries, do:
- Ask for the nameserver for example.com using
dig example.com ns
- Use one of the returned nameservers (
ns.inwx.de.
) and ask it to tell you about the name servers for the zone d.example.com:dig @ns.inwx.de. d.example.com ns
. - Use one of the returned nameservers (
ns1.d.example.com.
) and ask it to tell you about the zone d.example.com:dig @ns1.d.example.com. d.example.com soa
. - Ask that server for your dynamic domain name using
dig @ns1.d.example.com. sb.d.example.com. a
. - Check if the server allows/refuses to do a zone transfer from different machines:
dig @ns1.d.example.com. d.example.com. axfr
Also disable DNS-Rebind protection for the domain d.example.com
if you use a Fritz!Box router (found here):
DNS-Rebind-Protection
FRITZ!Box suppresses DNS answers that point to IP addresses in your home network (DNS-Rebind-Protection). Provide a list of domain names here to be excluded from this protection.
(You find this setting at Heimnetz/Netzwerk -> Netzwerkeinstellungen.)
If you ever need to flush the cached entries of your BIND9 server, run rndc flush
.
If you're interested in checking the times your responses are being cached and the performance of your DNS server, you may want to take a look at this forum post.
FQDN=example.com.
for i in {1..30}; do echo $FQDN; done | xargs -I^ -P10 dig ^ | grep time | awk /time/'{sum+=$4} END { print "Average query = ",sum/NR,"ms"}'
On my Raspberry Pi running Bind9, an initial query (uncached entry) takes 244 ms. A subsequent cached answer takes 11 ms. On a different virtual server with a little more processing power, the time to resolve the address was 38 ms initially and 5 ms for subsequent cached responses. It seems like the processing power of the RPi is not that good for a fast DNS server.
Here is a table of my speed tests to resolve A RRs of different FQDNs:
Name Server | FQDN | Client Connection | Initial time | Subsequent time | Ping time |
---|---|---|---|---|---|
RPi | google.at | 50/10 Mbit | 192 ms | 34 ms | 32.9 ms |
RPi | google.at | Gigabit | 244 ms | 11 ms | 4.97 ms |
vServer | google.at | 50/10 Mbit | 60 ms | 27 ms | 25.2 ms |
vServer | google.at | Gigabit | 38 ms | 5 ms | 4.72 ms |
RPi | bundesregierung.de | 50/10 Mbit | 91 ms | 35 ms | 32.1 ms |
vServer | bundesregierung.de | 50/10 Mbit | 49 ms | 27 ms | 24.9 ms |
RPi | golem.de | 50/10 Mbit | 62 ms | 36 ms | 31.5 ms |
vServer | golem.de | 50/10 Mbit | 47 ms | 26 ms | 23.0 ms |
8.8.8.8 | twitter.com | Gigabit | 12 ms | 12 ms | 11.9 ms |
RPi | twitter.com | Gigabit | 266 ms | 12 ms | 9.38 ms |
vServer | twitter.com | Gigabit | 11 ms | 5 ms | 4.01 ms |
8.8.8.8 | denic.de | Gigabit | 12 ms | 12 ms | 11.8 ms |
RPi | denic.de | Gigabit | 57 ms | 11 ms | 9.50 ms |
vServer | denic.de | Gigabit | 17 ms | 5 ms | 4.05 ms |
RPi | blog.philippklaus.de | Gigabit | 873 ms | 11 ms | 9.50 ms |
8.8.8.8 | ipv4.[...] | 50/10 Mbit | 97 ms | 34 ms | 32.3 ms |
RPi | ipv4.[...] | 50/10 Mbit | 35 ms | 35 ms | 32.8 ms |
vServer | ipv4.[...] | 50/10 Mbit | 27 ms | 27 ms | 22.7 ms |
The ping time is the avg of four ping rounds to the DNS server in question. The Server connection speed of the RPi and of the vServer was 100 Mbit in all tests.
The last three test entries contain a query for a domain being administered by the RPi&vServer. Thus the advantage of Google's 8.8.8.8 disappears.
Upating the entries
This can be done with nsupdate or with my Python based wrapper for nsupdate: ddns.py. See my previous blog post, Updating DNS Entries, for more details.
The above software is Python based. If you want to do update entries for low performance hardware such as routers running OpenWrt, you don't want to use Python but directly use nsupdate in a shell script. For more details, read the article DIY Dynamic DNS with OpenWrt & BIND and check out this startup script.
On your client you need the key files (.key
and .private
) generated in the dnssec-keygen
step above.
Running your own What's My IP HTTP service
If you want a laptop or a device connected to the net via a consumer provider with changing IPs to update the DNS entries to their current external IP, you need to find out about that external IP first. To do so, there are a couple of services running on the web but you can also run your own.
I wrote a minimal Python-based server for this purpose, it's called WhatsMyIP and you can find it on Github.
Setting it up is described in the blog post Arch Linux – Deploying a Pure Python Web App.
The script ddns.py, which was introduced in the section above, makes use of my deployment of that web app.
Setting up a secondary DNS on Arch Linux
pacman -S bind dnsutils
Add the following zone to /etc/named.conf
:
// uncomment
listen-on-v6 { any; };
// you might want to allow recursion for your network:
allow-recursion { 141.2.0.0/16; 2001:a38::/32; };
// add the slave-zone:
zone "d.example.com" IN {
type slave;
masters { 38.68.84.19; };
file "db.d.example.com";
};
Check your configuration with
named-checkconf
Start BIND9 with:
systemctl start named
Check that everything works as expected using systemctl status named
, less /var/log/named.log
, and by testing if the server responds to DNS requests.
If everything works as expected, let BIND9 be started automatically on system boot:
systemctl enable named
Alternatives to Bind / Dynamic DNS server For The Home Network
Just for the sake of completeness, here are some other possibilities that I found on the web. I prefer Bind though:
- Python Modular DNS Server ← very old, handles DNS on its own, not so good!
- Simple DNS server (UDP and TCP) in Python using dnslib.py
Also check my older blog post Updating DNS Entries (with nsupdate or alternative implementations) – Run Your Own DDNS.