HOWTO: Basic DNS "clustering"

Protollix

Verified User
Joined
Apr 24, 2004
Messages
59
ChangeLog: v1.2 - modified Slave Configuration #4. Instead of copying and deleting the incoming file, we just move the new file over. This keeps us from renaming and deleting it, which jlasman proposed. I think this method effectively solves the problem he posed in one of his replies below.

v1.1 - modifed Slave Configuration #4. Use named stop and named start instead of just name restart.

- modified the Mater Configuration #5. Moved the "rm" statement from the "start()" function to the "restart()" function.

- changes were made so that if you use two name servers, and each one is a slave for the other, you don't wind up with an infinite loop of restarts.


A lot of us have been looking for a way to automate our DNS updates. Many of us run several servers and maybe use some of these servers as "slaves". Personally, I use one server as a slave (ns2) to my other master DNS servers. Trying to keep these in sync has been an excercise in futility. I have come up with a way to have your master servers automatically add new domains to the slave(s). It's a push/pull system and requires a few cron entries to work properly.

First off, we need to get our files a little more organized for this.

We will configure the slave server first. I writing this HOWTO with only a single slave in mind. Simply repeat the process for multiple slaves. If you are attempting to follow this HOWTO, it is assumed you would know how to do this ;)

Slave Configuration
1. Create a directory to hold the slave files
Normally, all your zone files are tossed into the /var/named directory. That is fine for the zone files for which this server is the master, if there are any. However, to keep things clean, we want to seperate all the zone files we are slaving. So first, we need to create a directory for the master server. You should create one for each master server this slave server is a slave for. In my case, ns2.protollix.com is a slave for ns1.protollix.com. So I shell into the ns2 machine and su to root. I then execute the following commands:
Code:
mkdir /var/named/ns1
chown named.named /var/named/ns1

2. Modify your /etc/named.conf file
Next, we need to tell BIND where to find the zone files. Basically what we are doing, is storing the zone lines in a seperate file so we can just include the file into our master named.conf file. So, what you need to do is BACKUP THIS FILE.
Code:
cp /etc/named.conf /etc/named.conf.bak

Next, remove any slave lines from this file for the master server we are configuring. You probably only need to do this if you were maintaining a master/slave relationship by hand.

Then, you need to add a line to tell BIND to include a configuration file. I am using my file as an example. Add (probably at the end of the file)
Code:
include "/var/named/ns1/ns1.protollix.com.txt";

You can save and close this file.

3. Setup a user account to store the config files
I removed this section as you also must add the user somewhere else so he can ftp in. I don't have time to figure that out ;) You can use the user "admin" for this. I have changed the directories below to reference the admin's home dir for now.

4. Create the pull script and add it to cron
Create the below shell script and save it somewhere. I put scripts like this in /usr/local/sbin.
This script was written on RedHat EL 3.0 system. /etc/init.d/named refers to a BIND startup script. If you are on Debian or FreeBSD or something else, you might have a different startup script placed elsewhere.
Code:
#!/bin/sh

if [ -f "/home/admin/named/ns1.protollix.com.txt" ] ; then
        /bin/mv  -f /home/admin/named/ns1.protollix.com.txt /var/named/ns1/ns1.protollix.com.txt
        chown named.named /var/named/ns1/ns1.protollix.com.txt
        /etc/init.d/named stop
        /etc/init.d/named start

Don't worry if these seems a little confusing, since we have created the push script to create the file we are pulling with this script. It should all become clear at the end.

Basically, what this script does is:
1. checks to see if a new slave config file exists
2. if it does, it copies to where BIND expects it to be and changes the ownership of the file to the right user/group
3. we then restart BIND and remove the uploaded file so we don't reload BIND every time the script runs

Next, add this to cron. I execute it once per minute.
Code:
crontab -e

then insert:
Code:
*/1 * * * * /usr/local/sbin/copy_slave_configs.sh
Obviously, "copy_slaves_config.sh" is the filename I chose. You can use whatever you wish. Just make sure you have made the file executable. If you have not done that yet then issue:
Code:
chmod 700 /usr/local/sbin/copy_slave_configs.sh

We are now done with the slave configuration.

Master Configuration
1. Intro and Directories
First things first, since you probably already have several domains on this server, we need to clean up the named.conf file and also tell DA to use the proper notify settings when creating new zone files. So go ahead and SSH into your master server and su to root.

We need to keep things clean and organized here too. issue the following (salt to taste):
Code:
mkdir /var/named/pushtoslave
chown named.named /var/named/pushtoslave

2. Configure DA to use a new zone line
We need to use a custom template here:
Code:
cd /usr/local/directadmin/data/templates
cp zone.conf custom
chown diradmin.diradmin custom/zone.conf
Next, we need to modify this custom template. Edit /usr/local/directadmin/data/templates/custom/zone.conf with your favorite editor and change the one line in there to this (replace the IP address with the IP address(es) of your slave server(s))
Code:
zone "|DOMAIN|" { type master; file "|PATH|/|DOMAIN|.db"; notify yes; also-notify {12.12.12.12;}; };
(replace 12.12.12.12 with the IP of you slave server)
Save the file and close your editor.

3. Clean up the named.conf file
What I did was write a PHP script that grabs the domains DA says is hosted on the server, and write a new line to the named.conf file. You need to remove *all* master lines from this file first otherwise we will end up with duplicate lines, and BIND will whine! BACKUP YOUR named.conf FILE NOW. DO NOT SKIP THE BACKUP! We need to backup the file incase something gets hosed up.

So go ahead and delete all the master lines from the named.conf file. I could have added something in the below script to do this auotmatically, but I didn't. Namely because I had some zones that DA didn't know about yet. Here is the PHP script to grab the list of domains and stick the proper lines back into the named.conf file:
PHP:
<?php
function getDomains() {
    $domains = array();
    // open the domains file for reading    
    $fp = @fopen('/etc/virtual/domains', 'r+');
    while (!feof($fp)) {
        $domains[] = str_replace("\n", "", fgets($fp));
    }
    fclose($fp);
    asort($domains);
    return $domains;
}

function writeDomains($domains) {
    // for each domain, we create a new "zone" entry

    foreach ($domains as $key=>$val) {
        if (!empty($val)) 
                $zone_string = "zone \"$val\" {type master;  file \"/var/named/$val.db\"; notify yes; also-notify {12.12.12.12;}; };\r\n";
        $correct_domains[] = $zone_string;
    }
    $fp = fopen('/etc/named.conf', 'a+');
    foreach($correct_domains as $key=>$val) {
        fwrite($fp, $val);
    }
    fclose($fp);
}


$domains = getDomains();
writeDomains($domains);
?>
Replace 12.12.12.12 with the IP address of your Slave server.
go ahead and execute that script. I named it rewrite_named_conf.php and saved it in /usr/local/sbin. so I would issue the following command:
Code:
php /usr/local/sbin/rewrite_named_conf.php

Open up /etc/named.conf and verify your domains are there!
Code:
cat /etc/named.conf

4. Setup the push script and add it to cron
Sweet, we are almost done!
Next thing we need to do is create the script that will write the slave configuration file and push it to the slave server.
I create the below php file as /usr/local/sbin/push_to_slave.php
PHP:
<?php
function getDomains() {
    $domains = array();
    // open the domains file for reading    
    $fp = @fopen('/etc/virtual/domains', 'r+');
    while (!feof($fp)) {
        $domains[] = str_replace("\n", "", fgets($fp));
    }
    fclose($fp);
    asort($domains);
    return $domains;
}

function writeDomains($domains) {
    // for each domain, we create a new "zone" entry
    $fp = fopen('/var/named/pushtoslave/ns1.protollix.com.txt', 'w+');
    foreach ($domains as $key=>$val) {
        if (!empty($val)) 
                $zone_string = "zone \"$val\" {type slave; masters {12.12.12.12;}; file \"/var/named/ns1/$val.db\"; };\r\n";
        fwrite($fp, $zone_string);
    }
    fclose($fp);
}

function uploadFile() {
    $conn = ftp_connect('ns2.protollix.com');
    $login_result = ftp_login($conn, 'admin', 'password');
    if (!$conn) {
        die('Unable to upload named config');
    }
    $upload = ftp_put($conn, 'named/ns1.protollix.com.txt', '/var/named/pushtoslave/ns1.protollix.com.txt', FTP_ASCII);
    if (!$upload) {
        die('Error uploading file!');
    }
    ftp_close($conn);
}

// we only continue if the push file does not exist. If it does exist, then we have already pushed it since the last named restart
$test = @fopen('/var/named/pushtoslave/ns1.protollix.com.txt', 'r');
if (!$test) {
        $domains = getDomains();
        writeDomains($domains);
        uploadFile();
}
?>

Replace 12.12.12.12 with the IP address of this master nameserver.

Next, add this script to cron. I run it every minute. You can run it less frequently if you don't have a lot of DNS changes going on.
Code:
crontab -e
paste in:
Code:
*/1 * * * * /usr/local/bin/php /usr/local/sbin/push_to_slave.php

if the path to your php binary is different, please replace that path with what it is on your system.

5. Modify the BIND startup script
The below is for a RedHat EL 3.0 system. If you are on a different OS, you will probably need to modify a different file.
We need to edit the named init.d script to tell it to delete the slave config file when we restart BIND so our push script knows that it needs to rewrite the config file. We could make the script smarter to only upload a new file when a domain has been added or deleted, but that is outside the scope of this HOWTO.
Open /etc/init.d/named in your favorite text editor. Find this section (yours might vary slightly. The important thing is to find the "restart() function):
Code:
restart() {
        stop();
        start();
}

Right after the last echo statement, add the following:
Code:
/bin/rm -f /var/named/pushtoslave/ns1.protollix.com.txt

It shoud now resemble:
Code:
restart() {
        stop();
        start();
        /bin/rm -f /var/named/pushtoslave/ns1.protollix.com.txt
}

save the file and close the editor.

Conclusion
You should now be good to go. Every time your master server adds a new zone file, or modifies a zone file and restarts BIND, it will notify the slave that it needs to pull the new record. It will also automatically configure the slave server, telling the slave that it indeed should be slave for the domains on the master server.

NOTE/Indemnification
I wrote this after setting this up on my own systems. It is possible I have left out something. By following this HOWTO you agree that if your configuration becomes corrupt, it doesn't work as expected, your head explodes or anything else occurs, that you will not hold the author of this HOWTO, Sean Finkel, responsible for said damages. This is a "do it at your own risk" HOWTO. Please proceed with caution, and only if you know what you are doing.

On a more positive note ;)
If you notice any typos or other errors, please let me know and I will edit this HOWTO.

I hope this HOWTO helps some of you that have been clamoring for master/slave clustering!

JOHN
I know you are reading this ;)
Maybe you can add this to DA as the master/slave clustering for DNS. Obviously, it would need some changes, but I think it could be a decent base system to start with!

Also, can we get a config rewrite for the task queue that rewrites the named.conf file? :D
 
Last edited:
Sean,

Thanks for such a great Howto. I'll give a try and post in here my results.


Regards,
Ben
 
thanks :)

Note that I made a few changes! (changelog is now at the top of the post)
 
Protollix said:
A lot of us have been looking for a way to automate our DNS updates. Many of us run several servers and maybe use some of these servers as "slaves"
and ...
On a more positive note ;)
If you notice any typos or other errors, please let me know and I will edit this HOWTO.
Thanks for an awesome bit of work, Sean.

I've read it from top to bottom, trying a few things as I went, and I've found a few "issues" I thought I'd bring to your attention:

1) admin perhaps is a bad choice of a user account to use for the ftp. Why? Because it requires that the user password be found in plain text in the cron job that does the ftp transfer. If I were going to use ftp I'd create an unprivileged account just for this purpose. Perhaps better use scp, and set up scp to not require passwords. Or perhaps create the file to be transferred in a users home directory and then use wget from the slave nameserver to get it without a password (with the perhaps undesirable side effect of making it available to anyone who might come across it).

2) Every minute seems like a bit often to restart BIND; on a busy slave server with a lot of domains this could almost be a DOS attack; you could end up with a server that's down (as it reconfigures itself) more than it's up. I don't think I'd do it more than several times a day. DNS servers are unresponsive while they're restarting. For DNS servers with lots of domains that could take a while.

You do not have to modify the /etc/named.conf file; you do not need the notify yes; also-notify... code. Tried and tested minutes before writing this reply.

This saves a lot of time and room for error during the setup stage, and the addition is unnecessary as long as the zone files on your master machine reference the name of the slave nameserver with NS records.

You should not use the domains listed in /etc/virtual/domains as your definitive list of domains; it will NOT list all domains on the server for which there are master DNS zones. For example it won't manage any master zones you've set up manually, it won't list any master zones DA set up for subdomains, and it won't list any master zones DA set up for alias domains.

The only safe place to get the information is from the /etc/named.conf file. One of my programmers has written a script to modify the etc/named.conf file to get the list; I'll find it and post it.

And finally (for now anyway) if you insist on running the script on the slave server as often as once a minute you should probably implement a file-locking scheme; otherwise you could remove the file from the slave server just after a change is being uploaded, resulting in not getting any changes during the next runs until changes are made again.

But again, a great piece of work.

Jeff

Jeff
 
Re: Re: HOWTO: Basic DNS "clustering"

jlasman said:
Thanks for an awesome bit of work, Sean.
Thank ya :)


1) admin perhaps is a bad choice of a user account to use for the ftp. Why? Because it requires that the user password be found in plain text in the cron job that does the ftp transfer. If I were going to use ftp I'd create an unprivileged account just for this purpose. Perhaps better use scp, and set up scp to not require passwords. Or perhaps create the file to be transferred in a users home directory and then use wget from the slave nameserver to get it without a password (with the perhaps undesirable side effect of making it available to anyone who might come across it).
Indeed. I had originally written it have the reader create a new user account named "slave" that's sole purpose was to store the uploaded zone files. However, proftpd was not letting this user account login and I didn't have time to figure out where proftpd gets it's userlist.

While the password is in clear text, it's not in the cron job. It's in the php file, which can be chmodded 700 and owned by root.

2) Every minute seems like a bit often to restart BIND; on a busy slave server with a lot of domains this could almost be a DOS attack; you could end up with a server that's down (as it reconfigures itself) more than it's up. I don't think I'd do it more than several times a day. DNS servers are unresponsive while they're restarting. For DNS servers with lots of domains that could take a while.

Note that it's not restarting every minute, just checking for updates every minute. Obviously, if you have a really busy server, adjust the cron job run time as needed. On servers that make 10 or so updates per day, running every minute shouldn't be a problem. Especially on powerful servers. But I do agree with you. If you have a server where the DNS updates every couple minutes, then running the script every minute will probably indeed run the load up and make DNS mostly unavailable.

You do not have to modify the /etc/named.conf file; you do not need the notify yes; also-notify... code. Tried and tested minutes before writing this reply.
I have to disagree with you here. Maybe you have a different DNS setup than I do, which is entirely possible. I am using the default RHEL BIND setup (or does DA replace that with it's own?).

Anyhow, I just tested this. I added a subdomain to a zone file, changed the serial and restarted BIND. I waited 5 minutes and the slave zone file still hasn't refreshed. With the notify options in there, it refreshes almost immediately. This is key, IMO. Maybe it's not really required to update the slave that quickly for some people, but for some it is.

This saves a lot of time and room for error during the setup stage, and the addition is unnecessary as long as the zone files on your master machine reference the name of the slave nameserver with NS records.
Again, from the tests I just ran, this is not the case. The domain had "ns1.protollix.com" and "ns2.protollix.com" as NS records and the slave server still hasn't updated as I type this.

You should not use the domains listed in /etc/virtual/domains as your definitive list of domains; it will NOT list all domains on the server for which there are master DNS zones. For example it won't manage any master zones you've set up manually, it won't list any master zones DA set up for subdomains, and it won't list any master zones DA set up for alias domains.

I stated the first point (i think) in my howto.. about it only having the domains DA knows about.

Are you sure it doesn't store domain aliases/pointers there? I have a few that are indeed stored in there that I know I did not add by hand.

As well, master zone files DA setup for subdomains (as in, you create the subdomain as a DA account) are indeed stored in there. At least in my setup.
Matter of fact, I have four subdomains listed in there off my protollix.com domain...

The only safe place to get the information is from the /etc/named.conf file. One of my programmers has written a script to modify the etc/named.conf file to get the list; I'll find it and post it.
That would be most helpful. I was going to write a script that basically parsed the named.conf file and got the domains that way, but after comparing /etc/virtual/domains with my named.conf and seeing they contained the exact same info (save for zone files I added manually) I decided to go that route.

And finally (for now anyway) if you insist on running the script on the slave server as often as once a minute you should probably implement a file-locking scheme; otherwise you could remove the file from the slave server just after a change is being uploaded, resulting in not getting any changes during the next runs until changes are made again.
This is true. Good point. I don't see it happening (except very very rarely) except on the busiest of servers.

Thanks for the critique! :)
 
Notes... in no particular order...

DA doesn't change the named.conf file.

My reference server is running RHL 7.3; it's possible the default behavior has been changed in RHEL; I'll go with you for now, and make the changes on my own system.

My testbed is running a derivitive of RHEL, but it's not available right now; it's being used for a system restore testbed.

I had seventeen zones in /etc/named.conf which were not in /etc/virtual/domains. I can only speak of my own experience. It may very well be a problem only for aliased domains; I didn't study the server.

The code is already written and I just have to find it.

proftpd keeps the user passwords in /etc/proftpd.passwd.

While file-locking schemes are easy to implement (you just rename the file before you use it, so the new one can still come in under the "real" name), I don't need it because I'm staying with every few hours.

As you may note if you've followed my posts, I've had this on the back burner for several months and I'm really happy you put in so much work.

Are you going to open source it? Or do I have to rewrite it all anyway <smile>?

(Nevertheless my code (including the code my programmer wrote) is open source; so you can certainly make use of it as soon as I find it and post it.)

Jeff
 
jlasman said:

DA doesn't change the named.conf file.
It doesn't *change* it, but it does add to it (obviously). I assume you meant that it didn't modify a current entry, correct?

I had seventeen zones in /etc/named.conf which were not in /etc/virtual/domains. I can only speak of my own experience. It may very well be a problem only for aliased domains; I didn't study the server.
I certainly am not calling you a liar! ;)
But from what I am observing, it is adding aliases and subdomains that have master zone files. Maybe in previous versions of DA it did not?

proftpd keeps the user passwords in /etc/proftpd.passwd.
I knew it was something like that. Just didn't have the time to check

While file-locking schemes are easy to implement (you just rename the file before you use it, so the new one can still come in under the "real" name), I don't need it because I'm staying with every few hours.
Agreed. I think I will add that to the HOWTO. thanks!

As you may note if you've followed my posts, I've had this on the back burner for several months and I'm really happy you put in so much work.

Are you going to open source it? Or do I have to rewrite it all anyway <smile>?
Open source the PHP and Shell code? lol, yes of course. Do as you will with it ;) I consider that public domain. If you modify the HOWTO and repost, I just ask for credit and maybe a link to protollix.com

(Nevertheless my code (including the code my programmer wrote) is open source; so you can certainly make use of it as soon as I find it and post it.)
sweet :) thank you!
 
Protollix said:
It doesn't *change* it, but it does add to it (obviously). I assume you meant that it didn't modify a current entry, correct?
I was referring to the possibility that DA might make a change so as to need the "notify" stuff. It's probably a version change issue.
I certainly am not calling you a liar! ;)
But from what I am observing, it is adding aliases and subdomains that have master zone files. Maybe in previous versions of DA it did not?
I don't know. But I've got the code that works, and it's much better in that it works for any masters, whether they be Sun Cobalt, DA, Plesk, CPanel, whatever.
I knew it was something like that. Just didn't have the time to check
So hopefully you'll consider a different user?
Agreed. I think I will add that to the HOWTO. thanks!
Good idea.

I've been using renames as a filelocking mechanism since CP/M days in the 70s.
Open source the PHP and Shell code? lol, yes of course. Do as you will with it ;) I consider that public domain. If you modify the HOWTO and repost, I just ask for credit and maybe a link to protollix.com
I'm not planning changing your HOWTO. What I will probably still do is create a How-To on how to slave DNS from any kind of linuxbased BIND-based master DNS server.

A perl file is attached. Read it to see how it has to be called; it's most likely not suitable for your How-To as is.

As I refine it I'll publish it on my own website as part of any How-To I produce.

Jeff
 

Attachments

  • master2slave.pl.txt
    3 KB · Views: 617
Back
Top