Dynamic Dns on FreeBSD using Bind9

I was looking to install some remote services to my server connected to my home ADSL line. The problem I have is that I have got a dynamic IP address with my DSL line. After trying a number of the public services with varying degrees of sucess and flexibility I decided to build my own solution.

Our companies DNS servers run Bind9 and also the server I run at home also runs Bind9 which means that could use the supplied dns update service with encryption.

Three things are required to achieve this :

1. Method of getting your public IP address
2. Adding a dynamic zone to a dns server (bind9) capable of receiving secure updates
3. Scripting at the ADSL site to tie it all together.

Step 1 – Getting public IP address

There are a number of ways of achieving this – for me I setup a script to return my IP address on our companies servers. Script as follows :

<?
// getipaddress.php
if( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] )) {
    $my_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
    $my_ip =  $_SERVER['REMOTE_ADDR'];
}
echo $my_ip;
?>

Step 2 – Adding dynamic Zone

First you need to create a key for the zone on your DNS server :

cd /var/named/etc/namedb/
dnssec-keygen -b 512 -a HMAC-MD5 -n HOST dynamic.com

This will generate two files with names like :

Kdynamic.com.+157+34532.key
Kdynamic.com.+157+34532.private

Create the zone file dynamic.com in the dynamic folder

$ORIGIN   dynamic.com.
$TTL      3600           ; 1 hour
@         IN SOA ns.domain.com. hostmaster.domain.com. (
            2009042001   ; Serial
            900          ; refresh (15 mins)
            600          ; retry (10 mins)
            86400        ; expire (1 day)
            3600         ; min (1 hour)
            )
            NS      ns.domain.com.
$TTL      600            ; 10 mins

Add the keys and define the new zone by editing the named.conf file. For this you will need to open the .key file created using dnssec-keygen.

key "dynamic.com" { algorithm hmac-md5; secret "<key from .key file>"; };
zone "dynamic.com" {
    type master;
    file "dynamic/dynamic.com";
    allow-update { key dynamic.com; };
};

Step 3 – Scripting on the client server tying it all together

Securely transfer the two generated key/private files to you home server and place them in the folder /etc/namedb

You need to ensure you have installed the CURL library with OpenSSL support. The following script is one I use to send the updates:

#!/usr/local/bin/php -q
<?
$hostname = "dynamic.com";
$keyfile = "Kdynamic.com.+157+34532.key";
$auto_update_delay = 3600;                      // 1 Hour
$userpwd = "Username:Password";
$dns_server = "100.100.100.100";
$url = "https://myserver.com/getipaddress.php?hostname=".$hostname;
$datfile = "/home/scripts/".$hostname.".nsupdate";
$nsupdate = "/usr/bin/nsupdate -k /var/named/etc/namedb/".$keyfile;

$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL,$url );
curl_setopt( $ch, CURLOPT_HEADER, 0);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt( $ch, CURLOPT_TIMEOUT, 30 );
curl_setopt( $ch, CURLOPT_USERPWD, $userpwd );

$response = curl_exec( $ch );
if ( curl_errno($ch) ) {
        print curl_error($ch)."n";
        exit;
} else {
        $ipaddress = curl_exec($ch);
}
if( $ipaddress == "" ) {
        echo "No ip address returnedn";
        exit;
} if ( strlen( $ipaddress ) > 15 ) {
        echo "We got garbagen";
        exit;
}
$cmds = "server ".$dns_server."n".
        "update delete ".$hostname."n".
        "update add ".$hostname." 600 A ".$ipaddress."n".
        "sendn";

$update = TRUE;
if( file_exists( $datfile )) {
        $fp = fopen( $datfile, "r" );
        if( $fp ) {
                $info = fstat( $fp );
                if( $info['ctime'] + $auto_update_delay < time()) {
                        $update = TRUE;
                } else {
                        $saved_cmds = fread( $fp, 1024 );
                        if( $cmds == $saved_cmds ) {
                                $update = FALSE;
                        }
                }
                fclose( $fp );
        }
}
if( $update == TRUE ) {
        $fp = fopen( $datfile, "w" );
        fputs( $fp, $cmds );
        fclose( $fp );
        $cli = $nsupdate." < ".$datfile;
        system( $cli );
        echo $cli."n";

        echo "Updated DNS - ".$hostname." = ".$ipaddress."n";
} else {
        echo "No change DNS - ".$hostname." = ".$ipaddress."n";
}

?>

I have called this script setipaddress.php and I have added it to my crontab and run this every two minutes.

Mick