本当にどうでもいいメモなどをたまに書く程度。

PowerDNS で DNS コンテンツサーバー構築。バックエンドは MariaDB でレプリケーション

日付:

ぶっちゃけ「ALIAS レコードが使いたい」というだけの理由で、PowerDNS に乗り換えた。
だが本気で乗り換えてみれば、PDNS Manager でレコード編集は楽だし、バックエンドを MariaDB にしてレプリケーションさせればゾーンの増減時にスレーブでの作業が不要になってさらに楽。
ただし構築は NSD と比べて手間がかかる。


マスターが master.example.com (192.0.2.1)、スレーブが slave.example.com (198.51.100.1) という構成を想定して書いていく。
DNS 的なゾーン転送 (AXFR) をするわけではないのだが、一応。

マスターはメモリ 2GB で nginx 等も動作しており、スレーブはメモリ 512MB で DNS のみを担当という想定。想定というか実際そうなのだけれども。

ALIAS レコードを使うので、127.0.0.1:53 に PowerDNS Recursor を動かす。

手順は Debian でも Ubuntu でもほぼ同じ。実際に私のスレーブのうち1台は Ubuntu である。
(ただし /etc/mysql/my.cnf だけなぜか Ubuntu だとないので、Debian からコピーした)


MariaDB、PowerDNS の導入

マスター、スレーブで共通の作業。

MariaDB は Debian 収録物ではなく MariaDB Repositories で導入する。
ただ add-apt-repository は好みじゃないので手順を少し変更。

sudo apt install dirmngr
sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xF1656F24C74CD1D8
echo 'deb http://ftp.yz.yamagata-u.ac.jp/pub/dbms/mariadb/repo/10.3/debian stretch main' | sudo tee /etc/apt/sources.list.d/mariadb.list
sudo apt update
sudo apt install mariadb-server

PowerDNS も Debian 収録物ではなく PowerDNS repositories で導入する。

wget https://repo.powerdns.com/FD380FBB-pub.asc -O - | sudo apt-key add -
echo 'deb http://repo.powerdns.com/debian stretch-rec-41 main' | sudo tee /etc/apt/sources.list.d/pdns.list
echo 'deb http://repo.powerdns.com/debian stretch-auth-41 main' | sudo tee -a /etc/apt/sources.list.d/pdns.list
sudo apt update
sudo apt install pdns-recursor pdns-server pdns-backend-mysql

pdns-server が起動失敗するが、この時点ではそれで問題ない。
標準では pdns-server が *:53 を listen するのだが、127.0.0.1:53 を先に pdns-recursor が listen しているため、pdns-server が listen できないのだ。


MariaDB のインスタンスを分ける

マスターでは DNS 以外の仕事も担当しており、MariaDB は PowerDNS のみを収容しているわけではない。
しかし MariaDB の REPLICATION SLAVE 権限は特定のデータベースのみを対象とする権限ではなくグローバル権限なので、スレーブがマスターのデータベースすべてをレプリケーションする権限を持ってしまう (権限を持った上で、スレーブ側の設定でレプリケーションするデータベースを絞る、という形になる)。
これはセキュリティ上好ましくないので、PowerDNS 専用の MariaDB インスタンスを別に立ち上げる。

MariaDB を複数インスタンス起動する手段として、ちょっと古い記事だと mysqld_multi を使う説明が多いのだが、mariadb.service の代わりに mariadb@.service を使うという手段がある。
systemctl start mariadb@HOGE すると、mysqld --defaults-file=/etc/mysql/conf.d/myHOGE.cnf が実行される仕組み。この場合 /etc/mysql/my.cnf は読み込まれない。
なので /etc/mysql/my.cnf から末尾の !include-dir を除去したものを /etc/mysql/conf.d/mymariadb.cnf に置く。!include-dir /etc/mysql/conf.d なので除去しないと無限ループ。
!include の方は残してもいいが、PowerDNS 専用インスタンスはすべて mypdns.cnf に記述するので、!include も除去して mymariadb.cnf だけで済ませる方が統一感があるように思われる。

PowerDNS 専用インスタンスは次の設定とする。

  • ソケット /var/run/mysql/mysqld_pdns.sock
  • PID /var/run/mysql/mysqld_pdns.pid
  • ポート 3307
  • データディレクトリ /var/lib/mysql_pdns
  • バイナリログ /var/log/mysql/mariadb_pdns_bin
  • server-id 1
sudo systemctl stop mariadb
sudo systemctl disable mariadb
sed -e '/^!include/d' /etc/mysql/my.cnf | sudo tee /etc/mysql/conf.d/mymariadb.cnf
sed -e '/^!include/d' -e 's/\/var\/lib\/mysql/\/var\/lib\/mysql_pdns/g' -e 's/mysqld.\(sock\|pid\)/mysqld_pdns.\1/g' -e 's/3306/3307/g' 's/#\(server-id\)/\1/' -e 's/mariadb-bin/mariadb_pdns_bin/g' /etc/mysql/my.cnf | sudo tee /etc/mysql/conf.d/mypdns.cnf
sudo mysql_install_db --datadir=/var/lib/mysql_pdns
sudo systemctl enable mariadb@mariadb mariadb@pdns
sudo systemctl start mariadb@mariadb mariadb@pdns

PDNS Manager を使うので、空のデータベース pdns を作っておく。
同時にユーザー pdns@localhost、レプリケーション用ユーザー pdnsrepl@localhost も作る。

root のパスワードが空なので、これもパスワードを設定しておく。
mariadb を導入した時点で root のパスワードをつけたじゃないかって? それは mariadb@mariadb における root のパスワードで、mariadb@pdns における root のパスワードではない。

PowerDNS 慣れしている人はここでテーブルを作りそうになると思うが、PDNS Manager がテーブルを作るので、空のまま。

sudo mysql -P 3307 -h 127.0.0.1
CREATE DATABASE pdns;
GRANT USAGE ON *.* TO pdns@localhost IDENTIFIED BY 'パスワード';
GRANT ALL ON pdns.* TO pdns@localhost;
GRANT REPLICATION SLAVE ON *.* TO pdnsrepl@localhost IDENTIFIED BY 'パスワード';
UPDATE mysql.user SET password=PASSWORD('パスワード') WHERE user='root';
FLUSH PRIVILEGES;
\q

スレーブでもデータベース pdns、ユーザー pdns@localhost を作っておく。

sudo mysql -p
CREATE DATABASE pdns;
GRANT USAGE ON *.* TO pdns@localhost IDENTIFIED BY 'パスワード';
GRANT ALL ON pdns.* TO pdns@localhost;
\q

PDNS Manager の導入

マスターでの作業。

PDNS Manager を入れる。
公式サイトの説明が今時珍しく Apache での説明しか書かれていないので、nginx での設定を書いておく (PHP は php-fpm を使っている)。

server {
	server_name pdns.example.com;

	root /var/www/html/backend/public;
	index index.html index.php;

	location / {
		root /var/www/html/frontend;
		try_files $uri $uri/ /index.html;
	}

	location /api {
		try_files $uri $uri/ /index.php;
	}

	location ~ [^/]\.php(/|$) {
		if ($request_uri ~* "/api(.*)") {
			set $req $1;
		}
		fastcgi_pass unix:/run/php/php7.3-fpm.sock;
		fastcgi_split_path_info ^(/api)(/.*)$;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME $request_filename;
		fastcgi_param PATH_INFO $fastcgi_path_info;
		fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
		fastcgi_param REQUEST_URI $req;
	}
}

https://pdns.example.com/setup をブラウザーで開き、初期設定を行う。
基本的に Getting Started の通りだし、ぶっちゃけ画面見ればどこに何を入力すべきか一目瞭然だが、注意点が一つ。

Database の Host は localhost ではなく 127.0.0.1 とする

127.0.0.1 を指定しないと mariadb@pdns ではなく標準の方 (mariadb@mariadb) につながってしまうため。
コマンドラインで mysql -P 3307 -h 127.0.0.1 としていたのも同じ理由による。

ドメインの追加は、Add domain | MASTER | NATIVE | SLAVE とある中で、NATIVE で行う。
PowerDNS でゾーン転送 (AXFR) するのではなく、バックエンドである MariaDB を使って同期するためだ。


ssh port forwarding の設定

MariaDB は標準の設定ファイルでは localhost しか listen しないし、これを開放するのはセキュリティ上好ましくない。
なので、スレーブからマスターに ssh port forwarding で接続できるよう設定する。
レプリケーション用のシステムアカウントを作り、autossh で接続を維持する。

マスター、スレーブで共通の作業:

sudo useradd -r -s /usr/sbin/nologin -d /etc/powerdns/pdnsrepl pdnsrepl
sudo mkdir /etc/powerdns/pdnsrepl
sudo chown pdnsrepl:pdnsrepl /etc/powerdns/pdnsrepl
sudo chmod 700 /etc/powerdns/pdnsrepl
sudo -u pdnsrepl mkdir /etc/powerdns/pdnsrepl/.ssh
sudo -u pdnsrepl chmod 700 /etc/powerdns/pdnsrepl/.ssh

スレーブでの作業:

sudo -u pdnsrepl ssh-keygen -t ed25519
sudo apt install autossh

スレーブの /etc/powerdns/pdnsrepl/.ssh/id_25519.pub の内容を、マスターの /etc/powerdns/pdnsrepl/.ssh/authorized_keys に貼り付け。
もちろん authorized_keys は 600 にする。

スレーブで /etc/systemd/system/autossh-pdnsrepl.service を作成。

[Unit]
Description=AutoSSH port forwarding for PowerDNS DB replication

[Service]
ExecStart=/usr/bin/sudo -u pdnsrepl /usr/bin/autossh -f -M 0 -N -L 3307:127.0.0.1:3307 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" pdnsrepl@master.example.com
ExecStop=/usr/bin/killall -KILL autossh
Type=forking

[Install]
WantedBy=multi-user.target

スレーブでの作業:

sudo -u pdnsrepl ssh pdnsrepl@master.example.com
sudo systemctl enable autossh-pdnsrepl
sudo systemctl start autossh-pdnsrepl

これで、スレーブで 127.0.0.1:3307 に接続するとマスターの 127.0.0.1:3307 につながるようになった。


レプリケーションの設定

マスターでの作業:

sudo mysql -P 3307 -h 127.0.0.1 -p
FLUSH TABLES WITH READ LOCK;
\q
sudo mysqldump  -P 3307 -h 127.0.0.1 -p pdns --lock-all-tables > pdns.db
sudo mysql -P 3307 -h 127.0.0.1 -p
UNLOCK TABLES;
SHOW MASTER STATUS;
\q

SHOW MASTER STATUS; で表示されたファイル名とポジションをメモりつつ、できた pdns.db をスレーブにコピーする。

スレーブでの作業:

sudo mysql pdns -p < pdns.db
sudo systemctl stop mariadb

/etc/mysql/mariadb.conf.d/slave.cnf を作成。
マスターでは my.cnf をベースに編集したファイルを mariadb@.service で直接読み込ませていたが、スレーブでは MariaDB インスタンスを分けず mariadb.service で起動するため、最小限の追加で済ませる。

[mysqld]
server_id = 2
read_only
replicate_do_db = pdns

スレーブでの作業:

sudo systemctl start mariadb
sudo mysql -p
CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3307, MASTER_USER='pdnsrepl', MASTER_PASSWORD='(マスターで pdnsrepl@localhost につけたパスワード)', MASTER_LOG_FILE='(マスターの SHOW MASTER STATUS; で表示されたファイル名)', MASTER_LOG_POS=(マスターの SHOW MASTER STATUS; で表示されたポジションの数字);
START SLAVE;
\q

PowerDNS の設定

/etc/powerdns/pdns.d/mysql.conf を作成。

マスター:

launch+=gmysql
gmysql-dbname=pdns
gmysql-user=pdns
gmysql-password=パスワード
gmysql-socket=/var/run/mysqld/mysqld_pdns.sock

スレーブ:

launch+=gmysql
gmysql-dbname=pdns
gmysql-user=pdns
gmysql-password=パスワード
gmysql-socket=/var/run/mysqld/mysqld.sock

/etc/powerdns/pdns.conf を編集。
変更点は以下の通り。

  • expand-alias=yes
  • local-address は外側 IP アドレス (本記事の想定ではマスター 192.0.2.1、スレーブ 198.51.100.1) に設定する。
  • IPv6 がない場合、local-ipv6= にする。
  • resolver=127.0.0.1

pdns-server を起動。

sudo systemctl start pdns

完了。

comments powered by Disqus