DEV Community

Alexander Haas
Alexander Haas

Posted on

Set up your own DDNS Server with bind9 and go

What is DDNS used for?

Dynamic DNS (DDNS) services allow you to reach networks that aren't supplied with a static IP by their ISP.
To reliably communicate with for example your home network, the DDNS-provider usually supplies you with a (sub)domain running an ordinary DNS-server and an update-webservice. A client in the internet can then reach a home network simply by connecting to the supplied DDNS-url. To update the DNS-server the update-webservice url is placed in your router or is manually run from a device inside the home network.

See Dynamic DNS for a more in depth explanation.

Purpose of this tutorial

There are many DDNS tutorials out there. But I found it really difficult to find one thats simple, coherent and up-to-date. I would like to summarize my findings in this tutorial.
I will be using code snippets inspired by other tutorials but I will try to credit all the authors. If I make a mistake at giving credit feel free to contact me.

How to implement your own DDNS configuration

Disclaimer: I'm not an IT-security expert. If you find any vulnerabilities in this configuration please contact me!

DDNS services often cost as much as a separate linux virtual machine server. So if you already have one why not set it up by yourself.

This tutorial is only for linux machines. In particular I did this on a debian 11 VM.

I will be using bind9 as it's one of the most popular DNS Servers. Install it using your favourite package manager like apt:

# apt install bind9

If you intend to use go for the update server install it using the official instructions.

DNS and firewall configuration

Go to your Domain/DNS-provider and edit the DNS-server settings as follows:

home.ddns.example.com 3600 IN NS ns.example.com
Enter fullscreen mode Exit fullscreen mode

home.ddns.example.com will be the url under which you will be able to make a connection to you home network.
I'm using the subdomain ddns because your configuration will be more readable if intend to add multiple home networks.

ns.example.com 3600 IN A <Your Server IP>
Enter fullscreen mode Exit fullscreen mode

This is the nameserver under which your DNS server runs. This will serve the A-entry (IP) for your home network.
Enter the static server IP of your VM.

update-ddns.example.com 3600 IN A <Your Server IP>
Enter fullscreen mode Exit fullscreen mode

This entry will be the webservice that allows you to update the DNS entry for your home network.

Make sure to allow access to port 53-udp for DNS and port 80/443-tcp (http/https) for updating the "A"-entry in your DNS server in your firewall.

Bind9 configuration

The following is a relatively ordinary DNS configuration appart of a few exceptions.

At this point others often use the utility dnssec-keygen to generate a symmetric key. In my version of this tool the hmac algorithms weren't included. So I used the tool tsig-keygen.

To generate a symmetric key use:

# tsig-keygen ddnskey >> /secrets/ddnskey
key "ddnskey" {
    algorithm hmac-sha256;
    secret "BASE64=";
};
Enter fullscreen mode Exit fullscreen mode

ddnskey is the name of the key and can be chosen as you like. This key (the value next to secret) is the one you pass to a DNS update-tool so it can update the DNS entries.
More about this later.
Make sure to edit the permissions to that file so that the user which later runs the webservice has read access to it.

Next edit /etc/bind/named.conf to configure bind9. This file may be at a different location if you use something else than debian.

Add the following contents to the file:

key "ddnskey" {
        algorithm hmac-sha256;
        secret "BASE64=";
};

zone home.ddns.example.com. {
    type master;
    file "/var/lib/bind/db.home.example.com";
    allow-update { key ddnskey; };
};
Enter fullscreen mode Exit fullscreen mode

The first entry is the key output we just generated. The second entry is the definition of a new DNS zone. The zone still has to be defined in the file specified next to file. I had permission issues when creating the file in the /etc/bind folder as bind is executed with it's own user. zone-files usually have to go into the folder specified above.

Let's create that file:

$ORIGIN .
$TTL 200        ; 3 minutes 20 seconds
home.ddns.example.com IN SOA ns.example.com. alex.example.com. (
                                6          ; serial
                                500        ; refresh (8 minutes 20 seconds)
                                500        ; retry (8 minutes 20 seconds)
                                86400      ; expire (1 day)
                                500        ; minimum (8 minutes 20 seconds)
                                )
                        NS      ns.example.com.
                        A       1.2.3.4
Enter fullscreen mode Exit fullscreen mode

I used the zone file here as a base template. Zone files aren't the main subject of this tutorial but they basically represent the actual DNS-Server configuration for that specific zone. This information can be accessed by querying the server. They have strict formatting rules. I found a web tool that makes it easier to work with them.

The important parts are that the TTL should be relatively short because the IP might be very short lived. In the A entry just enter anything as this will be overwritten by our update-script and will hopefully always represent the IP of the home network.

Replace alex.example.com. with your email.

Reload the bind9 service with:

# systemctl restart bind9

and test with

nsupdate -k /secrets/ddnskey 
> server ns.example.com
> update delete home.ddns.example.com A
> update add home.ddns.example.com 200 A 1.2.3.4
> send
Enter fullscreen mode Exit fullscreen mode

This does the following:

  1. Connect to specified nameserver
  2. Delete the previous A-entry for your home network Ip
  3. Set a new A-entry
  4. Submit

If you have the dnsutils package installed you can check if the A entry was updated with

dig +trace home.ddns.example.com @8.8.8.8
Enter fullscreen mode Exit fullscreen mode

I found a script in another tutorial that I slightly modified but I unfortunately didn't find the authors again. Please let me know if you know the author and I will credit them.

The following is the aforementioned script:

KEYFILE=/secrets/ddnskey
NAMESERVER="ns.example.com"
ZONE="home.ddns.example.com"
TTL="200"

if [ -z "$1" ]
then
  IPADDR="`curl -s ifconfig.me`"
else
  IPADDR="$1"
fi

(
  echo "server $NAMESERVER"
  echo "update delete $ZONE A"
  echo "update add $ZONE $TTL A $IPADDR"
  echo "send"
) | nsupdate -k /secrets/ddnskey

# EOF
Enter fullscreen mode Exit fullscreen mode

If you want to update your server per http(s): I've written a go server for this purpose.

Top comments (0)