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


はじめに

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

前提

  • raspbian wheezy
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

今回、Raspberry Piが初代の古いもののためか、raspberrypi.orgのRASPBIAN JESSIE だとうまくsshで接続できなかったので、wheezyを使っています。

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

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

PowerDNSのインストール

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

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

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

依存のインストール

# 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もインストールします。

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

# cd pdns-4.0.1
# ./configure
# make
# make install

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

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

MySQLの設定

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

# 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を複製して編集します。

# cp /usr/local/etc/pdns.conf-dist /usr/local/etc/pdns.conf

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

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の設定でも使います。

起動

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

# 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を実行してみます。

# 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を利用します。

# cp pdns/pdns.init /etc/init.d/pdns
# chmod +x /etc/init.d/pdns

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

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

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

# 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してきます。

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

MySQLの設定

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

# mysql -uroot -p -e 'CREATE DATABASE powerdnsadmin'

依存のインストール

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

# 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

設定

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

(flask)# cp config_template.py config.py

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

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

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

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

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

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

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

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

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

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

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

Nginxのインストール

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

# apt-get install nginx

VirtualHostとプロキシの設定

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

pdns.rp.local.conf
server {
        listen 80;
        server_name pdns.rp.local;

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

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

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

# service nginx configtest
Testing nginx configuration: nginx.
# service nginx restart

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

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

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

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

あとがき

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

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