Create a dynamic dns service with BIND
Hi,
Some of us use an dynamic DNS service to access our home server or desktop, but with cheap INTERNET access prices you may think in having your own server (these days you can have 100/10MB fiber connection for 50€), that if you don’t like the cloud concept.
But what if you don’t want do use the domain name that your dynamic DNS provider give you (something like myhome.dyndns.org), lets say that you want www.at-my-domain.com. You have two options:
1 – You buy/rent a custom DNS Service (arround 40€/year/domain)
2 – You build your costom DNS server with BIND or TINYDNS.
I’ll write about the second one, but before we start let me tell you the implications this setup has.
1.º – Need root access to the server.
2.º – If you have a lot of domains you’ll have a big DNS overhead, this because you’ll have to have a small TTL (time to live) on the DNS requests you server, this to keep other DNS servers and clients updated.
3.º – Need to have cron running.
4.º – Need to have BIND installed.
5.º- Need a Dynamic DNS service provider for your initial domain, don’t forget to check use wildcard option, check www.dyndns.org, install and configure the client (following the documentation) and test it afterwards.
6.º- Need direct access to port UDP port 53, check your firewall, and hosts.deny file if you use TCP Wrappers.
7.º – This post is not about DNS security, you should read about it and hard your setup furthermore.
I’m going to set this up in a OpenSuSE 11.0 (X86_64) , but this setup should be suitable for other distros with small changes.
Imagine that you have bough the domain starwars-xpto.com
Lets start by BIND configuration and to do this lets edit /etc/named.conf and add the following lines
zone “starwars-xpto.com” in {
file “master/starwars-xpto.com”;
type master;
allow-transfer { any; };
};
now lets create and initial setup file by creating the file:
/var/lib/named/master/starwars-xpto.com
and add some initial content:
$TTL 60
@ IN SOA yourhost.yourdomain.name. root.yourhost.yourdomain.name. (
1249459201 ; serial
10800 ; refresh
3600 ; retry
604800 ; expiry
86400 ) ; minimum
starwars-xpto.com. IN MX 10 mail.starwars-xpto.com.
starwars-xpto.com. IN NS ns1
starwars-xpto.com. IN NS ns2
mail IN A 83.132.158.224
ns1 IN A 83.132.158.224
ns2 IN A 83.132.158.224
*.starwars-xpto.com. IN A 83.132.158.224
After this just reload named:
/etc/init.d/named reload
and test it:
dig @your_dns_server_IP www.starwars-xpto.com
you should get something like:
; <<>> DiG 9.6.1 <<>> @localhost www.starwars-xpto.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55310
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2
;; QUESTION SECTION:
;www.starwars-xpto.com. IN A
;; ANSWER SECTION:
www.starwars-xpto.com. 60 IN A 83.132.158.224
;; AUTHORITY SECTION:
starwars-xpto. 60 IN NS ns1.starwars-xpto.com.
starwars-xpto. 60 IN NS ns2.starwars-xpto.com.
;; ADDITIONAL SECTION:
ns1.starwars-xpto.com. 60 IN A 83.132.158.224
ns2.starwars-xpto.com. 60 IN A 83.132.158.224
;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Aug 17 18:02:31 2009
;; MSG SIZE rcvd: 120
If you got something like that it’s great, you have your BIND configuration working, if not, check the logs. Bind is really picky with the syntax, and by the way you can’t use # as comment in the config file just the ;
Now lets go to the interesting part, creating the script that will change your ip address on bind configuration whenever it changes. Just create a script wherever you want, my custom system scripts are usually in /root/bin so I’ll keep using it.
Lets create and edit the file /root/bin/update_dns not forgetting to change yourdomain_at_dyndns.org
just copy/past the content bellow:
#!/bin/bash
BIND_DIR=’/var/lib/named/master’ ;
DOMAIN=$1;
BIND_FILE=$DOMAIN ;
if [ $# -ne 1 ] ; then
echo Usage: update_dns domain ;
echo EX: update_dns domain.com;
exit ;
fi
function get_ip ()
{
echo `dig yourdomain_at_dyndns.org | grep yourdomain_at_dyndns.org | grep -v ‘;\|CNAME’ | awk ‘{print $5}’` ;
}
function update_dns ()
{
DATA_SEGUNDOS=`date +’%s’`;
DOMAIN_=$1
IP_=$2
BIND_WORK_DIR_=$3
BIND_WORK_FILE_=$4
if [ “$IP_” != “`grep ‘IN.*A’ /var/lib/named/master/$BIND_FILE | grep -v SOA | awk ‘{print $4}’ | uniq`” ] ; then
cat $BIND_WORK_DIR_/$BIND_WORK_FILE_ | sed s/'[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}’/$IP_/ > /tmp/$DOMAIN_ ;
cat /tmp/$DOMAIN_ | sed s/'[0-9]\{10\}’/$DATA_SEGUNDOS/g > /tmp/$DOMAIN_.bind ;
cat /tmp/$DOMAIN_.bind
rm $BIND_WORK_DIR_/$BIND_WORK_FILE_ ;
mv -f /tmp/$DOMAIN_.bind $BIND_WORK_DIR_/$BIND_WORK_FILE_ ;
rm /tmp/$DOMAIN_* ;
echo `date +’%b %d %H:%m:%S’` “Domain: $DOMAIN_ updated to IP: $IP_” >> /var/log/messages ;
chmod -R 755 /var/lib/named/master
else
echo “No need for update” ;
fi
}
function restart_dns_server ()
{
if [ ! -f /tmp/restarting_named ] ; then
touch /tmp/restarting_named;
/etc/init.d/named stop;
sleep 3 ;
pkill -9 named ;
sleep 1 ;
/etc/init.d/named restart ;
rm /tmp/restarting_named
else
sleep 10 ;
restart_dns_server ;
fi
}
IP=`get_ip` ;
if [ “$IP” != “`grep ‘[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}’ $BIND_DIR/$BIND_FILE | awk ‘{print $4}’ | uniq`” ] ; then
update_dns $DOMAIN $IP $BIND_DIR $BIND_FILE ;
restart_dns_server ;
fi
Finally you just need to setup cron, just type crontab -e
and add an entrace like
*/5 * * * * root /root/bin/updatedns starwars-xpto.com
Just wait the 5 minutes and check if the ip changed with the dig command as wrote above.
After all this and as the last step of configuration go to the domain provider were you bought your domain (ex: www.godaddy.com) and configure it to use as name server the NS1.yourdomain_at_dyndns.org and NS2.yourdomain_at_dyndns.org.
and your done.
This isn’t a easy setup to do but if you have multiple custom domains it can save a few € every year, I know in my case it does.
Cheers,
Pedro Oliveira