This how-to explains a way to build your own dynamic DNS server.
Hi, since some time my DDNS provider has problems which cause the loss of the connection to my home server.
To prevent this loss I've read some manpages and build my own DDNS server.
This is not a how-to about DNS, bind or any other software I've used.
Warranty
Requirements:
You have to change all 'dyndns.example.org' to your domain.
// named.conf options { ... ; working directory of bind directory "/var/named"; ... };
chmod 770 /var/named/
mkdir -p /etc/named/ cd /etc/named/ dnssec-keygen -a hmac-sha512 -b 512 -n HOST dyndns.example.org # webserver-group needs read access to file containing TSIG-key chown root:<webserver-group> /etc/named/Kdyndns.example.org*.private chmod 640 /etc/named/Kdyndns.example.org*.private # get and remember the key grep Key /etc/named/Kdyndns.example.org*.private
key mykey { algorithm hmac-sha512; secret "the-generated-key"; }; zone "dyndns.example.org" IN { type master; file "dyndns.example.org.zone"; allow-query { any; }; allow-transfer { none; }; allow-update { key mykey; }; };
$ORIGIN . $TTL 86400 ; 1 day dyndns.example.org IN SOA localhost. root.localhost. ( 52 ; serial 3600 ; refresh (1 hour) 900 ; retry (15 minutes) 604800 ; expire (1 week) 86400 ; minimum (1 day) ) NS localhost. $ORIGIN dyndns.example.org.
Create a subdomain (dyndns.example.org) and a vhost for the updating script.
For security purpose and compatibility of the php-script the vhost has to be protected by http-authentication.
For Lighttpd you can use the script provided here to generate the users.
Save this PHP-script in the vhost-directory:
<?php // configuration of user and domain $user_domain = array( 'user' => array('subdomain','sub2'), 'user2' => array('sub4') ); // main domain for dynamic DNS $dyndns = "dyndns.example.org"; // DNS server to send update to $dnsserver = "localhost"; // port of DNS server $dnsport = ""; // short sanity check for given IP function checkip($ip) { $iptupel = explode(".", $ip); foreach ($iptupel as $value) { if ($value < 0 || $value > 255) return false; } return true; } // retrieve IP $ip = $_SERVER['REMOTE_ADDR']; // retrieve user if ( isset($_SERVER['REMOTE_USER']) ) { $user = $_SERVER['REMOTE_USER']; } else if ( isset($_SERVER['PHP_AUTH_USER']) ) { $user = $_SERVER['PHP_AUTH_USER']; } else { syslog(LOG_WARN, "No user given by connection from $ip"); exit(0); } // open log session openlog("DDNS-Provider", LOG_PID | LOG_PERROR, LOG_LOCAL0); // check for given domain if ( isset($_POST['DOMAIN']) ) { $subdomain = $_POST['DOMAIN']; } else if ( isset($_GET['DOMAIN']) ) { $subdomain = $_GET['DOMAIN']; } else { syslog(LOG_WARN, "User $user from $ip didn't provide any domain"); exit(0); } // check for needed variables if ( isset($subdomain) && isset($ip) && isset($user) ) { // short sanity check for given IP if ( preg_match("/^(\d{1,3}\.){3}\d{1,3}$/", $ip) && checkip($ip) && $ip != "0.0.0.0" && $ip != "255.255.255.255" ) { // short sanity check for given domain if ( preg_match("/^[\w\d-_\*\.]+$/", $subdomain) ) { // check whether user is allowed to change domain if ( in_array("*", $user_domain[$user]) or in_array($subdomain, $user_domain[$user]) ) { if ( $subdomain != "-" ) $subdomain = $subdomain . '.'; else $subdomain = ''; // shell escape all values $subdomain = escapeshellcmd($subdomain); $user = escapeshellcmd($user); $ip = escapeshellcmd($ip); // prepare command $data = "<<EOF server $dnsserver $dnsport zone $dyndns update delete $subdomain$user.$dyndns A update add $subdomain$user.$dyndns 300 A $ip send EOF"; // run DNS update exec("/usr/bin/nsupdate -k /etc/named/K$dyndns*.private $data", $cmdout, $ret); // check whether DNS update was successful if ($ret != 0) { syslog(LOG_INFO, "Changing DNS for $subdomain$user.$dyndns to $ip failed with code $ret"); } } else { syslog(LOG_INFO, "Domain $subdomain is not allowed for $user from $ip"); } } else { syslog(LOG_INFO, "Domain $subdomain for $user from $ip with $subdomain was wrong"); } } else { syslog(LOG_INFO, "IP $ip for $user from $ip with $subdomain was wrong"); } } else { syslog(LOG_INFO, "DDNS change for $user from $ip with $subdomain failed because of missing values"); } // close log session closelog(); ?>
If you've configured all correctly you can update domains using this command:
wget --no-check-certificate --http-user="user" --http-passwd="password" --post-data "DOMAIN=example" -q https://dyndns.example.com
Some examples:
Script configuration:
$user_domain = array( 'user' => array('subdomain') ); $dyndns = "dyndns.example.org"
Result:
The user 'user' can update the IP for the domain subdomain.user.dyndns.example.org
.
Script configuration:
$user_domain = array( 'user' => array('subdomain'), 'user2' => array('test', 'foobar') ); $dyndns = "dyndns.example.org"
Result:
The user 'user' can update the IP for the domain subdomain.user.dyndns.example.org
.
The user 'user2' can update the IP for the domains test.user2.dyndns.example.org
and foobar.user2.dyndns.example.org
.
Script configuration:
$user_domain = array( 'user' => array('*'), 'user2' => array('test', 'foobar') ); $dyndns = "dyndns.example.org"
Result:
The user 'user' can update the IP for the wildcard domain *.user.dyndns.example.org
which means all subdomains of user.dyndns.example.org
are resolved to the IP set for *.
The user 'user2' can update the IP for the domains test.user2.dyndns.example.org
and foobar.user2.dyndns.example.org
.
Script configuration:
$user_domain = array( 'user' => array('-','subdomain'), 'user2' => array('test', 'foobar') ); $dyndns = "dyndns.example.org"
Result:
The user 'user' can update the IP for the domains subdomain.user.dyndns.example.org
and user.dyndns.example.org
.
The user 'user2' can update the IP for the domains test.user2.dyndns.example.org
and foobar.user2.dyndns.example.org
.