Using Dynamic DNS for your dynamic IP micro-howto


Table of Contents

1. About this document
2. Introduction
3. Dynamic dns in bind
Tests with bind 9
4. Prerequisites
Your own domain name
The nameserver
5. The setup of the nameserver
6. Updating the nameserver information
Script on the server
The script on the client
7. Things you can and cannot use this for
8. References

1. About this document

This document is in the "I should write this down so other people can use it too" category. It works for me which does not mean for one second it should work for you (that's a bit of a disclaimer). If you have anything to add, suggestions how to make this work for different setups then please let me know. Corrections, updates are welcome. I can be mailed at <koos@kzdoos.xs4all.nl>

2. Introduction

In June 2001 I got a subscription with a Dutch cablemodem provider, Casema. They (at that time) gave access using COM21 modems with PPPoE (PPP over Ethernet) as network protocol. All discussions about PPPoE aside, one disadvantage is that my IP address changes with every session. And it's something which looks like 12dyn219.com21.casema.net which is a bit hard to remember anyway. So, I wanted a name in my own domain to point at my cable modem.

3. Dynamic dns in bind

Berkeley Internet Name Daemon (bind) version 8 added the option for "dynamic updates". This adds the option for hosts to update their own information in the nameserver. This could be very interesting but there is no good authentication mechanism (this has been fixed in bind 9). The only mechanism available is setting the IP numbers that can update a dns zone (the entire zone, not just one entry in the zone).

Tests with bind 9

I have tested with bind 9, but only with the option to allow updates from localhost, not with better authentication.

4. Prerequisites

Stuff you need for my solution which I'll explain and describe :

  • Your own domain name. For the rest of this document I will use the canonical example domain example.com.

  • One nameserver on the Internet with a fixed IP which you have ssh access to.

Your own domain name

I decided to make the "dynamic" name a subdomain of the domain name for two reasons:

  • The dynamic stuff wouldn't affect the main zonefile which would therefore be safe from updates.

  • The dynamic stuff could run with exactly one nameserver where the main domain name needs at least two to be accepted.

The nameserver

The nameserver needs to be set up in such a way that I can log in using ssh and run the dns update script and it needs to be on a fixed IP (so I can delegate the cable zone to it and I know where to reach it when the connection comes up). It does not have to be the same nameserver as for the main server.

5. The setup of the nameserver

The main domain is example.com, the dynamic zone is cable.example.com. The zonefile for example.com has a pointer for cable:

cable   IN      NS      ns1.example.com.

I can have cable.example.com point directly at an IP since the 'zone top' can have MX, NS and A records just like any other record. The cable.example.com zone is set up to allow dynamic updates from localhost. The relevant part of named.conf:

zone "cable.example.com" {
        type master;
        file "dynamic/cable.example.com-zone";
        allow-update {localhost;};
        allow-query { any; };
};

The directory dynamic and the file cable.example.com-zone are set up to be writable for the named process (which does not run as root for security reasons).

The special statement is the allow-update statement. The only host that can update this zone is localhost.

The contents of the zonefile is quite simple:

;BIND DUMP V8
$ORIGIN example.com.
cable   3600    IN      SOA     ns1.example.com. koos.example.com. (
                20010661 3600 600 7200 3600 )   ;Cl=3
                3600    IN      NS      ns1.example.com.  ;Cl=3
                3600    IN      MX      10 mx.example.com.       ;Cl=3
                1800    IN      A       213.17.82.219   ;Cl=3

Which is indeed the contents after it has been updated by named. The original contents was:

@           IN  SOA ns1.example.com. koos.example.com. (
            20010661    ; serial
            3600        ; refresh
            600         ; retry
            7200        ; expire
            3600        ; minimum
)
            IN  NS  ns1.example.com.
            IN  MX  10  mx.example.com.
            IN  A   127.0.0.1

which might be a bit more readable for those who have configured nameservers before.

6. Updating the nameserver information

The interesting part is making the updates happen. There are two parts to this, the script to be run on the server and the call to that script on the client side.

Script on the server

This is a Perl script which is an adaptation of the script from Use Dynamic DNS for fun and profit!. I modified it slightly to allow me to update the zone top.

#!/usr/bin/perl -w

use strict;
use Net::DNS;
use vars qw ($zone $name $rr $res $query $update $ans $b);

if ($ARGV[0]){
        $zone="cable.example.com";
        $name="cable.example.com";
        $res = new Net::DNS::Resolver;
        $res->nameservers("127.0.0.1");
        $query = $res->search($name);
        if ($query){
                print "Attempting to remove old entry: $name = ";
                foreach $rr ($query->answer) {
                        next unless $rr->type eq "A";
                        print $rr->address, " ";
                }
                print "\n";
                $update = new Net::DNS::Update($zone);
                # Prerequisite (assumed) is that an A record must already exist.
                $update->push("update",
                        $b = new Net::DNS::RR(Name    => $name,
                                Type => "A",
                                Ttl => 0,
                                Class => "ANY",
                                Rdata => ""));
                $res = new Net::DNS::Resolver;
                $res->nameservers("127.0.0.1");
                $ans = $res->send($update);
                if (defined $ans) {
                        print "Return code: ", $ans->header->rcode, "\n";
                        if ($ans->header->rcode eq "NOERROR") {
                                print "Old entry removed successfully.\n";
                        } else {
                                print "Failed to remove old data!!!\n";
                        }
                } else {
                        printf("Error: %s\n",$res->errorstring);
                        print "Failed to remove old data!!!!\n";
                }
        }
        $update = new Net::DNS::Update($zone);
        # NXRRSET - Prerequisite is that no A records exist for the name.
        $update->push("pre", new Net::DNS::RR(Name  => $name,
            Class => "NONE",
            Type  => "A"));
        # Add one A records for the name.
        $update->push("update", new Net::DNS::RR(Name    => $name, 
            Ttl     => 1800, 
            Type    => "A",
            Address => $ARGV[0]));
        $res = new Net::DNS::Resolver;
        $res->nameservers("127.0.0.1");
        $ans = $res->send($update);

        if (defined $ans) {
                print "Return code: ", $ans->header->rcode, "\n";
                if ($ans->header->rcode eq "NOERROR") {
                        print "Success!\n";
                } else {
                        print "Failure!\n";
                }
        } else {
                printf("Error: %s\n",$res->errorstring);
                print "Failure!\n";
        }
}

This script simply tries to remove the current A record for cable.example.com (which is why the zone needed to be preloaded with an IP) and then loads the new one that was given as argument.

This script is available for download: dyndnsupdate perl script

The script on the client

The client (my system connected to the cablemodem) needs to do the call at the right moment. The right moment being when the PPP connection is up and has an ip number assigned. This is easy since pppd has the option of running the ip-up script when the ip number is assigned. In Debian Linux there is a /etc/ppp/ip-up.d/ directory for scripts to be executed. I added a dyndns script to the directory with:

#!/bin/sh

ssh -P dyndns@ns1.example.com "bin/updatedns $PPP_LOCAL"

The interesting part is that this has to happen as a passwordless action, so root on the ppp machine has a generated ssh public key which is in the authorized_keys of the dyndns account.

7. Things you can and cannot use this for

After you have this great name for your dynamic IP, you should still consider it "dynamic". This means the IP number can change and this influences the "quality" of the name.

You don't want to run your production, high-profile webserver on this. Suppose you lose the IP due to a glitch and the IP gets reassigned to the webserver of the kid down on the block sharing his "interesting images" collection.

You really don't want to point your MX records to dynamic names. When the IP gets reassigned and you don't change the A record (because for example your machine decides to hang during the day and you can't fix it until the evening) people will get bounces on mail telling them about "relaying denied" (hopefully) or the mail vanishes into a black hole.

8. References


Koos van den Hout (koos@kzdoos.xs4all.nl)IPv6 ready