It’s been a while since I wrote about PowerDNS, but I recently had the opportunity to look at the BIND backend. This backend enables PowerDNS to serve BIND-format zone files - which is a boon for any migration from BIND to PowerDNS. The BIND backend also supports serving DNSSEC RRs.
In this article, I will cover the installation of the latest version of PowerDNS Authoritative Server on a pair of CentOS 7.1 hosts - one set up as a master (centos-ns1) and the other as a slave (centos-ns2). Both of these servers will use the BIND backend. I’ll cover setting up these servers to run under systemd, and appropriate firewall configuration using firewall-cmd. Read my previous article for more detail on PowerDNS, the MySQL backend, chroot-ing the installation, and other topics.
First, install the required prerequisite packages:
|
1 |
# yum -y install boost boost-devel bzip2 mysql-devel gcc gcc-c++ bind-utils |
As you can see, I also install bind-utils so that I obtain the dig utility - required for testing the nameservers.
Next, download the latest version of PowerDNS Authoritative Server from the PowerDNS website. I’ve downloaded to /usr/local/src on both servers. Untar the installation package:
|
1 2 |
# cd /usr/local/src # tar xjf pdns-3.4.3.tar.bz2 |
As you can see, version 3.4.3 was current at the time of writing this article. Ensure that you grab the latest version of the software so that all appropriate security errata are applied.
Next, configure the installation, compile, and install.
|
1 2 3 4 |
# cd pdns-3.4.3 # ./configure --prefix=/usr/local/pdns-3.4.3 # make # make install |
Create a symlink to your installation - this will make upgrades easier (you just install the new version to the appropriate --prefix, and then update the symlink).
|
1 |
# ln -s /usr/local/pdns-3.4.3 /usr/local/pdns |
Next, copy the supplied systemd unit file into place. Modify it accordingly.
|
1 2 |
# cp /usr/local/src/pdns-3.4.3/contrib/systemd-pdns.service /etc/systemd/system/pdns.service # vi /etc/systemd/system/pdns.service |
My complete pdns.service unit file looks like this:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[Unit] Description=PowerDNS Authoritative Server Wants=network-online.target After=network-online.target mysqld.service postgresql.service slapd.service [Service] Type=forking ExecStart=/usr/local/pdns/sbin/pdns_server --daemon ExecStop=/usr/local/pdns/bin/pdns_control quit Restart=on-failure RestartSec=2 PrivateTmp=true [Install] WantedBy=multi-user.target |
Ensure the paths to ExecStart and ExecStop are correct for your installation. Next, we’ll configure the master and slave servers.
Next, we’ll add a pdns user (and implicitly a pdns group) that the server will run as:
|
1 |
# useradd -s /sbin/nologin -d /dev/null pdns |
Master Server Configuration
Create /usr/local/pdns/etc/pdns.conf as follows:
|
1 |
# vi /usr/local/pdns/etc/pdns.conf |
Let’s have a look. It’s a *very* minimal pdns.conf with just enough configuration to get the BIND backend up and running.
|
1 2 3 4 5 6 7 8 9 10 |
# cat /usr/local/pdns/etc/pdns.conf allow-axfr-ips=10.1.1.172 bind-check-interval=300 launch=bind bind-config=/usr/local/pdns/etc/bindbackend.conf local-address=10.1.1.171 master=yes setgid=pdns setuid=pdns version-string=anonymous |
The configuration is fairly self-explanatory. We allow AXFR from our slave server IP address, specify that the backend should check for zonefile updates every 300 seconds, then launch the bind backend with the configuration file /usr/local/pdns/etc/bindbackend.conf. This configuration file is essentially a minimal named.conf-format file. More on that soon. We then specify the local bind address (i.e. the local IP address of the interface to which you want PowerDNS to bind to), define that this server is indeed a master, and then set the server up to run as UID pdns and GID pdns. We then specify version-string to be anonymous so that a dig CH TXT version.bind @nameserver doesn’t give the script kiddies anything to go on.
Next we’ll create our bind-config file:
|
1 |
# vi /usr/local/pdns/etc/bindbackend.conf |
And let’s have a look at what’s been configured:
|
1 2 |
# cat /usr/local/pdns/etc/bindbackend.conf zone "example.com" { type master; file "/zones/example.com.zone"; }; |
As you can see, it’s just a minimal named.conf - I’ve specified a single zone - example.com. Don’t get fancy and use a lot of BIND configuration as PowerDNS will ignore it. PowerDNS is configured in pdns.conf!
Let’s make our zonefile directory, and create our first zonefile:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# mkdir /zones # vi /zones/example.com.zone # chown -R pdns:pdns /zones # cat /zones/example.com.zone $TTL 180 ; ; Zone file for example.com ; ; @ IN SOA ns1.example.com. postmaster.example.com. ( 2014080704 ; Serial Number (date YYYYMMDD++) 86400 ; Refresh (24 hours) 1800 ; Retry (1/2 hour) 3600000 ; Expire (42 days) 21600) ; Minimum (6 hours) IN NS ns1.example.com. IN NS ns2.example.com. @ IN A 192.168.0.1 IN MX 0 mail.example.com. www IN A 192.168.0.1 mail IN A 192.168.0.2 ns1 IN A 10.1.1.171 ns2 IN A 10.1.1.172 |
Next, ensure the appropriate firewall ports are open. For this, I open port 53/udp for queries, and port 53/tcp for AXFR and larger queries.
|
1 2 3 4 |
# firewall-cmd --add-port=53/tcp # firewall-cmd --add-port=53/tcp --permanent # firewall-cmd --add-port=53/udp # firewall-cmd --add-port=53/udp --permanent |
Let’s start the master server, and verify correct operation:
|
1 2 3 4 5 |
# systemctl start pdns # systemctl is-active pdns active # dig +short SOA example.com @centos-ns1 ns1.example.com. postmaster.example.com. 2014080704 86400 1800 3600000 21600 |
Now, we can move onto slave server configuration.
Slave Server Configuration
Create /usr/local/pdns/etc/pdns.conf as follows:
|
1 |
# vi /usr/local/pdns/etc/pdns.conf |
Again, it’s a minimal pdns.conf with just enough configuration to get the BIND backend up and running as a slave server.
|
1 2 3 4 5 6 7 8 |
launch=bind bind-config=/usr/local/pdns/etc/bindbackend.conf bind-check-interval=300 local-address=10.1.1.172 setgid=pdns setuid=pdns slave=yes version-string=anonymous |
The only difference between this and the master’s pdns.conf is that we specify slave=yes instead of master=yes. Let’s take a look at bindbackend.conf on the slave:
|
1 |
zone "example.com" { type slave; masters { 10.1.1.171; }; file "/zones/example.com.zone"; }; |
Create the zone directory and set appropriate permissions:
|
1 2 |
# mkdir /zones # chown pdns:pdns /zones |
Again, this is a very simple BIND named.conf-format configuration file. Open the firewall and start the server:
|
1 2 3 4 5 |
# firewall-cmd --add-port=53/tcp # firewall-cmd --add-port=53/tcp --permanent # firewall-cmd --add-port=53/udp # firewall-cmd --add-port=53/udp --permanent # systemctl start pdns |
Checking /var/log/messages, you should first note the following error as the zone has not yet been transferred:
|
1 2 3 |
Apr 4 22:39:36 centos-ns2 pdns[45445]: [bindbackend] error at Sat Apr 4 22:39:36 2015 parsing 'exam ple.com' from file '/zones/example.com.zone': Unable to open file '/zones/example.com.zone': No such file or directory |
You should note that the slave considers the zone stale, and initiates an AXFR:
|
1 2 3 4 5 6 7 8 9 |
Apr 4 22:39:36 centos-ns2 pdns[45445]: Domain 'example.com' is stale, master serial 2014080704, our serial 0 ... Apr 4 22:39:36 centos-ns2 pdns[45445]: Initiating transfer of 'example.com' from remote '10.1.1.171' ... Apr 4 22:39:36 centos-ns2 pdns[45445]: AXFR started for 'example.com' Apr 4 22:39:36 centos-ns2 pdns[45445]: Transaction started for 'example.com' Apr 4 22:39:36 centos-ns2 pdns[45445]: Zone 'example.com' (/zones/example.com.zone) reloaded Apr 4 22:39:36 centos-ns2 pdns[45445]: AXFR done for 'example.com', zone committed with serial number 2014080704 |
Now we can query the slave, and the zone’s RRs return as expected:
|
1 2 |
# dig +short SOA example.com @centos-ns2 ns1.example.com. postmaster.example.com. 2014080704 86400 1800 3600000 21600 |
Adding a New Zone
In order to add a new zone online without restarting the servers, follow this procedure. First, on the master, create the new zonefile and update bindbackend.conf.
|
1 2 3 4 5 |
# cd /zones # sed 's/com/biz/g' example.com.zone > example.biz.zone # chown pdns:pdns example.biz.zone # sed -n '$p' /usr/local/pdns/etc/bindbackend.conf zone "example.biz" { type master; file "/zones/example.biz.zone"; }; |
Verify that the zone is loaded:
|
1 2 3 |
# dig +short NS example.biz @centos-ns1 ns2.example.biz. ns1.example.biz. |
Next, use pdns_control to load the zone:
|
1 2 |
# /usr/local/pdns/bin/pdns_control bind-add-zone example.biz /zones/example.biz.zone Loaded zone example.biz from /zones/example.biz.zone |
On the slave, update bindbackend.conf as follows:
|
1 2 |
# sed -n '$p' /usr/local/pdns/etc/bindbackend.conf zone "example.biz" { type slave; masters { 10.1.1.171; }; file "/zones/example.biz.zone"; }; |
Run a rediscover:
|
1 2 |
# /usr/local/pdns/bin/pdns_control rediscover Ok Done parsing domains, 1 rejected, 1 new, 0 removed |
And next, retrieve:
|
1 2 |
# /usr/local/pdns/bin/pdns_control retrieve example.biz Added retrieval request for 'example.biz' from master 10.1.1.171 |
You should now be able to query the slave:
|
1 2 3 |
# dig +short NS example.biz @centos-ns2 ns2.example.biz. ns1.example.biz. |
Updating a Zone
Updating a zone is a simple case of modifying the zonefile, incrementing the serial and either waiting for bind-check-interval to pass, or issuing a pdns_control bind-reload-now:
|
1 2 |
# /usr/local/pdns/bin/pdns_control bind-reload-now example.net example.biz: parsed into memory at Sat Apr 4 22:46:15 2015 |
You should see lines along the following in /var/log/messages:
|
1 2 3 4 5 6 7 |
Apr 4 22:54:22 centos-ns1 pdns[35977]: Zone 'example.net' (/zones/example.net.zone) reloaded Apr 4 22:54:32 centos-ns1 pdns[35977]: 1 domain for which we are master needs notifications Apr 4 22:54:36 centos-ns1 pdns[35977]: Queued notification of domain 'example.net' to 10.1.1.172:53 Apr 4 22:54:37 centos-ns1 pdns[35977]: AXFR of domain 'example.net' initiated by 10.1.1.172 Apr 4 22:54:37 centos-ns1 pdns[35977]: AXFR of domain 'example.net' allowed: client IP 10.1.1.172 is in allow-axfr-ips Apr 4 22:54:37 centos-ns1 pdns[35977]: AXFR of domain 'example.net' to 10.1.1.172 finished Apr 4 22:54:37 centos-ns1 pdns[35977]: Removed from notification list: 'example.net' to 10.1.1.172:53 (was acknowledged) |
The slave will now be up-to-date.