Exporting PowerDNS Zones to Bind Files

Share on:

Can you easily export PowerDNS zones using a MySQL DB? The answer may not be as easy as it seems.

I have been working on a Migration from PowerDNS using a MySQL DB. We have a custom solution which gets updated and gets pushed to Akamai. The problem is, this custom solution requires all the zones to be in bind format. Doing some research I found out there is no easy way to get these zones into a bind zone file. You could setup a secondary server and do zone transfers, but that wouldnt meet my requirements.

Luckily I stumbled across this post by James Linden in which he created a PHP script that scrapes the entire database and then generates a bind file for each zone with the records.

It is actually a pretty easy script to run, you need to fill in about 5 variables and then you are good to execute it. Here is a copy of the script in case it is no longer available. (It was created in 2013, but still works flawlessly!)

All you need to do is update the 3 lines, to put in the MySQL DB IP, the username, password and database name. You will then enter in the nameservers and hostmaster to be used for the bind files.

 1#!/bin/env php
 2<?php
 3
 4/**
 5 * @copyright 2013 James Linden <[email protected]>
 6 * @author James Linden <[email protected]>
 7 * @url http://jameslinden.com/code/powerdns-to-bind-zone
 8 * @license BSD (2 clause) <http://www.opensource.org/licenses/BSD-2-Clause>
 9 */
10
11$pdns_db = [ 'host' => 'localhost', 'user' => 'root', 'pass' => 'powerdns', 'name' => 'powerdns' ];
12$zone_ns = [ 'ns1.lab.local', 'ns2.lab.local' ];
13$zone_adm = 'support.lab.local';
14
15$zones = [];
16$db = new mysqli( $pdns_db['host'], $pdns_db['user'], $pdns_db['pass'], $pdns_db['name'] );
17if( $q = $db->query( 'SELECT * FROM domains' ) ) {
18	while( $r = $q->fetch_assoc() ) {
19		$d = strtolower( $r['name'] );
20		if( $q1 = $db->query( 'SELECT * FROM records WHERE domain_id=' . $r['id'] ) ) {
21			$x = [ 'A' => [], 'CNAME' => [], 'NS' => [], 'MX' => [], 'TXT' => [] ];
22			while( $r1 = $q1->fetch_assoc() ) {
23				$r1['name'] = strtolower( $r1['name'] );
24				switch( $r1['type'] ) {
25					case 'A':
26						$x[ $r1['type'] ][] = [ $r1['name'], $r1['content'] ];
27					break;
28					case 'CNAME':
29						$x[ $r1['type'] ][] = [ trim( str_replace( $d, null, $r1['name'] ), '.' ), strtolower( $r1['content'] ) ];
30					break;
31					case 'NS':
32						$x[ $r1['type'] ][] = strtolower( $r1['content'] );
33					break;
34					case 'MX':
35						$x[ $r1['type'] ][] = [ (int)$r1['prio'], strtolower( $r1['content'] ) ];
36					break;
37					case 'TXT':
38						$x[ $r1['type'] ][] = [ trim( str_replace( $d, null, $r1['name'] ), '.' ), $r1['content'] ];
39					break;
40				}
41			}
42			$q1->free();
43		}
44		$zones[ $d ] = $x;
45	}
46	$q->free();
47}
48$db->close();
49
50if( is_array( $zones ) && count( $zones ) > 0 ) {
51	$p = './tmp/';
52	if( !is_dir( $p ) ) {
53		if( !mkdir( $p ) ) {
54			die( 'Unable to make tmp directory' . PHP_EOL );
55		}
56	}
57	if( count( $zones ) > 0 ) {
58		foreach( $zones as $d => $r ) {
59			$x = '             ';
60			$t = [ '$TTL 43200', null, '@ IN SOA ' . $zone_ns[0] . '. ' . $zone_adm . '. (', $x . time(), $x . '7200', $x . '3600', $x . '604800', $x . '43200 )', null ];
61			foreach( $zone_ns as $x ) {
62				$t[] = '  IN NS      ' . $x . '.';
63			}
64			$t[] = null;
65			foreach( $r['MX'] as $x ) {
66				$t[] = '  IN MX ' . str_pad( $x[0], 5, ' ', STR_PAD_RIGHT ) . $x[1] . ( is_numeric( substr( $x[1], -1, 1 ) ) ? null : '.' );
67			}
68			$t[] = null;
69			foreach( $r['A'] as $x ) {
70				$t[] = str_pad( $x[0] . '.', 32, ' ', STR_PAD_RIGHT ) . '  IN A ' . $x[1];
71			}
72			$t[] = null;
73			foreach( $r['CNAME'] as $x ) {
74				$t[] = str_pad( $x[0], 32, ' ', STR_PAD_RIGHT ) . '  IN CNAME ' . $x[1] . '.';
75			}
76			$t[] = null;
77			foreach( $r['TXT'] as $x ) {
78				$t[] = str_pad( $x[0], 32, ' ', STR_PAD_RIGHT ) . '  IN TXT ' . $x[1];
79			}
80			file_put_contents( $p . $d . '.zone', implode( PHP_EOL, $t ) . PHP_EOL );
81		}
82	}
83}
84
85?>

Once you have populated the script, you can run it by going to a commandline that has MySQL tools and PHP installed and run

1php scriptname.php

This will create a /tmp folder in the same directory with an export of all your zones.

Thats it! I just wanted to blog about this in case other people were looking for a way to accomplish this, its actually quite simple!

Thanks for stopping by.

comments powered by Disqus