Raspberry PiにPowerDNSをいれて自宅DNSサーバにした話

#はじめに

手元に余っているRaspberry Pi 1 Model Aがあったので、自宅のLAN内で動作するDNSサーバをたてた話をします。

#前提

- raspbian wheezy

```shell-session
root@raspberry-pi:~# lsb_release -da
No LSB modules are available.
Distributor ID:	Debian
Description:	Debian GNU/Linux 7.11 (wheezy)
Release:	7.11
Codename:	wheezy
```

- [PowerDNS](https://github.com/PowerDNS/pdns) 4.0.1
- [PowerDNS-Admin](https://github.com/ngoduykhanh/PowerDNS-Admin) githubのmasterブランチ

今回、Raspberry Piが初代の古いもののためか、[raspberrypi.org](https://www.raspberrypi.org/downloads/raspbian/)のRASPBIAN JESSIE だとうまくsshで接続できなかったので、wheezyを使っています。

また、ドメイン登録のためのWebFrontEndにPowerDNS-Adminを使いたかったので、WebAPIの機能からexperimentalが抜けている現時点で最新のPowerDNS 4.0.1を [ソースコード](https://downloads.powerdns.com/releases/)からコンパイルしてインストールしています。

**全体としてとりあえず動かすことを重視しています。セキュリティ的な観点が欠落しているので、ご留意ください。**

# PowerDNSのインストール

## ソースコードのダウンロード

前提で話したように、現時点で最新のバージョンを使いたかったので、4.0.1のソースコードをダウンロードして解凍する。

```shell-session
# wget https://downloads.powerdns.com/releases/pdns-4.0.1.tar.bz2
# tar xvfj pdns-4.0.1.tar.bz2
```

## 依存のインストール

```shell-session
# apt-get update && apt-get upgrade
# apt-get install g++ libboost-all-dev libtool make pkg-config libmysqlclient-dev libssl-dev
# apt-get install flex
# apt-get install libbison-dev
# apt-get install libmysqld-dev mysql-server mysql-client
```

バックエンドにgmysqlを使うので、MySQLもインストールします。

## コンパイルとインストール

```shell-session
# cd pdns-4.0.1
# ./configure
# make
# make install
```

`./configure --help`すると、いろいろなオプションがでるので、必要なものを設定してください。今回は、とくに設定を加えることなく、そのままコンパイルしました。

また、コンパイル途中でエラーがでたら、エラーを確認して、足りないライブラリなどをインストールしてください。実際、コンパイルしている時に、boostに関連するエラーがでたので、`libboost-dev`から`libboost-all-dev`を追加でインストールして解決しました。

## MySQLの設定

DNSのレコードを管理するデータベースとテーブルをMySQL上に作成します。今回は、面倒だったので、rootで管理するようにしてしまっていますが、実際はセキュリティのために専用のMySQLのユーザを作成してください。

```shell-session
# mysql -uroot -p -e 'CREATE DATABASE powerdns'
# mysql -uroot -p powerdns < modules/gmysqlbackend/schema.mysql.sql
```

ソースコードの中の`modules/gmysqlbackend/schema.mysql.sql`にスキーマ情報があるので、それを実行します。

## 設定

gmysqlを使うための設定と、PowerDNS-Adminを使うためのAPIの設定をします。
ソースコードからコンパイルしてインストールした場合は、設定ファイルは`/usr/local/etc/`になります。`pdns.conf-dist`を複製して編集します。

```shell-session
# cp /usr/local/etc/pdns.conf-dist /usr/local/etc/pdns.conf
```

編集箇所は以下になります。

```diff
diff pdns.conf-dist pdns.conf
50c50
< # api=no
---
> api=yes
55c55
< # api-key=
---
> api-key=your-powerdns-api-key
245c245
< # launch=
---
> launch=gmysql
540c540
< # webserver=no
---
> webserver=yes
---
> gmysql-host=localhost
> gmysql-dbname=powerdns
> gmysql-user=root
> gmysql-password=yourpassword
```

`api-key`は、PowerDNSのWebAPIを使うための任意の文字列になります。PowerDNS-Adminの設定でも使います。

## 起動

動作確認のために、コマンドから実行して動作を確認します。

```shell-session
# pdns_server
Jan 02 04:28:46 Reading random entropy from '/dev/urandom'
Jan 02 04:28:46 This is a standalone pdns
Jan 02 04:28:46 Listening on controlsocket in '/var/run/pdns.controlsocket'
Jan 02 04:28:46 UDP server bound to 0.0.0.0:53
Jan 02 04:28:46 UDPv6 server bound to [::]:53
Jan 02 04:28:46 TCP server bound to 0.0.0.0:53
Jan 02 04:28:46 TCPv6 server bound to [::]:53
Jan 02 04:28:46 PowerDNS Authoritative Server 4.0.1 (C) 2001-2016 PowerDNS.COM BV
Jan 02 04:28:46 Using 32-bits mode. Built using gcc 4.7.2 on Dec 31 2016 07:28:24 by root@raspberry-pi.
Jan 02 04:28:46 PowerDNS comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it according to the terms of the GPL version 2.
Jan 02 04:28:46 Listening for HTTP requests on 0.0.0.0:8081
Jan 02 04:28:46 Polled security status of version 4.0.1 at startup, no known issues reported: OK
Jan 02 04:28:46 Creating backend connection for TCP
Jan 02 04:28:46 About to create 3 backend threads for UDP
Jan 02 04:28:46 Done launching threads, ready to distribute questions
```

試しに別のshellを起動してdigを実行してみます。

```shell-session
# dig @127.0.0.1

; <<>> DiG 9.8.4-rpz2+rl005.12-P1 <<>> @127.0.0.1
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 8397
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;.				IN	NS

;; Query time: 14 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Jan  2 04:27:27 2017
;; MSG SIZE  rcvd: 17
```

正常に動作していないと、not foundと表示されると思います。

一度、`Ctrl + c`でpdns_serverを終了して、サービス起動スクリプトを用意します。
スクリプトはソースコード内の`pdns/pdns.init`を利用します。

```shell-session
# cp pdns/pdns.init /etc/init.d/pdns
# chmod +x /etc/init.d/pdns
```

複製したスクリプトのコメントの下、変数を定義する最初のところにPowerDNSをインストールしたフォルダのプリフィックスを追加します。

```diff
diff pdns/pdns.init /etc/init.d/pdns
18c18
<
---
> prefix=/usr/local
```

サービスから実行してみます。

```shell-session
# service pdns start
Starting PowerDNS authoritative nameserver: started
# service pdns status
16887: Child running on pid 16889
```

`service pdns status`と実行して、pdnsが動いていることが確認できます。
以上で、PowerDNSの設定は完了です。

# PowerDNS-Adminのインストール

まず、githubからソースコードをcloneしてきます。

```shell-session
# git clone https://github.com/ngoduykhanh/PowerDNS-Admin.git
```

## MySQLの設定

PowerDNS-Adminのデータを管理するようのデータベースをMySQL上に作ります。

```shell-session
# mysql -uroot -p -e 'CREATE DATABASE powerdnsadmin'
```

## 依存のインストール

pythonを使うので、それに関連するものをインストールします。

```shell-session
# apt-get install python-pip
# pip install MySQL-python
# pip install virtualenv
# cd PowerDNS-Admin
# virtualenv flask
# source ./flask/bin/activate
(flask)# pip install -r requirements.txt
```

## 設定

設定ファイルの雛形があるので、複製します。

```shell-session
(flask)# cp config_template.py config.py
```

必要となる設定は、上で作成したMySQLへ接続するための設定とPowerDNSのWebAPIにアクセスするための情報になります。

```diff
diff config_template.py config.py
6,7c6,7
< SECRET_KEY = 'We are the world'
< BIND_ADDRESS = '127.0.0.1'
---
> SECRET_KEY = 'abcdefghijkl' # 適当な文字列
> BIND_ADDRESS = '0.0.0.0' # とりあえず外部からみえるように一時的に
25,27c25,27
< SQLA_DB_USER = 'powerdnsadmin'
< SQLA_DB_PASSWORD = 'powerdnsadminpassword'
< SQLA_DB_HOST = 'mysqlhostorip'
---
> SQLA_DB_USER = 'root'
> SQLA_DB_PASSWORD = 'yourpassword'
> SQLA_DB_HOST = '127.0.0.1'
73,75c73,75
< PDNS_STATS_URL = 'http://172.16.214.131:8081/'
< PDNS_API_KEY = 'you never know'
< PDNS_VERSION = '3.4.7'
---
> PDNS_STATS_URL = 'http://127.0.0.1:8081/'
> PDNS_API_KEY = 'your-powerdns-api-key' # PowerDNSの設定で作成したAPI Keyを記述
> PDNS_VERSION = '4.0.1'
```

## 起動

起動する前に、データベースにスキーマの情報を流します。

```
(flask)# ./create_db.py
```

無事、成功したらPowerDNS-Adminを実行します。

```
(flask)# ./run.py
[INFO]  * Running on http://0.0.0.0:9393/ (Press CTRL+C to quit)
[INFO]  * Restarting with stat
[WARNING]  * Debugger is active!
[INFO]  * Debugger pin code: 246-032-548
```

9393ポートにブラウザでアクセスして、ログイン画面が表示されることを確認してください。

![Screen Shot 2017-01-02 at 14.15.25 1.png](https://qiita-image-store.s3.amazonaws.com/0/20991/c813575f-64b1-44f7-d07c-23cfab14e520.png)

初期ユーザは用意されていないので、`Create an account`をクリックして、ユーザを登録してログインできることを確認します。

# ドメインの登録とドメインを使ったアクセス

次に、せっかくなのでPowerDNS-Adminへドメイン名`pdns.rp.local`を使ってアクセスできるようにします。

## PowerDNS-Adminからドメインを登録

左側のメニューのNew Domainをクリックします。

表示されたページの、`Enter a valid domain name (required)`と書かれている入力エリアに`rp.local`と入力します。他は、そのままで`Submit`をクリックします。

![Screen Shot 2017-01-02 at 14.27.21.png](https://qiita-image-store.s3.amazonaws.com/0/20991/307319d8-33d4-10d6-bbd9-12371f4439d4.png)

登録が完了すると、Dashboardの下部のHosted Domainsに登録したドメインが表示されていることが確認できます。

![Screen Shot 2017-01-02 at 14.27.35.png](https://qiita-image-store.s3.amazonaws.com/0/20991/d1e6b607-189b-fc30-8415-d4eca7d426fd.png)

次に、rp.localのManageをクリックします。ここからレコードを登録することができるのでpdnsという名前でAレコードを追加します。IPアドレスは、Raspberry PiのIPアドレスを登録します。

![Screen Shot 2017-01-02 at 14.28.14.png](https://qiita-image-store.s3.amazonaws.com/0/20991/51fabcec-6c2f-285d-42bd-4c56f474a334.png)

Saveしたあと、右上にある、Apply Changesをクリックします。確認のモーダルが表示されるので、Applyをクリックすると設定が反映されます。

## Nginxのインストール

特に最新のNginxの機能などを使う予定はないので、apt-getで普通にインストールします。

```shell-session
# apt-get install nginx
```

## VirtualHostとプロキシの設定

9393ポートで動いているPowerDNS-Adminをドメイン付きでプロキシします。`/etc/nginx/conf.d`に設定ファイルを追加します。

```nginx:pdns.rp.local.conf
server {
        listen 80;
        server_name pdns.rp.local;

        location / {
                proxy_pass http://localhost:9393;
        }
}
```

必要最低限の設定なので、必要に応じて設定を追加してください。

念のためconfigtestをしてスクリプトに問題がないことを確認したあとNginxを再起動します。

```shell-session
# service nginx configtest
Testing nginx configuration: nginx.
# service nginx restart
```

## クライアント側のDNSの設定

最後にクライアント側の参照するDNSサーバに、Raspberry PiのIPアドレスを追加します。

通常ですと、ルータのDHCPサーバの設定に追加したりすると思うのですが、設定する場所が見つけられなかったので……。今回はMacのネットワーク設定に追加しました。自分の場合は、Raspberry PiのローカルIPが`192.168.179.2`となっているので、これを追加します。

![Screen Shot 2017-01-02 at 14.37.45.png](https://qiita-image-store.s3.amazonaws.com/0/20991/e76a5f4b-b4f3-1468-6531-d29b3a58f70f.png)

ブラウザから、`http://pdns.rp.local/`にアクセスしてページが表示できることを確認します。

![Screen Shot 2017-01-02 at 14.40.52.png](https://qiita-image-store.s3.amazonaws.com/0/20991/f0555289-dc2f-e4d1-c2c0-d69c63f414e4.png)

# あとがき

当初、別のUbuntuマシンにDockerを使ってPowerDNSとPowerDNS-Adminを立てようと考えてました。ところが、libvirtが使っているdnsmasqと53/udpで衝突してしまい、どう頑張っても解決できませんでした。

そこで、Dockerを動かしていないまっさらなRaspberry Pi上のLinuxならできるだろうと思い、なんとか動作させることができました。