我几年前写过一篇部署PowerDNS权威服务器的文章,最近需要搭建一个,然后翻出来看了下发现写的不够完善,而且纯手动部署太麻烦了,遂决定重新记录下使用Docker部署的步骤,另外主要补充一下:主从同步、DNSSEC的配置。
首先还是需要到域名注册商添加胶水记录(Glue Record),我使用的是spaceship,这家的用户面板不叫胶水记录,叫个人名称服务器,说实话有时候挺烦这种自己瞎起名字的行为。。害我在他们的这个面板里面找半天:
由于spaceship的限制,这里至少要添加2条胶水记录,如果你不打算部署2台服务器(主从同步),可以把两条记录的值都指向同一台服务器的IP,之后更改域名的NS服务器为刚设置的“个人名称服务器”:
本文示例:91.99.72.72为主(primary)服务器,49.13.168.202为从(secondary)服务器,在两台服务器内都安装Docker:
apt -y update apt -y install curl curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.sh
在主(primary)服务器创建目录新建compose文件:
mkdir /opt/pdns-mysql && cd /opt/pdns-mysql && nano docker-compose.yml
写入如下内容,需要修改的地方写了注释:
services:
mariadb:
image: mariadb:lts
container_name: pdns-mariadb
restart: unless-stopped
networks:
- pdns
environment:
- MARIADB_ROOT_PASSWORD=setyourpdnsmasterdbpassword # 设置数据库ROOT用户的密码
volumes:
- ./mariadb-data:/var/lib/mysql:Z
healthcheck:
test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
timeout: 10s
retries: 5
phpmyadmin:
image: phpmyadmin
restart: unless-stopped
networks:
- pdns
ports:
- 8988:80
environment:
- PMA_HOST=mariadb
pdns-master:
image: pschiffe/pdns-mysql:latest
container_name: pdns-master
hostname: ns1.ohsb.cc # 设置PDNS主服务器的主机名,务必与你的胶水记录保持一致
restart: unless-stopped
networks:
pdns:
ipv4_address: 172.89.64.74
depends_on:
mariadb:
condition: service_healthy
environment:
- PDNS_gmysql_host=mariadb
- PDNS_gmysql_port=3306
- PDNS_gmysql_user=root
- PDNS_gmysql_password=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
- PDNS_gmysql_dbname=powerdns
- PDNS_gmysql_dnssec=yes
- PDNS_primary=yes
- PDNS_api=yes
- PDNS_api_key=setyourpdnsapikey # 设置PDNS API KEY
- PDNS_webserver=yes
- PDNS_webserver_address=0.0.0.0
- PDNS_webserver_allow_from=0.0.0.0/0,::/0
- PDNS_webserver_password=setyourwebserverpassword # 设置PDNS WEB密码
- PDNS_version_string=anonymous
- PDNS_default_ttl=1500
- PDNS_allow_axfr_ips=49.13.168.202 # 设置为从服务器的公网IP
- PDNS_only_notify=49.13.168.202 # 设置为从服务器的公网IP
ports:
- '53:53'
- '53:53/udp'
- '8081:8081'
volumes:
- /etc/localtime:/etc/localtime:ro
pdns-admin:
image: pschiffe/pdns-admin
container_name: pdns-admin
restart: unless-stopped
depends_on:
mariadb:
condition: service_healthy
networks:
- pdns
ports:
- '8989:8080'
environment:
- PDNS_ADMIN_SQLA_DB_HOST=mariadb
- PDNS_ADMIN_SQLA_DB_PORT=3306
- PDNS_ADMIN_SQLA_DB_USER=root
- PDNS_ADMIN_SQLA_DB_PASSWORD=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
- PDNS_ADMIN_SQLA_DB_NAME=powerdnsadmin
- PDNS_API_URL="http://pdns-master:8081/"
- PDNS_API_KEY=setyourpdnsapikey # 设置连接到PDNS Master的API KEY
- PDNS_VERSION=5.0
volumes:
- /etc/localtime:/etc/localtime:ro
networks:
pdns:
ipam:
config:
- subnet: 172.89.0.0/16
gateway: 172.89.0.1
在从(secondary)服务器创建目录新建compose文件:
mkdir /opt/pdns-slave && cd /opt/pdns-slave && nano docker-compose.yml
写入如下内容,需要修改的地方写了注释:
services:
mariadb:
image: mariadb:lts
container_name: pdns-mariadb
restart: unless-stopped
networks:
- pdns
environment:
- MARIADB_ROOT_PASSWORD=setyourpdnsmasterdbpassword # 设置数据库ROOT用户的密码
volumes:
- ./mariadb-data:/var/lib/mysql:Z
healthcheck:
test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
timeout: 10s
retries: 5
phpmyadmin:
image: phpmyadmin
restart: unless-stopped
networks:
- pdns
ports:
- 8988:80
environment:
- PMA_HOST=mariadb
pdns-slave:
image: pschiffe/pdns-mysql:latest
container_name: pdns-slave
hostname: ns2.ohsb.cc # 设置PDNS从服务器的主机名,务必与你的胶水记录保持一致,从服务器必须设置,否则无法与主服务器同步!
restart: unless-stopped
networks:
pdns:
ipv4_address: 172.89.64.75
depends_on:
mariadb:
condition: service_healthy
environment:
- PDNS_gmysql_host=mariadb
- PDNS_gmysql_port=3306
- PDNS_gmysql_user=root
- PDNS_gmysql_password=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
- PDNS_gmysql_dbname=powerdnsslave
- PDNS_gmysql_dnssec=yes
- PDNS_secondary=yes
- PDNS_autosecondary=yes
- PDNS_webserver=yes
- PDNS_webserver_address=0.0.0.0
- PDNS_webserver_allow_from=0.0.0.0/0,::/0
- PDNS_webserver_password=setyourwebserverpassword # 设置PDNS WEB密码
- PDNS_version_string=anonymous
- PDNS_default_ttl=1500
- PDNS_disable_axfr=yes
- PDNS_allow_notify_from=91.99.72.72 # 设置为主服务器的公网IP
- SUPERMASTER_IPS=91.99.72.72 # 设置为主服务器的公网IP
ports:
- '53:53'
- '53:53/udp'
- '8081:8081'
volumes:
- /etc/localtime:/etc/localtime:ro
networks:
pdns:
ipam:
config:
- subnet: 172.89.0.0/16
gateway: 172.89.0.1
配置项太多,这里我也懒得详细说明了,打字太累= =为了方便理解,我没有给敏感内容(域名、服务器IP等信息)脱敏,这套配置是我目前从测试服务器1:1复制下来的。只要你按照注释来配置,肯定能跑起来的,并且功能都是正常的。
启动主、从服务器的所有服务:
docker compose up -d
配置DNSSEC,首先打开PowerDNS-Admin(91.99.72.72:8989)注册一个账号,第一个注册的账号自动成为管理员。
在PowerDNS-Admin添加Zone,Zone Name:你的域名,Zone Type选择:Primary,一定要选择Primary,Primary,Primary!
按如图所示添加2条NS记录以及2条A记录:
启用DNSSEC:
会回显类似如图的信息:
DNSKEY不用管,你可以简单理解为这是公钥。我们需要注意的是DS下面的内容,这实际上代表两条DS记录:
41411 13 2 48e9394892ee2da8... 41411 13 4 9a9382822735e648...
41411是“密钥标签”,13是“算法”,2和4是“摘要类型”,后面一长串是“摘要”。按照这个格式在spaceship内添加两条DS记录:
实际上只添加一条DS记录也是可以的,这个取决于你自己。检查DNSSEC是否生效,可以安装如下软件包:
apt install bind9-dnsutils
测试:
delv ns1.ohsb.cc delv ns2.ohsb.cc
如果输出的内容有fully validated,则说明DNSSEC工作正常:
检查主从同步是否正常,可以使用compose内部署的phpmyadmin登录到服务器数据库,查看两个数据库内的数据是否一致:
总结下部署过程中遇到的问题。从服务器无法同步,报错:
Unable to find backend willing to host ohsb.cc for potential autoprimary 91.99.72.72. Remote nameservers
这是从服务器的Docker容器没有设置正确的hostname导致的,见此issue。
在spaceship设置了DNSSEC的DS记录后,spaceship面板的DNS传播状态异常。这是由于PowerDNS内的NS记录配置错误导致的。我在spaceship配置了两个NS服务器,那么PowerDNS-Admin内也应该有两条NS记录,必须要保持一致。
参考资料:
https://hub.docker.com/r/pschiffe/pdns-mysql
https://github.com/pschiffe/docker-pdns/blob/master/docker-compose-mysql.yml
https://doc.powerdns.com/authoritative/settings.html
https://doc.powerdns.com/authoritative/backends/generic-mysql.html#gmysql-dnssec
荒岛

























直接 PowerDNS 暴露在公网会有性能和安全问题,尤其是 DNS 流量大了以后更明显,建议监听个比如本地 5353 端口,然后前面用 dnsdist 做一层 DNS 负载均衡,效果会更好