DDNS: Membuat Sendiri

From OnnoWiki
Jump to navigation Jump to search

Sumber: http://andrwe.org/linux/own-ddns


HOW-TO: be your own DDNS provider Summary

This how-to explains a way to build your own dynamic DNS server. Main

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 There is no warranty that this how-to works for any system. I'm just providing these information because it worked for me this way. If you have questions you can leave a message here but I decide whether I'll answer and help or not.

Requirements:

   own server with static IP
   own domain resolving (e.g.: example.org)
   subdomain delegated to to your server (e.g.: dyndns.example.org)
   php5
   webserver supporting PHP (I use Lighttpd, but any will do)
   bind>=9
   dnsutils

Configurations

You have to change all 'dyndns.example.org' to your domain. bind

   the bind user requires write-access to bind working directory:
   // named.conf
   options {
     ...
     ; working directory of bind
     directory "/var/named";
     ...
   };
   chmod 770 /var/named/
   we generate a TSIG-key in a new directory which is used to verify the server and client:
   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
   create zone in named.conf
   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; };
   };
   create zone-file
   /var/named/dyndns.example.org.zone
       $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.

Webserver

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:

index.php

   <?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();
   ?>

Usage

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.



Referensi