Skip to content
Mar 2 / Greg

Failover DNS Change Script

So, as promised, here is my DNS change script. This is written in php for a BIND DNS server. Right now it is very simplistic; it is made to change a single A record, but can be adapted to do multiples at once.

This is designed to be used in conjunction with Webmin. Setup a remote monitor in the “System and Server Status” section under “other”.

Webmin will monitor your web server and ensure they are up and returning a webpage that matches a regex statement. If it does go down it runs our php script with the command line parameter of “down”. The script will then change the A record in the bind file to the secondary server’s IP and then restart the bind service. Once the server comes back up, the script runs again with “up” on the command line. The up command sets the a record back to the main server. This is meant to be run on the DNS server itself. This could be adapted to issue the rndc command to reload the single zone if you so wanted.

You will want to set the TTL on the A record down to like 60 seconds.

Click the link below for Webmin setup and the PHP script!

Webmin Setup:

Add type Remote HTTP Service

Webmin Sys Status

Then configure as below.

Bind Script Webmin Config

PHP Script. I’m just putting it in the /root folder. Make sure to set it to executable.

= 2) {
if ($argv[1] == 'up') {
//set the A record back to main
echo "up \n";
$FindIT = $Backup;
$SetIT = $Main;
} elseif ($argv[1] == 'down') {
//set the A record to backup
echo "down \n";
$FindIT = $Main;
$SetIT = $Backup;
}

$lineNum = 0;
echo $Path . $Domains . '.hosts' . "\n";
//open the domain file
$lines = file($Path . $Domains . '.hosts');
//open the file for editing
$fp=fopen($Path . $Domains . '.hosts',"w+");
foreach ($lines as $line_num => $line) {
$lineNum = $lineNum + 1;
//increment sequence #
if ($lineNum == 3) {
$line = $line + 1;
$line = " " . $line . "\n";
}
$pos = 0;
//loop through each line checking if it matches $FindIT exactly with the
//last values at the end of the line. If found, change it to $SetIT
$pos = strpos('test' . $line, $FindIT);
if ($pos > 0) {
//we found the line
$line = str_replace($FindIT, $SetIT, $line);
}
//write each line as we loop through
fwrite($fp,$line);
}

//restart bind service
exec('/etc/init.d/named restart');
}
?>

11 Comments

leave a comment
  1. Gary / Nov 26 2009

    You are the man! I dont know how long I have been trying to find a script that will do this. I have some awesome techniques for providing redundant/failover SMTP services, let me know if you want to me elaborate…then again you may have better solutions! Great stuff!

    Thanks!

    G

  2. Greg / Nov 28 2009

    Gary :You are the man! I dont know how long I have been trying to find a script that will do this. I have some awesome techniques for providing redundant/failover SMTP services, let me know if you want to me elaborate…then again you may have better solutions! Great stuff!

    Thanks!

    G

    Gary,

    Thanks dude! I made this as a proof of concept, but I’m glad to see someone running it in production. I would be intereted to see what you have cooking for SMTP HA!

  3. Gary / Nov 28 2009

    I have had these visions of creating redundant web (http) services. I knew that if I could figure out a way to monitor both sites and figure out which one was up, I could simply replace the DNS A record in bind to serve the primary site or backup site….I have been playing around with your script and it is working fabulous! Exactly what I needed….

    I wrote a script to monitor web servers that does the exact same thing as the Webmin monitor…

    ———————

    #!/usr/bin/php
    < ?php //put in an ip address and port number that you will use as a test connection //it just has to open. There is no data exchanged //open http socket on to check connectivity $fp = fsockopen(\"1.1.1.1\", 80, $errno, $errstr, 15); $fp1 = fsockopen(\"2.2.2.2\", 80, $errno, $errstr, 15); $fp2 = fsockopen(\"3.3.3.3\", 80, $errno, $errstr, 15); $fp3 = fsockopen(\"4.4.4.4\", 80, $errno, $errstr, 15); $fp4 = fsockopen(\"5.5.5.5\", 80, $errno, $errstr, 15); $fp5 = fsockopen(\"6.6.6.6\", 80, $errno, $errstr, 15); $fp6 = fsockopen(\"7.7.7.7\", 80, $errno, $errstr, 15); //checks to see if fsockopen didnt make connection if(!$fp) { //exec(\"./down34\"); --- found out this (exec) doesnt work for custom scripts //if no connection, executes shell script to copy red image to web folder shell_exec(\'cp /www/default/images/down/34.gif /www/default/images/34.gif\'); //calls on script to email you connection is down include(\'34.php\'); } else { //exec(\"./up34\"); //if connection, executes shell script to copy green image to web folder shell_exec(\'cp /www/default/images/up/34.gif /www/default/images/34.gif\'); } if(!$fp1) { //exec(\"./down35\"); shell_exec(\'cp /www/default/images/down/35.gif /www/default/images/35.gif\'); include(\'35.php\'); } else { //exec(\"./up35\"); shell_exec(\'cp /www/default/images/up/35.gif /www/default/images/35.gif\'); } if(!$fp2) { //exec(\"./down36\"); shell_exec(\'cp /www/default/images/down/36.gif /www/default/images/36.gif\'); include(\'36.php\'); } else { //exec(\"./up36\"); shell_exec(\'cp /www/default/images/up/36.gif /www/default/images/36.gif\'); } if(!$fp3) { //exec(\"./down37\"); shell_exec(\'cp /www/default/images/down/37.gif /www/default/images/37.gif\'); include(\'37.php\'); } else { //exec(\"./up37\"); shell_exec(\'cp /www/default/images/up/37.gif /www/default/images/37.gif\'); } if(!$fp4) { //exec(\"./down38\"); shell_exec(\'cp /www/default/images/down/38.gif /www/default/images/38.gif\'); include(\'38.php\'); } else { //exec(\"./up38\"); shell_exec(\'cp /www/default/images/up/38.gif /www/default/images/38.gif\'); } if(!$fp5) { //exec(\"./down38\"); //shell_exec(\'cp /www/default/images/down/38.gif /www/default/images/38.gif\'); include(\'89.php\'); } else { //exec(\"./up38\"); //shell_exec(\'cp /www/default/images/up/38.gif /www/default/images/38.gif\'); } if(!$fp6) { //exec(\"./down38\"); //shell_exec(\'cp /www/default/images/down/38.gif /www/default/images/38.gif\'); include(\'90.php\'); } else { //exec(\"./up38\"); //shell_exec(\'cp /www/default/images/up/38.gif /www/default/images/38.gif\'); } ?>

    ———————

    I removed the IP’s obviously…but the thing works awesome. It runs in a cron job every 5 minutes. It will check to see if a server is alive by making a connection on port 80, if it is up, it will copy a green image into a web folder. If it is down, it will copy a red image into the same web folder and email me. I figured by doing this in php it would give me more flexibility than the webmin version.

    As for the failover SMTP… it is too complex for me to elaborate in words without a schematic. If I get a chance, I will post a link to a generic one…

  4. Greg / Nov 28 2009

    Gary,

    Just glancing at your script it looks like your system would work a treat! The only down side to this as compared to webmin would be the fact that not only does webmin check if the server responds on port 80, but it also does a regex on the page looking for specific text showing that the site is responding properly. With your script I’m sure you could do a get on the page and just parse the returned results looking for text matches.

    I do like the all inclusive approach, I’m just not one to reinvent the wheel…hehe. 😉 I like the nice gui interface that webmin provides, it already has the down timing in there, and it also includes the email notification options right there too. What I’m really trying to say is that I’m lazy…heh.

    Again, thanks for sharing!

  5. Kent / Mar 20 2011

    Nice script, Greg. I’m looking for a few different ways to do the DNS fail over, other than getting through a service like DNS Made Easy.
    I do have a question, though, I haven’t yet tried this script, but didn’t see this addressed – what about updating the zone serial? It seems any slave servers would not get the new info, since it’s not incrementing. Have you had issues with that?

  6. Greg / Mar 20 2011

    @Kent
    I do increment the sequence number, check the “//increment sequence #” section. =)
    I see you are in Austin, I’m in College Station. Good to see some locals.

  7. Kent / Mar 22 2011

    Greg,
    Howdy neighbor! Glad to know you are just a jump skip and holler away. 🙂
    I totally missed “increment”, but I see that now. My default Linux install always has webmin, so this is just perfect.
    Thanks for hammering that out, and most of all, for sharing it.

  8. Greg / Mar 22 2011

    Kent,

    If you throw this into production, let me know how it goes. I’m sure you will evolve it one way or the other.

  9. Scott Grayban / Dec 15 2011

    OMG THANK YOU !

    I have been looking for exactly like this for a long time and this little php script is like finding a 50 ounce gold nugget !

  10. Greg / Dec 15 2011

    @Scott
    Send me the nugget, you keep the script 😉

  11. Barry / Jul 30 2012

    Thanks for the script! Just one remark, the variable ‘$Arecord’ is declared but never used.

    (I think I understand what it was meant to do… 😉

Leave a Comment

 

*