PowerDNSでRFC7706

## はじめに

DNS Advent Calendar 2015 の15日目の記事です。

[RFC 7706(Decreasing Access Time to Root Servers by Running One on Loopback)]("https://tools.ietf.org/html/rfc7706") は、DNSキャッシュサーバのloopbackにてルートサーバ（スレーブ）を動かすことについて記載しています。このRFCのAppendix Bには、

* Bind
* Unbound/NSD
* Microsoft Windows Server 2012

の設定例がありますが、PowerDNSの設定例はありません。ここではPowerDNSでRFC7706を設定してみます。

## 設定方法

### 環境

ここでは以下の環境を使用します。

* CentOS 6.7
* PowerDNS Authoritative Server 3.4.7
* PowerDNS Recursor 3.7.3
* MySQL 5.1.73
* 192.167.33.0/24に対して、フルリゾルバを提供する。

### MySQL

RPMのMySQLパッケージをインストールし、mysqldを起動します。

    $ sudo yum install mysql-server
    $ sudo service mysqld start
    $ sudo chkconfig mysqld on

PowerDNS用データベースを作成します。

    $ mysql -uroot 
    > CREATE DATABASE pdns;
    > GRANT ALL ON pdns.* TO pdns@127.0.0.1 identified by 'pdns_password';
    > quit

[Generic MySQL and PostgreSQL backends(doc.powerdns.com)]("https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/")の"The default setup conforms to the following schema:"を入力します。powerdns_mysql_backend_schema.sqlは[ここ(gist)](http://gist.githubusercontent.com/sischkg/981870b44c7555648a03/raw/31e53d69fe84349ac29ad43b18ece29bc2576733/powerdns_mysql_backend_schema.sql)にあります。

    $ mysql -h127.0.0.1 -updns -p pdns < powerdns_mysql_backend_schema.sql

ルートゾーンの設定をdomainsテーブルに追加します。この時、

* マスターサーバには、ルートサーバのIPアドレスをカンマ区切りで指定
* nameは"."ではなく*""（空文字列）*を指定
  + nameに"."を指定すると、ルートサーバからゾーン転送でデータを取得してきますが、ゾーン外のデータとして無視されてしまいます。

を指定します。
 
    $ mysql -h127.0.0.1 -updns -p pdns
    > INSERT INTO domains
        (name,type,master)
        values('',
               'SLAVE',
               '192.228.79.201,192.33.4.12,192.5.5.241,192.112.36.4,193.0.14.129,192.0.47.132,192.0.32.132');
    > quit

### PowerDNS Authoritative Server

まず、PowerDNS Authoritative Serverをコンパイルするためのライブラリをインストールします。

    $ sudo yum install boost-devel mysql-devel

[PowerDNS.com]("https://www.powerdns.com/downloads.html")からPowerDNS Authoritative Serverをダウンロードします。

    $ wget https://downloads.powerdns.com/releases/pdns-3.4.7.tar.bz2

tarballを展開し、コンパイル・インストールします。

    $ tar xjf pdns-3.4.7.tar.bz2
    $ cd pdns-3.4.7
    $ ./configure
    $ make
    $ sudo make install
    $ sudo cp pdns/pdns /etc/init.d/
    $ sudo chkconfiig --add pdns

PowerDNS用のユーザを追加します。

    $ sudo groupadd pdns
    $ sudo useradd -g pdns pdns

PowerDNS Authoritative Serverの設定ファイルを作成します。

    $ sudo cp /usr/local/etc/pdns.conf-dist /usr/local/etc/pdns.conf
    $ sudo vi /usr/local/etc/pdns.conf

次の設定を追加します。

* daemonとして動作
* loopback 127.12.12.12をbind
* gidはpdns
* uidはpdns
* slave機能を有効
* バックエンドはMySQL

設定ファイルには以下の行を追加します。

    daemon=yes
    local-address=127.12.12.12
    setgid=pdns
    setuid=pdns
    slave=yes
    
    launch=gmysql
    gmysql-host=127.0.0.1
    gmysql-user=pdns
    gmysql-dbname=pdns
    gmysql-password=pdns_password

pdnsを起動します。

    $ sudo service pdns start
    $ sudo chkconfig pdns on

pdnsは起動後にルートゾーンのゾーン転送を行い、成功すれば以下のようなログを出力します。

    $ less /var/log/messagges

    Dec 13 13:20:36 localhost pdns[13016]: 1 slave domain needs checking, 0 queued for AXFR
    Dec 13 13:20:37 localhost pdns[13016]: Received serial number updates for 1 zones, had 0 timeouts
    Dec 13 13:20:37 localhost pdns[13016]: Domain '' is stale, master serial 2015121300, our serial 0
    Dec 13 13:20:37 localhost pdns[13016]: Initiating transfer of '' from remote '193.0.14.129'
    Dec 13 13:20:37 localhost pdns[13016]: Done launching threads, ready to distributequestions
    Dec 13 13:20:37 localhost pdns[13016]: AXFR started for ''
    Dec 13 13:20:37 localhost pdns[13016]: Transaction started for ''
    Dec 13 13:20:39 localhost pdns[13016]: AXFR done for '', zone committed with serial number 2015121300


### PowerDNS Recursor

[PowerDNS.com]("https://www.powerdns.com/downloads.html")からPowerDNS Recursorをダウンロードします。

    $ wget https://downloads.powerdns.com/releases/pdns-recursor-3.7.3.tar.bz2

tarballを展開し、コンパイル・インストールします。

    $ tar xjf pdns-recursor-3.7.3.tar.bz2
    $ cd pdns-recursor-3.7.3
    $ ./configure
    $ make
    $ sudo make install
    $ sudo chkconfig --add pdns-recursor

PowerDNS Recursorの設定ファイルを作成します。

    $ sudo cp /etc/powerdns/recursor.conf-dist /etc/powerdns/recursor.conf
    $ sudo vi /etc/powerdns/recursor.conf

次の設定を追加します。

* 127.0.0.0/8, 192.168.33.0/24からの問い合わせを許可する。
* ルートゾーン(".")は127.12.12.12へ問い合わせる。
* 127.0.0.1と192.168.33.254をbind
* gidはpdns
* uidはpdns

設定ファイルには以下の行を追加します。

    allow-from=127.0.0.0/8, 192.168.33.0/24
    forward-zones=.=127.12.12.12
    local-address=127.0.0.1,192.168.33.254
    setgid=pdns
    setuid=pdns

PowerDNS Recursorを起動します。

    $ sudo service pdns-recursor start
    $ sudo chkconfig pdns-recursor on

## 動作確認

digにてフルリゾルバの動作を確認します。

    $ dig @127.0.0.1 qiita.com a
    
    ; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.37.rc1.el6_7.4 <<>> @127.0.0.1 qiita.com a
    ; (1 server found)
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50249
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;qiita.com.			IN	A
    
    ;; ANSWER SECTION:
    qiita.com.		60	IN	A	176.34.54.212
    
    ;; Query time: 555 msec
    ;; SERVER: 127.0.0.1#53(127.0.0.1)
    ;; WHEN: Sun Dec 13 14:20:41 2015
    ;; MSG SIZE  rcvd: 43


tcpdumpにてloopbackのDNSサーバへ問い合わせしていることを確認します。

    $ sudo tcpdump -i lo -n port 53
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
    14:20:40.867466 IP 127.0.0.1.56788 > 127.0.0.1.domain: 50249+ A? qiita.com. (27)
    14:20:40.867654 IP 127.12.12.12.11423 > 127.12.12.12.domain: 64624 A? qiita.com. (27)
    14:20:40.872687 IP 127.12.12.12.domain > 127.12.12.12.11423: 64624- 0/13/13 (483)
    14:20:41.151102 IP 127.12.12.12.45802 > 127.12.12.12.domain: 46524 A? ns-1049.awsdns-03.org. (39)
    14:20:41.154018 IP 127.12.12.12.domain > 127.12.12.12.45802: 46524- 0/6/12 (441)
    14:20:41.422804 IP 127.0.0.1.domain > 127.0.0.1.56788: 50249 1/0/0 A 176.34.54.212 (43)

## 最後に

これでキャッシュサーバとして利用できます。ただし、PowerDNS Recursor 3.7.3にはDNSSEC Validationの機能はありませんので、必要な場合はunboundなどを利用してください。