以下是我初次建立 powerdns 服務的紀錄
環境
- OS: Debian 12
- DB: postgresql-14
- pdns-server version: 4.7
事前準備
- 安裝 postgresql
- 建立資料庫
sudo su - postgres
psql
CREATE DATABASE pdnsdb;
CREATE USER pdns WITH PASSWORD 'pdnsPassword';
GRANT CONNECT ON DATABASE pdnsdb TO pdns;
ALTER DATABASE pdnsdb OWNER TO pdns;
建置 table schema,可以在說明文件中找到: /usr/share/doc/pdns-backend-pgsql/schema.pgsql.sql
並執行下列指令
psql -h <host> -U <user> <database> -f /usr/share/pdns-backend-pgsql/schema/schema.pgsql.sql
以下是內容
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);
安裝事前作業
- 停用 systemd-resolved
sudo systemctl disable --now systemd-resolved
設定穩定版本來源
設定參考官方說明: PowerDNS repositories
- /etc/apt/sources.list.d/pdns-stable.list
# PowerDNS Authoritative Server - stable branch
deb [signed-by=/etc/apt/keyrings/auth-49-pub.asc arch=amd64] http://repo.powerdns.com/debian bookworm-auth-49 main
# PowerDNS Recursor - stable branch
deb [signed-by=/etc/apt/keyrings/rec-51-pub.asc arch=amd64] http://repo.powerdns.com/debian bookworm-rec-51 main
- /etc/apt/preferences.d/auth-49
Package: auth*
Pin: origin repo.powerdns.com
Pin-Priority: 600
- /etc/apt/preferences.d/rec-51
Package: rec*
Pin: origin repo.powerdns.com
Pin-Priority: 600
安裝 PowerDNS
sudo install -d /etc/apt/keyrings; curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo tee /etc/apt/keyrings/auth-49-pub.asc
sudo install -d /etc/apt/keyrings; curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo tee /etc/apt/keyrings/rec-51-pub.asc
apt install -y pdns-server pdns-backend-pgsql pdns-recursor
cp /usr/share/doc/pdns-backend-pgsql/examples/gpgsql.conf /etc/powerdns/pdns.d/gpgsql.conf
chmod 0640 /etc/powerdns/pdns.d/gpgsql.conf
chgrp pdns /etc/powerdns/pdns.d/gpgsql.conf
- 設定文件: /etc/powerdns/recursor.conf
#################################
# api-key Static pre-shared authentication key for access to the REST API
#
# api-key=
api-key=<api-key>
#################################
# webserver Start a webserver (for REST API)
#
# webserver=no
webserver=yes
#################################
# webserver-port Port of webserver to listen on
#
# webserver-port=8082
webserver-port=8082
#################################
# webserver-address IP Address of webserver to listen on
#
# webserver-address=127.0.0.1
webserver-address=0.0.0.0
#################################
# webserver-allow-from Webserver access is only allowed from these subnets
#
# webserver-allow-from=127.0.0.1,::1
webserver-allow-from=0.0.0.0/0
#################################
# allow-from If set, only allow these comma separated netmasks to recurse
#
# allow-from=127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10
#設定允許遞迥查詢內部網段
allow-from=127.0.0.1, 192.168.0.0/24
#################################
# forward-zones Zones for which we forward queries, comma separated domain=ip pairs
#
# forward-zones=
#轉送查詢的網域與伺服器,格式是 網域=伺服器ip,多個網域以逗點分隔
forward-zones=.=127.0.0.1:54
#################################
# forward-zones-recurse Zones for which we forward queries with recursion bit, comma separated domain=ip pairs
#
# forward-zones-recurse=
#內部查詢不到會查到外部查詢
forward-zones-recurse=.=8.8.8.8, .=168.95.1.1
#################################
# local-address IP addresses to listen on, separated by spaces or commas. Also accepts ports.
#
# local-address=127.0.0.1
local-address=0.0.0.0
#################################
# local-port port to listen on
#
# local-port=53
local-port=53
#################################
# logging-facility Facility to log messages as. 0 corresponds to local0
#
# logging-facility=
logging-facility=0
#################################
# max-negative-ttl maximum number of seconds to keep a negative cached entry in memory
#
# max-negative-ttl=3600
max-negative-ttl=3600
#################################
# setgid If set, change group id to this gid for more security
#
setgid=pdns
#################################
# setuid If set, change user id to this uid for more security
#
setuid=pdns
#################################
# config-dir Location of configuration directory (recursor.conf)
#
config-dir=/etc/powerdns
#################################
# hint-file If set, load root hints from this file
#
# hint-file=
hint-file=/usr/share/dns/root.hints
#################################
# include-dir Include *.conf files from this directory
#
# include-dir=
include-dir=/etc/powerdns/recursor.d
#################################
# lua-config-file More powerful configuration options
#
lua-config-file=/etc/powerdns/recursor.lua
#################################
# quiet Suppress logging of questions and answers
#
quiet=yes
- 設定文件: /etc/powerdns/pdns.conf
# Autogenerated configuration file template
#################################
# ignore-unknown-settings Configuration settings to ignore if they are unknown
#
# ignore-unknown-settings=
#################################
# 8bit-dns Allow 8bit dns queries
#
# 8bit-dns=no
#################################
# allow-axfr-ips Allow zonetransfers only to these subnets
#
# allow-axfr-ips=127.0.0.0/8,::1
#################################
# allow-dnsupdate-from A global setting to allow DNS updates from these IP ranges.
#
# allow-dnsupdate-from=127.0.0.0/8,::1
#################################
# allow-notify-from Allow AXFR NOTIFY from these IP ranges. If empty, drop all incoming notifies.
#
# allow-notify-from=0.0.0.0/0,::/0
#################################
# allow-unsigned-autoprimary Allow autoprimaries to create zones without TSIG signed NOTIFY
#
# allow-unsigned-autoprimary=yes
#################################
# allow-unsigned-notify Allow unsigned notifications for TSIG secured zones
#
# allow-unsigned-notify=yes
#################################
# allow-unsigned-supermaster Allow supermasters to create zones without TSIG signed NOTIFY
#
# allow-unsigned-supermaster=yes
#################################
# also-notify When notifying a zone, also notify these nameservers
#
# also-notify=
#################################
# any-to-tcp Answer ANY queries with tc=1, shunting to TCP
#
# any-to-tcp=yes
#################################
# api Enable/disable the REST API (including HTTP listener)
#
api=yes
#################################
# api-key Static pre-shared authentication key for access to the REST API
#
api-key=<api-key>
#################################
# autosecondary Act as an autosecondary (formerly superslave)
#
# autosecondary=no
#################################
# axfr-fetch-timeout Maximum time in seconds for inbound AXFR to start or be idle after starting
#
# axfr-fetch-timeout=10
#################################
# axfr-lower-serial Also AXFR a zone from a master with a lower serial
#
# axfr-lower-serial=no
#################################
# cache-ttl Seconds to store packets in the PacketCache
#
# cache-ttl=20
#################################
# carbon-instance If set overwrites the instance name default
#
# carbon-instance=auth
#################################
# carbon-interval Number of seconds between carbon (graphite) updates
#
# carbon-interval=30
#################################
# carbon-namespace If set overwrites the first part of the carbon string
#
# carbon-namespace=pdns
#################################
# carbon-ourname If set, overrides our reported hostname for carbon stats
#
# carbon-ourname=
#################################
# carbon-server If set, send metrics in carbon (graphite) format to this server IP address
#
# carbon-server=
#################################
# chroot If set, chroot to this directory for more security
#
# chroot=
#################################
# config-dir Location of configuration directory (pdns.conf)
#
# config-dir=/etc/powerdns
#################################
# config-name Name of this virtual configuration - will rename the binary image
#
# config-name=
#################################
# consistent-backends Assume individual zones are not divided over backends. Send only ANY lookup operations to the backend to reduce the number of lookups
#
# consistent-backends=yes
#################################
# control-console Debugging switch - don't use
#
# control-console=no
#################################
# daemon Operate as a daemon
daemon=yes
#################################
# default-api-rectify Default API-RECTIFY value for zones
#
# default-api-rectify=yes
#################################
# default-ksk-algorithm Default KSK algorithm
#
# default-ksk-algorithm=ecdsa256
#################################
# default-ksk-size Default KSK size (0 means default)
#
# default-ksk-size=0
#################################
# default-publish-cdnskey Default value for PUBLISH-CDNSKEY
#
# default-publish-cdnskey=
#################################
# default-publish-cds Default value for PUBLISH-CDS
#
# default-publish-cds=
#################################
# default-soa-content Default SOA content
#
# default-soa-content=a.misconfigured.dns.server.invalid hostmaster.@ 0 10800 3600 604800 3600
#################################
# default-soa-edit Default SOA-EDIT value
#
# default-soa-edit=
#################################
# default-soa-edit-signed Default SOA-EDIT value for signed zones
#
# default-soa-edit-signed=
#################################
# default-ttl Seconds a result is valid if not set otherwise
#
# default-ttl=3600
#################################
# default-zsk-algorithm Default ZSK algorithm
#
# default-zsk-algorithm=
#################################
# default-zsk-size Default ZSK size (0 means default)
#
# default-zsk-size=0
#################################
# direct-dnskey Fetch DNSKEY, CDS and CDNSKEY RRs from backend during DNSKEY or CDS/CDNSKEY synthesis
#
# direct-dnskey=no
#################################
# disable-axfr Disable zonetransfers but do allow TCP queries
#
# disable-axfr=no
#################################
# disable-axfr-rectify Disable the rectify step during an outgoing AXFR. Only required for regression testing.
#
# disable-axfr-rectify=no
#################################
# disable-syslog Disable logging to syslog, useful when running inside a supervisor that logs stdout
#
# disable-syslog=no
#################################
# distributor-threads Default number of Distributor (backend) threads to start
#
# distributor-threads=3
#################################
# dname-processing If we should support DNAME records
#
# dname-processing=no
#################################
# dnssec-key-cache-ttl Seconds to cache DNSSEC keys from the database
#
# dnssec-key-cache-ttl=30
#################################
# dnsupdate Enable/Disable DNS update (RFC2136) support. Default is no.
#
# dnsupdate=no
#################################
# domain-metadata-cache-ttl Seconds to cache zone metadata from the database
#
# domain-metadata-cache-ttl=
#################################
# edns-cookie-secret When set, set a server cookie when responding to a query with a Client cookie (in hex)
#
# edns-cookie-secret=
#################################
# edns-subnet-processing If we should act on EDNS Subnet options
#
# edns-subnet-processing=no
#################################
# enable-lua-records Process LUA records for all zones (metadata overrides this)
#
# enable-lua-records=no
#################################
# entropy-source If set, read entropy from this file
#
# entropy-source=/dev/urandom
#################################
# expand-alias Expand ALIAS records
#
# expand-alias=no
#################################
# forward-dnsupdate A global setting to allow DNS update packages that are for a Slave zone, to be forwarded to the master.
#
# forward-dnsupdate=yes
#################################
# forward-notify IP addresses to forward received notifications to regardless of master or slave settings
#
# forward-notify=
#################################
# guardian Run within a guardian process
#
# guardian=no
#################################
# include-dir Include *.conf files from this directory
#
# include-dir=
include-dir=/etc/powerdns/pdns.d
#################################
# launch Which backends to launch and order to query them in
#
# launch=
launch=
#################################
# load-modules Load this module - supply absolute or relative path
#
# load-modules=
#################################
# local-address Local IP addresses to which we bind
#
local-address=0.0.0.0, ::
#################################
# local-address-nonexist-fail Fail to start if one or more of the local-address's do not exist on this server
#
# local-address-nonexist-fail=yes
#################################
# local-port The port on which we listen
#
local-port=54
#################################
# log-dns-details If PDNS should log DNS non-erroneous details
#
log-dns-details=yes
#################################
# log-dns-queries If PDNS should log all incoming DNS queries
#
log-dns-queries=yes
#################################
# log-timestamp Print timestamps in log lines
log-timestamp=yes
#################################
# logging-facility Log under a specific facility
#
logging-facility=0
#################################
# loglevel Amount of logging. Higher is more. Do not set below 3
#
loglevel=4
#################################
# lua-axfr-script Script to be used to edit incoming AXFRs
#
# lua-axfr-script=
#################################
# lua-dnsupdate-policy-script Lua script with DNS update policy handler
#
# lua-dnsupdate-policy-script=
#################################
# lua-health-checks-expire-delay Stops doing health checks after the record hasn't been used for that delay (in seconds)
#
# lua-health-checks-expire-delay=3600
#################################
# lua-health-checks-interval LUA records health checks monitoring interval in seconds
#
# lua-health-checks-interval=5
#################################
# lua-prequery-script Lua script with prequery handler (DO NOT USE)
#
# lua-prequery-script=
#################################
# lua-records-exec-limit LUA records scripts execution limit (instructions count). Values <= 0 mean no limit
#
# lua-records-exec-limit=1000
#################################
# master Act as a primary
#
# master=no
#################################
# max-cache-entries Maximum number of entries in the query cache
#
# max-cache-entries=1000000
#################################
# max-ent-entries Maximum number of empty non-terminals in a zone
#
# max-ent-entries=100000
#################################
# max-generate-steps Maximum number of $GENERATE steps when loading a zone from a file
#
# max-generate-steps=0
#################################
# max-include-depth Maximum number of nested $INCLUDE directives while processing a zone file
#
# max-include-depth=20
#################################
# max-nsec3-iterations Limit the number of NSEC3 hash iterations
#
# max-nsec3-iterations=100
#################################
# max-packet-cache-entries Maximum number of entries in the packet cache
#
# max-packet-cache-entries=1000000
#################################
# max-queue-length Maximum queuelength before considering situation lost
#
# max-queue-length=5000
#################################
# max-signature-cache-entries Maximum number of signatures cache entries
#
# max-signature-cache-entries=
#################################
# max-tcp-connection-duration Maximum time in seconds that a TCP DNS connection is allowed to stay open.
#
# max-tcp-connection-duration=0
#################################
# max-tcp-connections Maximum number of TCP connections
#
# max-tcp-connections=20
#################################
# max-tcp-connections-per-client Maximum number of simultaneous TCP connections per client
#
# max-tcp-connections-per-client=0
#################################
# max-tcp-transactions-per-conn Maximum number of subsequent queries per TCP connection
#
# max-tcp-transactions-per-conn=0
#################################
# module-dir Default directory for modules
#
#################################
# negquery-cache-ttl Seconds to store negative query results in the QueryCache
#
# negquery-cache-ttl=60
#################################
# no-shuffle Set this to prevent random shuffling of answers - for regression testing
#
# no-shuffle=off
#################################
# non-local-bind Enable binding to non-local addresses by using FREEBIND / BINDANY socket options
#
# non-local-bind=no
#################################
# only-notify Only send AXFR NOTIFY to these IP addresses or netmasks
#
# only-notify=0.0.0.0/0,::/0
#################################
# outgoing-axfr-expand-alias Expand ALIAS records during outgoing AXFR
#
# outgoing-axfr-expand-alias=no
#################################
# overload-queue-length Maximum queuelength moving to packetcache only
#
# overload-queue-length=0
#################################
# prevent-self-notification Don't send notifications to what we think is ourself
#
# prevent-self-notification=yes
#################################
# primary Act as a primary
#
# primary=no
#################################
# proxy-protocol-from A Proxy Protocol header is only allowed from these subnets, and is mandatory then too.
#
# proxy-protocol-from=
#################################
# proxy-protocol-maximum-size The maximum size of a proxy protocol payload, including the TLV values
#
# proxy-protocol-maximum-size=512
#################################
# query-cache-ttl Seconds to store query results in the QueryCache
#
# query-cache-ttl=20
#################################
# query-local-address Source IP addresses for sending queries
#
# query-local-address=0.0.0.0 ::
#################################
# query-logging Hint backends that queries should be logged
#
query-logging=yes
#################################
# queue-limit Maximum number of milliseconds to queue a query
#
# queue-limit=1500
#################################
# receiver-threads Default number of receiver threads to start
#
# receiver-threads=1
#################################
# resolver Use this resolver for ALIAS and the internal stub resolver
#
# resolver=no
#################################
# retrieval-threads Number of AXFR-retrieval threads for slave operation
#
# retrieval-threads=2
#################################
# reuseport Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket
#
# reuseport=no
#################################
# rng Specify the random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.
#
# rng=auto
#################################
# secondary Act as a secondary
#
# secondary=no
#################################
# secondary-check-signature-freshness Check signatures in SOA freshness check. Sets DO flag on SOA queries. Outside some very problematic scenarios, say yes here.
#
# secondary-check-signature-freshness=yes
#################################
# secondary-do-renotify If this secondary should send out notifications after receiving zone transfers from a primary
#
# secondary-do-renotify=no
#################################
# security-poll-suffix Zone name from which to query security update notifications
#
# security-poll-suffix=secpoll.powerdns.com.
security-poll-suffix=
#################################
# send-signed-notify Send TSIG secured NOTIFY if TSIG key is configured for a zone
#
# send-signed-notify=yes
#################################
# server-id Returned when queried for 'id.server' TXT or NSID, defaults to hostname - disabled or custom
#
# server-id=
#################################
# setgid If set, change group id to this gid for more security
#
setgid=pdns
#################################
# setuid If set, change user id to this uid for more security
#
setuid=pdns
#################################
# signing-threads Default number of signer threads to start
#
# signing-threads=3
#################################
# slave Act as a secondary
#
# slave=no
#################################
# slave-cycle-interval Schedule slave freshness checks once every .. seconds
#
# slave-cycle-interval=60
#################################
# slave-renotify If we should send out notifications for secondaried updates
#
# slave-renotify=no
#################################
# socket-dir Where the controlsocket will live, /var/run/pdns when unset and not chrooted. Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).
#
# socket-dir=
#################################
# superslave Act as a autosecondary
#
# superslave=no
#################################
# svc-autohints Transparently fill ipv6hint=auto ipv4hint=auto SVC params with AAAA/A records for the target name of the record (if within the same zone)
#
# svc-autohints=no
#################################
# tcp-control-address If set, PowerDNS can be controlled over TCP on this address
#
# tcp-control-address=
#################################
# tcp-control-port If set, PowerDNS can be controlled over TCP on this address
#
# tcp-control-port=53000
#################################
# tcp-control-range If set, remote control of PowerDNS is possible over these networks only
#
# tcp-control-range=127.0.0.0/8, 10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fe80::/10
#################################
# tcp-control-secret If set, PowerDNS can be controlled over TCP after passing this secret
#
# tcp-control-secret=
#################################
# tcp-fast-open Enable TCP Fast Open support on the listening sockets, using the supplied numerical value as the queue size
#
# tcp-fast-open=0
#################################
# tcp-idle-timeout Maximum time in seconds that a TCP DNS connection is allowed to stay open while being idle
#
# tcp-idle-timeout=5
#################################
# traceback-handler Enable the traceback handler (Linux only)
#
# traceback-handler=yes
#################################
# trusted-notification-proxy IP address of incoming notification proxy
#
# trusted-notification-proxy=
#################################
# udp-truncation-threshold Maximum UDP response size before we truncate
#
# udp-truncation-threshold=1232
#################################
# upgrade-unknown-types Transparently upgrade known TYPExxx records. Recommended to keep off, except for PowerDNS upgrades until data sources are cleaned up
#
# upgrade-unknown-types=no
#################################
# version-string PowerDNS version in packets - full, anonymous, powerdns or custom
#
# version-string=full
#################################
# webserver Start a webserver for monitoring (api=yes also enables the HTTP listener)
#
webserver=yes
#################################
# webserver-address IP Address of webserver/API to listen on
#
webserver-address=0.0.0.0
#################################
# webserver-allow-from Webserver/API access is only allowed from these subnets
#
webserver-allow-from=0.0.0.0/0
#################################
# webserver-hash-plaintext-credentials Whether to hash passwords and api keys supplied in plaintext, to prevent keeping the plaintext version in memory at runtime
#
# webserver-hash-plaintext-credentials=no
#################################
# webserver-loglevel Amount of logging in the webserver (none, normal, detailed)
#
# webserver-loglevel=normal
#################################
# webserver-max-bodysize Webserver/API maximum request/response body size in megabytes
#
# webserver-max-bodysize=2
#################################
# webserver-password Password required for accessing the webserver
#
# webserver-password=
#################################
# webserver-port Port of webserver/API to listen on
#
webserver-port=8081
#################################
# webserver-print-arguments If the webserver should print arguments
#
# webserver-print-arguments=no
#################################
# write-pid Write a PID file
#
# write-pid=yes
#################################
# xfr-cycle-interval Schedule primary/secondary SOA freshness checks once every .. seconds
#
# xfr-cycle-interval=60
#################################
# xfr-max-received-mbytes Maximum number of megabytes received from an incoming XFR
#
# xfr-max-received-mbytes=100
#################################
# zone-cache-refresh-interval Seconds to cache list of known zones
#
# zone-cache-refresh-interval=300
#################################
# zone-metadata-cache-ttl Seconds to cache zone metadata from the database
#
# zone-metadata-cache-ttl=60
- 設定文件: /etc/powerdns/pdns.d/gpgsql.conf
# See https://doc.powerdns.com/authoritative/backends/generic-postgresql.html
launch+=gpgsql
#################################
# gpgsql-dbname Backend database name to connect to
#
# gpgsql-dbname=
gpgsql-dbname=pdnsdb
#################################
# gpgsql-dnssec Enable DNSSEC processing
#
# gpgsql-dnssec=no
gpgsql-dnssec=yes
#################################
# gpgsql-extra-connection-parameters Extra parameters to add to connection string
#
# gpgsql-extra-connection-parameters=
#################################
# gpgsql-host Database backend host to connect to
#
# gpgsql-host=
gpgsql-host=127.0.0.1
#################################
# gpgsql-password Database backend password to connect with
#
# gpgsql-password=
gpgsql-password=pdnsPassword
#################################
# gpgsql-port Database backend port to connect to
#
# gpgsql-port=
gpgsql-port=5432
#################################
# gpgsql-prepared-statements Use prepared statements instead of parameterized queries
#
# gpgsql-prepared-statements=yes
#################################
# gpgsql-user Database backend user to connect as
#
# gpgsql-user=
gpgsql-user=pdns
systemctl enable pdns ; systemctl restart pdns
設定 log
編輯 system daemon
systemctl edit --full pdns
移除 --disable-syslog
--log-timestamp=no
- /etc/rsyslog.d/pdns.conf
local0.info -/var/log/pdns/pdns.info
local0.warn -/var/log/pdns/pdns.warn
local0.err /var/log/pdns/pdns.err
mkdir -p /var/log/pdns
systemctl restart pdns rsyslog
安裝 PowerDNS-Admin
設定資料庫
CREATE DATABASE pdnsadmindb;
CREATE USER pdnsadmin WITH PASSWORD 'pdnsPassword';
GRANT CONNECT ON DATABASE pdnsadmindb TO pdnsadmin;
ALTER DATABASE pdnsadmindb OWNER TO pdnsadmin;
執行安裝
sudo -i
# 預先安裝必要套件
apt install -y python3.11-venv python3-psycopg2 libmariadb-dev libsasl2-dev libldap2-dev libssl-dev libxml2-dev libxslt1-dev libxmlsec1-dev libffi-dev pkg-config apt-transport-https virtualenv build-essential python3-venv libpq-dev python3-dev
# 安裝NodeJS
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
export NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
apt install -y nodejs
# 安裝 yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update
sudo apt install -y yarn
# 安裝 PowerDNS-Admin
wget -O /tmp/PowerDNS-Admin.tar.gz https://github.com/PowerDNS-Admin/PowerDNS-Admin/archive/refs/tags/v0.4.2.tar.gz
mkdir -p /opt/web/
tar -zxf /tmp/PowerDNS-Admin.tar.gz -C /opt/web/powerdns-admin
ln -s /opt/web/PowerDNS-Admin-0.4.2 /opt/web/powerdns-admin
cd /opt/web/powerdns-admin
python3 -mvenv ./venv
source ./venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
cp /opt/web/powerdns-admin/configs/development.py /opt/web/powerdns-admin/configs/production.py
- 設定文件: /opt/web/powerdns-admin/configs/production.py
import os
import urllib.parse
basedir = os.path.abspath(os.path.dirname(__file__))
### BASIC APP CONFIG
SALT = '$2b$12$yLUMTIfl21FKJQpTkRQXCu'
SECRET_KEY = 'e951e5a1f4b94151b360f47edf596dd2'
BIND_ADDRESS = '0.0.0.0'
PORT = 9191
SERVER_EXTERNAL_SSL = os.getenv('SERVER_EXTERNAL_SSL', None)
### DATABASE CONFIG
SQLA_DB_USER = 'pdnsadmin'
SQLA_DB_PASSWORD = 'pdnsPassword'
SQLA_DB_HOST = '127.0.0.1'
SQLA_DB_NAME = 'pdnsadmindb'
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLA_DB_PORT = 5432
#CAPTCHA Config
CAPTCHA_ENABLE = True
CAPTCHA_LENGTH = 6
CAPTCHA_WIDTH = 160
CAPTCHA_HEIGHT = 60
CAPTCHA_SESSION_KEY = 'captcha_image'
#Server side sessions tracking
#Set to TRUE for CAPTCHA, or enable another stateful session tracking system
SESSION_TYPE = 'sqlalchemy'
### DATABASE - MySQL
## Don't forget to uncomment the import in the top
#SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format(
# urllib.parse.quote_plus(SQLA_DB_USER),
# urllib.parse.quote_plus(SQLA_DB_PASSWORD),
# SQLA_DB_HOST,
# SQLA_DB_NAME
#)
### DATABASE - PostgreSQL
## Don't forget to uncomment the import in the top
SQLALCHEMY_DATABASE_URI = 'postgresql://{}:{}@{}:{}/{}'.format(
urllib.parse.quote_plus(SQLA_DB_USER),
urllib.parse.quote_plus(SQLA_DB_PASSWORD),
SQLA_DB_HOST,
SQLA_DB_PORT,
SQLA_DB_NAME
)
### DATABASE - SQLite
# SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db')
### SMTP config
# MAIL_SERVER = 'localhost'
# MAIL_PORT = 25
# MAIL_DEBUG = False
# MAIL_USE_TLS = False
# MAIL_USE_SSL = False
# MAIL_USERNAME = None
# MAIL_PASSWORD = None
# MAIL_DEFAULT_SENDER = ('PowerDNS-Admin', '[email protected]')
# SAML Authnetication
SAML_ENABLED = False
# SAML_DEBUG = True
# SAML_PATH = os.path.join(os.path.dirname(__file__), 'saml')
# ##Example for ADFS Metadata-URL
# SAML_METADATA_URL = 'https://<hostname>/FederationMetadata/2007-06/FederationMetadata.xml'
# #Cache Lifetime in Seconds
# SAML_METADATA_CACHE_LIFETIME = 1
# # SAML SSO binding format to use
# ## Default: library default (urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect)
# #SAML_IDP_SSO_BINDING = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
# ## EntityID of the IdP to use. Only needed if more than one IdP is
# ## in the SAML_METADATA_URL
# ### Default: First (only) IdP in the SAML_METADATA_URL
# ### Example: https://idp.example.edu/idp
# #SAML_IDP_ENTITY_ID = 'https://idp.example.edu/idp'
# ## NameID format to request
# ### Default: The SAML NameID Format in the metadata if present,
# ### otherwise urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
# ### Example: urn:oid:0.9.2342.19200300.100.1.1
# #SAML_NAMEID_FORMAT = 'urn:oid:0.9.2342.19200300.100.1.1'
# Following parameter defines RequestedAttributes section in SAML metadata
# since certain iDPs require explicit attribute request. If not provided section
# will not be available in metadata.
#
# Possible attributes:
# name (mandatory), nameFormat, isRequired, friendlyName
#
# NOTE: This parameter requires to be entered in valid JSON format as displayed below
# and multiple attributes can given
#
# Following example:
#
# SAML_SP_REQUESTED_ATTRIBUTES = '[ \
# {"name": "urn:oid:0.9.2342.19200300.100.1.3", "nameFormat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "isRequired": true, "friendlyName": "email"}, \
# {"name": "mail", "isRequired": false, "friendlyName": "test-field"} \
# ]'
#
# produces following metadata section:
# <md:AttributeConsumingService index="1">
# <md:RequestedAttribute Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" FriendlyName="email" isRequired="true"/>
# <md:RequestedAttribute Name="mail" FriendlyName="test-field"/>
# </md:AttributeConsumingService>
# ## Attribute to use for Email address
# ### Default: email
# ### Example: urn:oid:0.9.2342.19200300.100.1.3
# #SAML_ATTRIBUTE_EMAIL = 'urn:oid:0.9.2342.19200300.100.1.3'
# ## Attribute to use for Given name
# ### Default: givenname
# ### Example: urn:oid:2.5.4.42
# #SAML_ATTRIBUTE_GIVENNAME = 'urn:oid:2.5.4.42'
# ## Attribute to use for Surname
# ### Default: surname
# ### Example: urn:oid:2.5.4.4
# #SAML_ATTRIBUTE_SURNAME = 'urn:oid:2.5.4.4'
# ## Attribute to use for username
# ### Default: Use NameID instead
# ### Example: urn:oid:0.9.2342.19200300.100.1.1
# #SAML_ATTRIBUTE_USERNAME = 'urn:oid:0.9.2342.19200300.100.1.1'
# ## Attribute to get admin status from
# ### Default: Don't control admin with SAML attribute
# ### Example: https://example.edu/pdns-admin
# ### If set, look for the value 'true' to set a user as an administrator
# ### If not included in assertion, or set to something other than 'true',
# ### the user is set as a non-administrator user.
# #SAML_ATTRIBUTE_ADMIN = 'https://example.edu/pdns-admin'
## Attribute to get admin status for groups with the IdP
# ### Default: Don't set administrator group with SAML attributes
#SAML_GROUP_ADMIN_NAME = 'GroupName'
## Attribute to get operator status for groups with the IdP
# ### Default: Don't set operator group with SAML attributes
#SAML_GROUP_OPERATOR_NAME = 'GroupName'
# ## Attribute to get account names from
# ### Default: Don't control accounts with SAML attribute
# ### If set, the user will be added and removed from accounts to match
# ### what's in the login assertion. Accounts that don't exist will
# ### be created and the user added to them.
# SAML_ATTRIBUTE_ACCOUNT = 'https://example.edu/pdns-account'
# ## Attribute name that aggregates group names
# ### Default: Don't collect IdP groups from SAML group attributes
# ### In Okta, you can assign administrators by group using "Group Attribute Statements."
# ### In this case, the SAML_ATTRIBUTE_GROUP will be the attribute name for a collection of
# ### groups passed in the SAML assertion. From there, you can specify a SAML_GROUP_ADMIN_NAME.
# ### If the user is a member of this group, and that group name is included in the collection,
# ### the user will be set as an administrator.
# #SAML_ATTRIBUTE_GROUP = 'https://example.edu/pdns-groups'
# #SAML_GROUP_ADMIN_NAME = 'PowerDNSAdmin-Administrators'
# SAML_SP_ENTITY_ID = 'http://<SAML SP Entity ID>'
# SAML_SP_CONTACT_NAME = '<contact name>'
# SAML_SP_CONTACT_MAIL = '<contact mail>'
# Configures the path to certificate file and it's respective private key file
# This pair is used for signing metadata, encrypting tokens and all other signing/encryption
# tasks during communication between iDP and SP
# NOTE: if this two parameters aren't explicitly provided, self-signed certificate-key pair
# will be generated in "PowerDNS-Admin" root directory
# ###########################################################################################
# CAUTION: For production use, usage of self-signed certificates it's highly discouraged.
# Use certificates from trusted CA instead
# ###########################################################################################
# SAML_CERT = '/etc/pki/powerdns-admin/cert.crt'
# SAML_KEY = '/etc/pki/powerdns-admin/key.pem'
# Configures if SAML tokens should be encrypted.
# SAML_SIGN_REQUEST = False
# #Use SAML standard logout mechanism retreived from idp metadata
# #If configured false don't care about SAML session on logout.
# #Logout from PowerDNS-Admin only and keep SAML session authenticated.
# SAML_LOGOUT = False
# #Configure to redirect to a different url then PowerDNS-Admin login after SAML logout
# #for example redirect to google.com after successful saml logout
# #SAML_LOGOUT_URL = 'https://google.com'
# #SAML_ASSERTION_ENCRYPTED = True
# Some IdPs, like Okta, do not return Attribute Statements by default
# Set the following to False if you are using Okta and not manually configuring Attribute Statements
# #SAML_WANT_ATTRIBUTE_STATEMENT = True
# Remote authentication settings
# Whether to enable remote user authentication or not
# Defaults to False
# REMOTE_USER_ENABLED=True
# If set, users will be redirected to this location on logout
# Ignore or set to None to avoid redirecting altogether
# Warning: if REMOTE_USER environment variable is still set after logging out and not cleared by
# some external module, not defining a custom logout URL might trigger a loop
# that will just log the user back in right after logging out
# REMOTE_USER_LOGOUT_URL=https://my.sso.com/cas/logout
# An optional list of remote authentication tied cookies to be removed upon logout
# REMOTE_USER_COOKIES=['MOD_AUTH_CAS', 'MOD_AUTH_CAS_S']
export FLASK_CONF=../configs/production.py
export FLASK_APP=powerdnsadmin/__init__.py
flask db upgrade
操作結果
(venv) root@pl-dns-01:/opt/web/powerdns-admin# export FLASK_CONF=../configs/production.py
(venv) root@pl-dns-01:/opt/web/powerdns-admin# export FLASK_APP=powerdnsadmin/__init__.py
(venv) root@pl-dns-01:/opt/web/powerdns-admin# flask db upgrade
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 787bdba9e147, Init DB
INFO [alembic.runtime.migration] Running upgrade 787bdba9e147 -> 59729e468045, Add view column to setting table
INFO [alembic.runtime.migration] Running upgrade 59729e468045 -> 1274ed462010, Change setting.value data type
INFO [alembic.runtime.migration] Running upgrade 1274ed462010 -> 4a666113c7bb, Adding Operator Role
INFO [alembic.runtime.migration] Running upgrade 4a666113c7bb -> 31a4ed468b18, Remove all setting in the DB
INFO [alembic.runtime.migration] Running upgrade 31a4ed468b18 -> 654298797277, Upgrade DB Schema
INFO [alembic.runtime.migration] Running upgrade 654298797277 -> 0fb6d23a4863, Remove user avatar
INFO [alembic.runtime.migration] Running upgrade 0fb6d23a4863 -> 856bb94b7040, Add comment column in domain template record table
INFO [alembic.runtime.migration] Running upgrade 856bb94b7040 -> b0fea72a3f20, Update domain serial columns type
INFO [alembic.runtime.migration] Running upgrade b0fea72a3f20 -> 3f76448bb6de, Add user.confirmed column
INFO [alembic.runtime.migration] Running upgrade 3f76448bb6de -> 0d3d93f1c2e0, Add domain_id to history table
INFO [alembic.runtime.migration] Running upgrade 0d3d93f1c2e0 -> 0967658d9c0d, add apikey account mapping table
INFO [alembic.runtime.migration] Running upgrade 0967658d9c0d -> fbc7cf864b24, update history detail quotes
INFO [alembic.runtime.migration] Running upgrade fbc7cf864b24 -> 6ea7dc05f496, Fix typo in history detail
INFO [alembic.runtime.migration] Running upgrade 6ea7dc05f496 -> f41520e41cee, update domain type length
INFO [alembic.runtime.migration] Running upgrade f41520e41cee -> b24bf17725d2, Add unique index to settings table keys
產生資料檔
yarn install --pure-lockfile
flask assets build
操作結果
(venv) root@pl-dns-01:/opt/web/powerdns-admin# yarn install --pure-lockfile
yarn install v1.22.22
[1/4] Resolving packages...
warning Resolution field "@fortawesome/[email protected]" is incompatible with requested version "@fortawesome/fontawesome-free@^5.15.4"
[2/4] Fetching packages...
info There appears to be trouble with your network connection. Retrying...
[3/4] Linking dependencies...
warning " > [email protected]" has unmet peer dependency "[email protected] - 3".
warning " > [email protected]" has unmet peer dependency "popper.js@^1.16.1".
warning "admin-lte > [email protected]" has incorrect peer dependency "bootstrap@^3.1.1".
warning " > [email protected]" has unmet peer dependency "jquery@^1.7 || ^2.0 || ^3.1".
warning "admin-lte > [email protected]" has unmet peer dependency "moment-timezone@^0.5.31".
warning "admin-lte > [email protected]" has unmet peer dependency "[email protected]".
warning "admin-lte > bootstrap-colorpicker > [email protected]" has unmet peer dependency "@popperjs/core@^2.11.6".
[4/4] Building fresh packages...
Done in 29.55s.
(venv) root@pl-dns-01:/opt/web/powerdns-admin# flask assets build
Building bundle: generated/login.js
[2024-12-22 22:51:03,754] [script.py:167] INFO - Building bundle: generated/login.js
Building bundle: generated/validation.js
[2024-12-22 22:51:03,763] [script.py:167] INFO - Building bundle: generated/validation.js
Building bundle: generated/login.css
[2024-12-22 22:51:03,765] [script.py:167] INFO - Building bundle: generated/login.css
Building bundle: generated/main.js
[2024-12-22 22:51:03,812] [script.py:167] INFO - Building bundle: generated/main.js
Building bundle: generated/main.css
[2024-12-22 22:51:03,857] [script.py:167] INFO - Building bundle: generated/main.css
(venv) root@pl-dns-01:/opt/web/powerdns-admin#
記下當前版本 PowerDNS 當前版本,等等設定會使用到
sudo pdns_control version
試營運
./run.py
URL: http://192.168.56.100:9191/login
- 預設使沒有登入帳號的,需要先註冊一個帳號
離開 Python 虛擬環境
deactivate
建立 system-daemon
- /etc/systemd/system/powerdns-admin.service
[Unit]
Description=PowerDNS-Admin
Requires=powerdns-admin.socket
After=network.target
[Service]
Environment="FLASK_CONF=../configs/production.py"
PIDFile=/run/powerdns-admin/pid
User=pdns
Group=pdns
WorkingDirectory=/opt/web/powerdns-admin
ExecStart=/opt/web/powerdns-admin/venv/bin/gunicorn --pid /run/powerdns-admin/pid --bind unix:/run/powerdns-admin/socket 'powerdnsadmin:create_app()'
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
- /etc/systemd/system/powerdns-admin.socket
[Unit]
Description=PowerDNS-Admin socket
[Socket]
ListenStream=/run/powerdns-admin/socket
[Install]
WantedBy=sockets.target
- /etc/tmpfiles.d/powerdns-admin.conf
d /run/powerdns-admin 0755 pdns pdns -
sudo systemctl daemon-reload && sudo systemctl start powerdns-admin.socket && sudo systemctl enable powerdns-admin.socket
sudo chown -R pdns:pdns /run/powerdns-admin
sudo chown -R pdns:pdns /opt/web/PowerDNS-Admin-0.4.2
sudo systemctl restart powerdns-admin
安裝 nginx
apt install nginx
- /etc/nginx/conf.d/http.conf
server {
listen *:80;
server_name 192.168.56.100;
index index.html index.htm index.php;
root /opt/web/powerdns-admin;
access_log /var/log/nginx/powerdns-admin.local.access.log combined;
error_log /var/log/nginx/powerdns-admin.local.error.log;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_redirect off;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;
proxy_buffer_size 8k;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_headers_hash_bucket_size 64;
location ~ ^/static/ {
include /etc/nginx/mime.types;
root /opt/web/powerdns-admin/powerdnsadmin;
location ~* \.(jpg|jpeg|png|gif)$ {
expires 365d;
}
location ~* ^.+.(css|js)$ {
expires 7d;
}
}
location / {
proxy_pass http://unix:/run/powerdns-admin/socket;
proxy_read_timeout 120;
proxy_connect_timeout 120;
proxy_redirect off;
}
}