各種設定ファイル解説
今回利用するOSSは以下の通りです。今回はDNSSEC対応は行わない(正直よく分かってない)。
- キャッシュDNS:PowerDNS Recursor
- 権威DNS:PowerDNS Authoritative Server
- DNS設定用GUI:powerdnsadmin/pda-legacy
- DB:PostgreSQL
- Digテスト:Ubuntu
私の構築環境は以下の通りです。
- Docker Desktop for Windows
- Windows ネットワーク:172.16.1.0/24
- Recursorの設定
- “local”や”home”等は内部DNS(“auth”)で解決する
- 他のドメインは”8.8.8.8″に解決してもらう
- digコマンドによるテストは”dig_test”で行う
- “auth”の設定
- “webapp”のWeb画面からゾーン情報の追加設定を行う
(その際Composeファイル記載の”Auth”のPDNS_AUTH_API_KEYキーが必要になる)
- “webapp”のWeb画面からゾーン情報の追加設定を行う
Docker Composeファイルは以下の通り。Digコマンドによる設定テストは”dig_test”で行っています。
ですが注意点があります。
- 各コンテナで時刻が同期されなければならない
- DNSSECの無効化
- ”healthcheck”による”db”依存のサービスの遅延起動
- “webapp”の原因不明なエラー
1. 各コンテナで時刻が同期されなければならない
Recursorに対してDigコマンドでリクエストを送信した際にエラーが派生しました。エラー内容については忘れましたが調べてみると時刻が同期されていない場合に発生した旨のサイトを見つけたので対応しました。
対応内容は”cap_add”で”SYS_TIME”を指定することです。
ちなみにAuthへ直接問い合わせた場合には発生しなかったので、RecursorからAuthへのforwardする際に同期が必要ということだと考えています。
2. DNSSECの無効化
DNSSECをデフォルト設定のままにした場合にエラーが発生しました。ログを見る限り設定どおりforwardに設定したアドレス(Auth)に陸枝エストしゾーン情報(IPアドレス)を取得できました。
にもかかわらずエラーになったのはDNSSECによるcheckに引っかかったことが原因なようです。(正直DNSSECが何かもよく分かっていません。ポイズン対策と書いてあった気がしますが。)
今回は自宅で構築した上時間がかかってしまっていたのでOffにしました。
3. ”healthcheck"による"db"依存のサービスの遅延起動
DBの調整が済む前に先に”Auth”や”webapp”が起動するとエラーでコンテナが落ちます。
対策としてDBで”healthcheck”セクションでDBの準備OK条件を設定して、DBが必要な各コンテナの”depends_on”で”condition: service_healthy”を設定しています。
4. "webapp"の原因不明なエラー
正直これは原因が分かっていません。
ただきっかけは利用するDBをSQLiteからPostgreSQLに変更した時だったかと記憶しています。(PostgreSQLに変えた理由はSQLiteでセッションに関するトランザクション・エラーが発生したことです。)
現象としてはWebアプリは起動し画面の参照自体は問題なく行えるのですが、ゾーンの追加でエラーが発生したのです。Web画面には”400″or”402″のエラーが表示され、以下のメッセージがログに表示されていました。
“requests.exceptions.MissingSchema: Invalid URL ‘…’: No schema supplied. Perhaps you meant …”
ここで問題なのがこの原因が分かっていないことと、コンテナ軍の削除&再起動を繰り返しているうちにこのエラーが解消されてしまうことです。
恐らくですが先の”db”の”healthcheck”で指定しているコマンドは”auth”のデータベース設定の準備ができていることは確認しているものの、”webapp”のデータベースが準備できていることが確認できていないことが原因かと思われます。
想定通りなら”healthcheck”のコマンド(条件)を変更するか、タイムアウト時間を延ばせば解消するのではないかと睨んでいます。検証はしていません。
docker-compose.yml
version: '3.0'
# Common
x-common-timezone: &COMMON_TZ
"Asia/Tokyo"
services:
# キャッシュDNS
recursor:
image: powerdns/pdns-recursor-master
container_name: pdns_recursor
# restart: always
environment:
PDNS_RECURSOR_API_KEY: ABC_DE_F123
DEBUG_CONFIG: yes
TZ: *COMMON_TZ
ports:
# - "53:53"
# - "53:53/udp"
- "8082:8082"
volumes:
- ./recursor/recursor.conf.yml:/etc/powerdns/recursor.conf:ro
networks:
powerdns:
ipv4_address: 172.23.0.2
cap_add:
- SYS_TIME
# 権威DNS
auth:
image: powerdns/pdns-auth-master
container_name: pdns_auth
# restart: always
environment:
# webappで入力するAPIキー
PDNS_AUTH_API_KEY: ABC_DE_F123
DEBUG_CONFIG: yes
TZ: *COMMON_TZ
ports:
- "8081:8081"
volumes:
- auth_varlib:/var/lib/powerdns/
- ./auth/pdns.conf:/etc/powerdns/pdns.conf:ro
networks:
powerdns:
ipv4_address: 172.23.0.3
depends_on:
db:
condition: service_healthy
cap_add:
- SYS_TIME
# "requests.exceptions.MissingSchema: Invalid URL '...': No schema supplied. Perhaps you meant ..."
# 上記エラーはボリュームを含むコンテナ軍の削除&起動を繰り返すといつか解消される(powerdns_dbのhealthcheck値を伸ばすと良いかも?)
webapp:
image: powerdnsadmin/pda-legacy:latest
container_name: powerdns_admin
# restart: always
ports:
- "9191:80"
logging:
driver: json-file
options:
max-size: 50m
environment:
GUNICORN_TIMEOUT: 60
GUNICORN_WORKERS: 2
GUNICORN_LOGLEVEL: DEBUG
SQLALCHEMY_DATABASE_URI: 'postgresql://powerdnsadmin:powerdnsadmin_password@powerdns_db/powerdnsadmindb'
TZ: *COMMON_TZ
volumes:
- powerdns_web:/data
networks:
powerdns:
ipv4_address: 172.23.0.4
depends_on:
db:
condition: service_healthy
cap_add:
- SYS_TIME
db:
image: postgres:16.3
container_name: powerdns_db
expose:
- 5432
environment:
POSTGRES_DB: powerdb
POSTGRES_ROOT_PASSWORD: redminep
POSTGRES_USER: powerdns
POSTGRES_PASSWORD: powerpass
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
TZ: *COMMON_TZ
volumes:
- db_data:/var/lib/postgresql/data
- ./postgresql/:/docker-entrypoint-initdb.d/
networks:
powerdns:
ipv4_address: 172.23.0.5
cap_add:
- SYS_TIME
healthcheck:
test: ["CMD", "psql", "-U", "powerdns", "powerdb"]
interval: 2s
timeout: 10s
retries: 3
start_period: 8s
dig_test:
image: ubuntu
command: bash -c "apt update -y && DEBIAN_FRONTEND=noninteractive TZ=Asia/Tokyo apt-get install -y dnsutils curl tzdata && bash"
tty: true
environment:
TZ: *COMMON_TZ
networks:
powerdns:
ipv4_address: 172.23.0.6
dns: 172.23.0.2
cap_add:
- SYS_TIME
volumes:
powerdns_auth:
driver: local
auth_varlib:
powerdns_web:
driver: local
db_data:
networks:
powerdns:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.23.0.0/16
gateway: 172.23.0.1
recursor.conf.yml
recursor:
forward_zones:
- zone: local
forwarders:
- 172.23.0.3:53
recurse: true
# notify_allowed: true
- zone: home
forwarders:
- 172.23.0.3:53
recurse: true
# notify_allowed: true
- zone: home.home
forwarders:
- 172.23.0.3:53
recurse: true
# notify_allowed: true
- zone: 0.23.172.in-addr.arpa
forwarders:
- 172.23.0.3:53
forward_zones_recurse:
- zone: .
forwarders:
- 8.8.8.8
# recurse: true
# notify_allowed: true
include_dir: /etc/powerdns/recursor.d
incoming:
listen:
- 0.0.0.0
- '::'
port: 53
allow_from:
- 172.16.1.0/24
- 172.23.0.0/16
# DNSSECサーバは今回動作させえていない
dnssec:
validation: off
# logging:
# loglevel: 7
# common_errors: true
# quiet: false
# trace: yes
pdns.conf
api=yes
webserver=yes
include-dir=/etc/powerdns/pdns.d
# launch=gsqlite3
# gsqlite3-database=/var/lib/powerdns/pdns.sqlite3
local-address=0.0.0.0
local-port=53
# security-poll-suffix=
setgid=pdns
setuid=pdns
primary=no
secondary=no
# DNSゾーン転送を無条件で許可するIPアドレスを書く(PowerDNSキャッシュサーバのIPとRTX830のIP)
allow-axfr-ips=172.23.0.0/16,172.16.1.0/24
allow-dnsupdate-from=172.23.0.0/16
version-string=unknown
# Log
# loglevel=7
# log-dns-queries=yes
# log-dns-details=yes
# PostgreSQLs
launch=gpgsql
gpgsql-host=powerdns_db
gpgsql-port=5432
gpgsql-dbname=powerdb
gpgsql-user=powerdns
gpgsql-password=powerpass
# gpgsql-extra-connection-parameters=
# gpgsql-prepared-statements=yes
gpgsql-dnssec=no
default_schema.sql
このファイルはPostgreSQLで使用されます。Composeファイルでマウントされているディレクトリに配置されているSQLファイルやスクリプトは自動で実行されます。
この機能を利用して”auth”に必要なテーブル等を構築しています。
CREATE TABLE domains (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
master VARCHAR(128) DEFAULT NULL,
last_check INT DEFAULT NULL,
type TEXT NOT NULL,
notified_serial BIGINT DEFAULT NULL,
account VARCHAR(40) DEFAULT NULL,
options TEXT DEFAULT NULL,
catalog TEXT DEFAULT NULL,
CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT)))
);
CREATE UNIQUE INDEX name_index ON domains(name);
CREATE INDEX catalog_idx ON domains(catalog);
CREATE TABLE records (
id BIGSERIAL PRIMARY KEY,
domain_id INT DEFAULT NULL,
name VARCHAR(255) DEFAULT NULL,
type VARCHAR(10) DEFAULT NULL,
content VARCHAR(65535) DEFAULT NULL,
ttl INT DEFAULT NULL,
prio INT DEFAULT NULL,
disabled BOOL DEFAULT 'f',
ordername VARCHAR(255),
auth BOOL DEFAULT 't',
CONSTRAINT domain_exists
FOREIGN KEY(domain_id) REFERENCES domains(id)
ON DELETE CASCADE,
CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT)))
);
CREATE INDEX rec_name_index ON records(name);
CREATE INDEX nametype_index ON records(name,type);
CREATE INDEX domain_id ON records(domain_id);
CREATE INDEX recordorder ON records (domain_id, ordername text_pattern_ops);
CREATE TABLE supermasters (
ip INET NOT NULL,
nameserver VARCHAR(255) NOT NULL,
account VARCHAR(40) NOT NULL,
PRIMARY KEY(ip, nameserver)
);
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
domain_id INT NOT NULL,
name VARCHAR(255) NOT NULL,
type VARCHAR(10) NOT NULL,
modified_at INT NOT NULL,
account VARCHAR(40) DEFAULT NULL,
comment VARCHAR(65535) NOT NULL,
CONSTRAINT domain_exists
FOREIGN KEY(domain_id) REFERENCES domains(id)
ON DELETE CASCADE,
CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT)))
);
CREATE INDEX comments_domain_id_idx ON comments (domain_id);
CREATE INDEX comments_name_type_idx ON comments (name, type);
CREATE INDEX comments_order_idx ON comments (domain_id, modified_at);
CREATE TABLE domainmetadata (
id SERIAL PRIMARY KEY,
domain_id INT REFERENCES domains(id) ON DELETE CASCADE,
kind VARCHAR(32),
content TEXT
);
CREATE INDEX domainidmetaindex ON domainmetadata(domain_id);
CREATE TABLE cryptokeys (
id SERIAL PRIMARY KEY,
domain_id INT REFERENCES domains(id) ON DELETE CASCADE,
flags INT NOT NULL,
active BOOL,
published BOOL DEFAULT TRUE,
content TEXT
);
CREATE INDEX domainidindex ON cryptokeys(domain_id);
CREATE TABLE tsigkeys (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
algorithm VARCHAR(50),
secret VARCHAR(255),
CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT)))
);
CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm);
power_admin.sh
このファイルも”default_schema.sql”と同様にPostgreSQLで使用されます。
この機能を利用して”webapp”のデータベースやユーザを構築しています。
#!/bin/bash
createuser powerdnsadmin -U powerdns
createdb -E UTF8 -l en_US.UTF-8 -O powerdnsadmin -T template0 powerdnsadmindb 'The database for PowerDNS-Admin' -U powerdns
psql -U powerdns powerdnsadmindb <<EOL
ALTER ROLE powerdnsadmin WITH PASSWORD 'powerdnsadmin_password';
EOL
psql -U powerdnsadmin powerdnsadmindb <<EOL
\q
EOL