DNS zone transfer scripts for PowerDNS to BIND
Our current setup is an internal PDNS server with the MySQL back-end and three BIND slaves scattered across the country, and this is how we transfer zones from the master to the slaves.
On the master
<?php ob_start(); if(!isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER'] !== '%CLIENTUSER%' || $_SERVER['PHP_AUTH_PW'] !== '%CLIENTPASSWD%') die(); if(!isset($_REQUEST['hostname']) || empty($_REQUEST['hostname'])) die(); $configTarget = $_REQUEST['hostname']; $masters_default = array('master.company.com' => 'XXX.XXX.XXX.XXX'); $internal_ns = array( 'ns1.company.com' => 'XXX.XXX.XXX.XXX', 'ns2.company.com' => 'XXX.XXX.XXX.XXX', 'ns3.company.com' => 'XXX.XXX.XXX.XXX', ); $external_ns = array( 'external.othercompany.com' => 'XXX.XXX.XXX.XXX', ); $mysql = new mysqli('%DBHOST%','%DBUSER%','%DBPASSWD%','%DBNAME%',%DBPORT%); if($mysql->connect_error) die($mysql->connect_error); $sql = 'SELECT DISTINCT d.name,d.account,GROUP_CONCAT(r.content SEPARATOR \';\') AS ns' . ' FROM domains AS d' . ' RIGHT JOIN records AS r ON d.id=r.domain_id' . ' WHERE r.name=d.name AND r.type=\'NS\'' . 'GROUP BY d.id'; $result = $mysql->query($sql); /* * Internal named.conf */ $result->data_seek(0); while($domain = $result->fetch_object()) { $ns = split(';', $domain->ns); printf("# Domain: %s\n", $domain->name); //printf("# Account: %s\n", empty($domain->account) ? 'n/a' : $domain->account); if(!in_array($configTarget, $ns)) { printf("# WARNING: %s not in %s\n", $configTarget, $domain->name); } $masters = $masters_default; $allow_transfer = $internal_ns; unset($allow_transfer[$configTarget]); foreach($external_ns as $exthost => $extip) { if(in_array($exthost, $ns)) { $allow_transfer[$exthost] = $extip; } } printf("zone \"%s\" {\n", $domain->name); printf("\ttype slave;\n"); printf("\tfile \"slaves/%s\";\n", str_replace('/', '_', $domain->name)); printf("\tnotify no;\n"); printf("\tmasters { %s; };\n", implode('; ', $masters)); printf("\tallow-transfer { %s; };\n", implode('; ', $allow_transfer)); printf("\tallow-notify { %s; };\n", implode('; ', $allow_transfer)); printf("};\n"); } $output = ob_get_contents(); ob_end_clean(); printf("# %s %s\n%s", sha1($output), date('Y-m-d H:i:s'), $output); ?>
On the slaves
#!/bin/bash ts=`date +%s` hostname=`hostname` url="https://%CLIENTUSER%:%CLIENTPASSWD%@master.company.com/getconfig.php?hostname=${hostname}" tmpfile=`mktemp /tmp/download.XXXX` newconf=`mktemp /tmp/${hostname}.named.conf.XXXX` sysconf="/etc/named/master-zones.conf" curl --cacert /etc/pki/tls/certs/master.crt -s "${url}" > "${tmpfile}" if [[ "$?" != "0" ]]; then echo "download failed" rm -f "${tmpfile}" "${newconf}" exit 1 fi tail -n+2 "${tmpfile}" > "${newconf}" hash1=`head -n1 ${tmpfile} | cut -d' ' -f2` hash2=`sha1sum ${newconf} | cut -d' ' -f1` if [[ "${hash1}" == "${hash2}" ]]; then cmp -s ${sysconf} ${newconf} if [[ "$?" != "0" ]]; then mv "${sysconf}" "${sysconf}-${ts}" install -m640 -o root -g named "${newconf}" "${sysconf}" named-checkconf "${sysconf}" if [[ "$?" == "0" ]]; then rndc reload diff -u "${sysconf}-${ts}" "${sysconf}" else echo "named-checkconf failed, aborting update" mv "${sysconf}-${ts}" "${sysconf}" fi fi else echo "HASH FAIL. aborted." echo "hash1 $hash1" echo "hash2 $hash2" fi rm -f "${tmpfile}" "${newconf}"
Include the config file on the slaves, put this in /etc/named.conf
include "/etc/named/master-zones.conf";MAILTO=logwatch@company.com */5 * * * * root /usr/local/system_scripts/update-zones.sh
Hopefully it’s pretty self-explanatory, if not, leave a comment or drop a mail.














