CENTOS 7 – USING THE POWERDNS WEB API TO ADD AND EDIT RECORDS

In a previous post I’ve talked about how to install PowerDNS on a CentOS 7 as both recursive and authoritative server for my lab environment.

In this post I’ll explain you how I use the experimental web API to add records to my DNS authoritative server to solve requests for a local domain called artemit.local.

I’m using the official documentation as a reference but also adding more examples so they can be helpful for you (I’ll be updating this post from time to time). The PowerDNS server has a web server listening on the 127.0.0.1 address port 8081 and I’ll use curl to use the API.

Important: Please replace changeme to the API key you had configured

  1. Adding or editing a new zone/domain called artemit.local and assigning a name server called ns1.artemit.local
    curl -X POST --data '{"name":"artemit.local", "kind": "Master","dnssec":false,"soa-edit":"INCEPTION-INCREMENT","masters": [], "nameservers": ["ns1.artemit.local"]}' -v -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones | jq .
  2. Adding or editing a Start Of Authority (SOA) record for the artemit.local domain. The name server is ns1.artemit.local and the contact mail address is hostmaster.artemit.com.es.
    curl -X PATCH --data '{"rrsets": [ {"name": "artemit.local", "type": "SOA", "changetype": "REPLACE", "records": [ {"content": "ns1.artemit.local hostmaster.artemit.com.es 0 10800 3600 604800 3600", "disabled": false, "name": "artemit.local", "ttl": 86400, "type": "SOA", "priority": 0 } ] } ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/artemit.local | jq .
  3. Adding or editing an address record (A) so ns1.artemit.local record has the IP address 192.168.4.4
    curl -X PATCH --data '{"rrsets": [ {"name": "ns1.artemit.local", "type": "A", "changetype": "REPLACE", "records": [ {"content": "192.168.4.4", "disabled": false, "name": "ns1.artemit.local", "ttl": 86400, "type": "A", "priority": 0 } ] } ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/artemit.local | jq .
  4. Adding or editing an alias record (CNAME) so tornasol.artemit.local has the same IP address as ns1.artemit.local.
    curl -X PATCH --data '{"rrsets": [ {"name": "tornasol.artemit.local", "type": "CNAME", "changetype": "REPLACE", "records": [ {"content": "ns1.artemit.local", "disabled": false, "name":"tornasol.artemit.local", "ttl": 86400, "type": "CNAME", "priority": 0 } ] } ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/artemit.local | jq .
  5. Adding or editing a reverse lookup zone/domain if you want to ask for the hostname for an IP address in the 192.168.4.0/24 network:
    curl
    -X POST --data '{"name":"4.168.192.in-addr.arpa",
    "kind":
    "Master","dnssec":false,"soa-edit":"INCEPTION-INCREMENT","masters":
    [], "nameservers": ["ns1.artemit.local"]}' -v -H
    'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones
    | jq .
  6. Adding or editing the SOA for the reverse lookup:
    curl -X PATCH --data '{"rrsets": [ {"name": "4.168.192.in-addr.arpa", "type": "SOA", "changetype": "REPLACE", "records": [ {"content": "ns1.artemit.local hostmaster.artemit.com.es 0 10800 3600 604800 3600", "disabled": false, "name": "4.168.192.in-addr.arpa", "ttl": 86400, "type": "SOA", "priority": 0 } ] } ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/4.168.192.in-addr.arpa | jq .
  7. Adding or editing a reverse PTR record (e.g when asking for the 192.168.4.4 IP address we’ll get ns1.artemit.local
  8. curl -X PATCH --data '{"rrsets": [ {"name": "4.4.168.192.in-addr.arpa", "type": "PTR", "changetype": "REPLACE", "records": [ {"content": "ns1.artemit.local", "disabled": false, "name": "4.4.168.192.in-addr.arpa", "ttl": 86400, "type": "PTR", "priority": 0 } ] } ] }' -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/4.168.192.in-addr.arpa | jq .
  9. Deleting a zone/domain (e.g artemit.local)
curl -X DELETE -v -H 'X-API-Key: changeme' http://127.0.0.1:8081/servers/localhost/zones/artemit.local | jq .

Mmmm. Don’t like these commands? Well I can’t blame you however I find them useful as I’m preparing a Python script to help you to manage PowerDNS easily (check my GitHub account from time to time). Don’t worry, you have some web GUIs and you can always add records using your favourite PostgreSQL client.

Enjoy!

CentOS 7 – Installing and configuring PowerDNS with PostgreSQL

In my lab I like to use DNS servers rather than hosts files and dnsmasq. In the past I’ve used djbdns but I wanted to learn something new so I’ve decided to have a look at PowerDNS.

PowerDNS runs fine, it’s quite easy to install and has great documentation so here are the steps I’ve followed in my CentOS 7 host using PostgreSQL as the database backend (MariaDB/MySQL is also supported!).

In this post I’m using two PowerDNS servers:

  1. DNS authoritative server to answer the DNS requests for my local domain called artemit.local (e.g tornasol.artemit.local)
  2. DNS recursive server to answer and save in cache the DNS requests for other domains like those on Internet (e.g google.com).

PowerDNS is available at the EPEL repository:

yum install -y epel-release

yum -y install pdns pdns-tools pdns-backend-postgresql pdns-recursor net-tools bind-utils jq

We’ll need to create a postgres database so we’ll use the postgres user and execute some commands from the psql CLI. I’ll name the database pdns.

su - postgres

-bash-4.2$ psql
psql (9.2.10)
postgres=# CREATE DATABASE pdns;
CREATE DATABASE
postgres=# \q

We’ll load the schema available in the /usr/share/doc folder. When you install the pdns package the version number may have been changed so replace 3.4.4 accordingly:

-bash-4.2$ psql pdns < /usr/share/doc/pdns-backend-postgresql-3.4.4/schema.pgsql.sql

We’ll create a user called pdns and will grant the right permissions. Important!: Replace xxxxxxx with your password 😀

-bash-4.2$ psql
postgres=# \c pdns
pdns=# CREATE USER pdns WITH PASSWORD 'xxxxxxx';
CREATE ROLE
pdns=# GRANT ALL ON DATABASE "pdns" TO pdns;
pdns=# GRANT ALL ON ALL TABLES IN SCHEMA public TO pdns;
pdns=# GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO pdnsGRANT
pdns=#\c postgres
postgres=# \q

Let’s close the postgres user session:

-bash-4.2$ exit

We need to edit the following line in the /var/lib/pgsql/data/pg_hba.conf file, and change the ident keyword to  trust:

host all all 127.0.0.1/32 trust

Let’s restart the PostgreSQL server:

systemctl restart postgresql.service

Now we’ll modify the /etc/pdns/pdns.conf file adding the following lines at the end, but first let’s explain what we’re doing:

  • We’re the database backend. My PostgreSQL server is listening in the 127.0.0.1 IP address by default. The database is named pdns and the user is named pdns too. The pdns password should be changed with the one you’ve set before!
  • My server is listening for authoritative DNS requests on the 192.168.4.4 address (local address).
  • My server is listening for recursive DNS requests on the 127.0.0.1 address (recursor)
  • In my example I’m allowing the hosts on the 192.168.4.0/24 to query my DNS recursive server.
  • We’re using a experimental web API to add DNS records. The web API uses by default the changeme password, but you can use another one!
launch=gpgsql
allow-recursion=192.168.4.0/24
recursor=127.0.0.1
local-address=192.168.4.4
gpgsql-host=127.0.0.1
gpgsql-dbname=pdns
gpgsql-user=pdns
gpgsql-password=xxxxxxx
experimental-json-interface=yes
experimental-api-key=changeme
webserver=yes

We also need to add the following entry to the /etc/pdns-recursor/recursor.conf file so recursive queries are allowed for the localhost and the 192.168.4.0/24 network:

allow-from=127.0.0.0/8, 192.168.4.0/24

Now we’ll enable the pdns and pdns-recursor to start at boot time:

systemctl enable pdns.service
systemctl enable pdns-recursor.service

Let’s start the services:

systemctl start pdns.service
systemctl start pdns-recursor.service

Let’s check the status of both servers:

systemctl -l status pdns.service
pdns.service - PowerDNS Authoritative Server
Loaded: loaded (/usr/lib/systemd/system/pdns.service; enabled)
Active: active (running) since dom 2015-04-05 18:08:09 CEST; 985ms ago
[...]

systemctl -l status pdns-recursor.service
pdns-recursor.service - PowerDNS recursing nameserver
Loaded: loaded (/usr/lib/systemd/system/pdns-recursor.service; enabled)
Active: active (running) since dom 2015-04-05 17:36:27 CEST; 33min ago
[...]

Let’s check that both DNS servers are listening on TCP and UDP 53 ports and that the PostgreSQL is being reached by the PowerDNS servers:

netstat -ntuap | grep dns
tcp 0 0 127.0.0.1:8081 0.0.0.0:* LISTEN 15366/pdns_server
tcp 0 0 192.168.4.4:53 0.0.0.0:* LISTEN 15366/pdns_server
tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN 13964/pdns_recursor
tcp 0 0 127.0.0.1:34150 127.0.0.1:5432 ESTABLISHED 15366/pdns_server
tcp 0 0 127.0.0.1:34147 127.0.0.1:5432 ESTABLISHED 15366/pdns_server
tcp 0 0 127.0.0.1:34148 127.0.0.1:5432 ESTABLISHED 15366/pdns_server
tcp 0 0 127.0.0.1:34149 127.0.0.1:5432 ESTABLISHED 15366/pdns_server
udp 0 0 192.168.4.4:53 0.0.0.0:* 15366/pdns_server
udp 0 0 127.0.0.1:53 0.0.0.0:* 13964/pdns_recursor
udp 0 0 127.0.0.1:49594 127.0.0.1:53 ESTABLISHED 15366/pdns_server

Let’s open ports in our firewall. My 192.168.4.0/24 is allowed, that’s why I use it as the source address:

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.4.0/24" port port="53" protocol="udp" accept'

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.4.0/24" port port="53" protocol="tcp" accept'

firewall-cmd --reload

Don’t forget to add or edit the DNS server for your host. For example you can add a DNS1 entry to the network script at /etc/sysconfig/network-scripts/ifcfg-eth0 and change the entry to DNS1=192.168.4.4 or any other IP you’ve set for your PowerDNS server.

Wait a minute! And how I can add entries for my local domain?

There are some web interfaces for PowerDNS but I do love CLI so I’ll using some commands to use the experimental web API to add and edit DNS records. But this post is long enough, continue here to know more.