Here is how to slave cPanel to a Bind9 server without breaking cPanel or having cPanel undo the sync changes.
On the slave server I installed bind (apt-get install bind9) and made a directory "/Scripts" where I write two scripts, one is a class to wrap up cPanel API functions, the other is the actual DNS zone sync script. Both were written in PHP since it is my language of choice and is very easy to interface to the cPanel API with.
The class
=================================
<?PHP
class cPanel {
private $host;
private $user;
private $hash;
private function __construct($host, $user, $hash) {
$this->host = $host;
$this->user = $user;
$this->hash = preg_replace("/[\t\r\n ]/", "", $hash);
}
private static $instance;
public static function getInstance($host, $user, $hash) {
if (!(cPanel::$instance instanceof cPanel))
cPanel::$instance = new cPanel($host, $user, $hash);
return cPanel::$instance;
}
private function getJSON($request) {
$query = "https://{$this->host}:2087/json-api/" . $request;
$curl = curl_init();
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$header = array("Authorization: WHM {$this->user}:{$this->hash}");
curl_setopt($curl,CURLOPT_HTTPHEADER,$header);
curl_setopt($curl, CURLOPT_URL, $query);
$result = curl_exec($curl);
curl_close($curl);
if ($result == false)
throw new Exception("curl_exec threw error \"" . curl_error($curl) . "\" for $query");
return json_decode($result);
}
public function listzones() {
$info = $this->getJSON('listzones');
$zones = array();
foreach($info->zone as $zone)
$zones[$zone->domain] = $zone->zonefile;
return $zones;
}
}
?>
=================================
And the script:
=================================
#!/usr/bin/php
<?PHP ob_start(); ?>
PUT YOUR cPanel ACCESS HASH HERE
<?PHP
$hash = ob_get_clean();
$master = '123.123.123.123'; //YOUR MASTER SERVER IP
require('/Scripts/cPanel.php');
$cpanel = cPanel::getInstance('www.YOUR_HOST.com', 'root', $hash);
$zones = $cpanel->listzones();
if (count($zones) == 0)
throw new Exception('Unable to retrieve the server\'s zone list');
$fp = fopen('/etc/bind/cpanel.zones', 'w');
foreach($zones as $zone => $file) {
$zone =
"zone \"{$zone}\" {\n" .
" type slave;\n" .
" file \"{$file}\";\n" .
" masters { {$master}; };\n" .
"};\n";
fwrite($fp, $zone);
}
fclose($fp);
shell_exec('/usr/sbin/rndc reload');
?>
=================================
I then ran the script to create the zone file, and then created a symlink in /etc/cron.hourly to the sync script, so every hour the zones are fetched from the server, the configuration is re-built and bind is reloaded.
Then I modified /etc/bind/named.conf.options with the following two options, the first for security, the second to accept notify events from the master server to tell us to update our records.
options {
........ default options ........
recursion no;
allow-notify { 123.123.123.123; };
};
replace 123.123.123.123 with the master server's ip address. Then I just had to make a change to /etc/bind/name.conf.local to include the zone list built from the master server.
include "/etc/bind/cpanel.zones"
And thats it for the slave... now the master needs a minor modification to tell the client to update and to allow zone transfers to the slave. Add the following two lines to the options section in /etc/named.conf
allow-transfer { 123.123.123.123; };
also-notify { 123.123.123.123; };
Again, replacing the 123.123.123.123 with the IP address of the slave server.