Poweradmin v4.4.3 API 使用详解

Poweradmin REST API v2 全资源实操:X-API-Key 鉴权与 key 签发、Swagger 文档入口、zones/records/rrsets 增删改查、用户与权限模板管理,附 zone 解析可见性与 webserver ACL 两个真实排障实录。

PoweradminDNS服务器域名服务网络2026/10/26

Poweradmin v4.4.3 API 使用详解

目标:把 Poweradmin 自带 REST API(/api/v2/*)的鉴权、签发、文档入口与全部资源实操一次讲完,并沉淀本轮实测踩到的三个真实故障点(zone 解析可见性、PowerDNS webserver ACL、方法不匹配)。本文全部命令与回显在一台按《Poweradmin v4.4.3 Docker Compose 部署指南》部署好的服务器上执行,配置开关的含义见《Poweradmin v4.4.3 配置参数详解》。

一、API 形态与鉴权总览

1.1 版本依据与实验环境

项目取值
操作系统Ubuntu 22.04(root 用户)
Poweradminv4.4.3(官方 poweradmin/poweradmin:4.4.3),Web 端口 8131
PowerDNS Authoritative4.9.18(gmysql 后端,HTTP API 监听 8081)
数据库MariaDB 10.11
API 总开关PA_API_ENABLED=true、PA_API_DOCS_ENABLED=true(配置依据见配置篇第七节)
管理员账号admin / q1w2e3r4
实测 API keypwa_a5b508fa4a904f88c880daa4a25e1b488d7d2ed0ea73e92fca4e4a1ccca40da8
已有 zoneexample.com(部署篇创建,zone id=1)

1.2 两套 API 的关系

Poweradmin 部署形态里实际存在两套 REST API,排障时先分清:

Poweradmin API v2PowerDNS API v1
路径前缀/api/v2/*(随 Web 端口,本栈 8131)/api/v1/*(PowerDNS webserver 端口,本栈 8081)
鉴权X-API-Key(Poweradmin 签发的 pwa_ 串)X-API-Key(pdns.conf 的 api-key)
职责管理面板全集:zones/records/users/groups/权限模板/zone 模板DNS 数据本源:zones/rrsets/metadata/cryptokeys
文档入口/api/docs(内置 Swagger UI)PowerDNS 官方文档(无内置 UI)

SQL 直连模式(PA_DNS_BACKEND=sql,本栈形态)下 Poweradmin API 写库、PowerDNS 进程读库,两者经数据库解耦;API 后端模式(PA_DNS_BACKEND=api)下 Poweradmin 的 zone 数据全部转发给 PowerDNS API v1,此时后者是必配项。

1.3 鉴权实测:X-API-Key 与 401/403 语义

不携带 key、携带错误 key、携带有效 key 三种请求(实测回显):

$ curl -s -w '\n[%{http_code}]\n' http://127.0.0.1:8131/api/v2/zones
{"success":false,"data":null,"message":"Unauthorized: Invalid credentials"}
[401]
$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_wrong" http://127.0.0.1:8131/api/v2/zones
{"success":false,"data":null,"message":"Unauthorized: Invalid credentials"}
[401]
$ curl -s -o /dev/null -w '[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/zones
[200]

错误与缺失 key 返回同一 401 报文,不泄露 key 是否存在。另一类 403 出现在 API 总开关关闭时(实测:PA_API_ENABLED=false 后带有效 key 请求返回 {"error":true,"message":"The API feature is disabled in the system configuration."},验证过程见配置篇 7.1 节)。

二、API key 签发

2.1 签发入口:只有 Web 界面

API key 的签发与吊销没有 API 端点(OpenAPI 规范的资源清单里没有 api-keys 路径),只能在 Web 界面完成:以管理员登录 → 右上角用户菜单 → API Keys → Add API key。签发时可选:

  • 过期时间(Expire at,留空不过期);
  • 关联权限(Use permission from:跟随某用户/某权限模板,或按需勾选单项权限,如 zone_content_view_own、zone_content_edit_own)。

签发成功后界面显示一次完整 key(pwa_ 前缀 + 64 位十六进制),仅此一次,之后只显示前缀摘要。每用户默认最多 5 把 key(管理员不限),上限由 PA_API_MAX_KEYS_PER_USER 控制。

2.2 key 的权限语义

key 的实际权限 = 签发时绑定的用户/模板权限。本轮实验用的 key 绑定管理员,因此能读写全部资源;若绑定只读模板,写操作会得到 403 而非 401——排障时 401 查 key 本身、403 查开关或权限。

三、文档与调试入口:/api/docs

浏览器打开 http://<服务器>:8131/api/docs 得到内置 Swagger UI(实测页面引用 swagger-ui-dist@5.10.5),左上角可切换版本:

const apiUrls = {
    v1: '/api/docs/v1/json',
    v2: '/api/docs/v2/json'
};

/api/docs/v2/json 即 OpenAPI 规范本体(实测 285,661 字节,info.title: Poweradmin API V2 / 2.0.0),可直接导入 Postman 或用 swagger-codegen 生成客户端。规范覆盖的全部资源组(实测清单):

/v2/zones                     /v2/zones/{id}
/v2/zones/{id}/metadata(/kind)  /v2/zones/{id}/owners(/user_id)
/v2/zones/{id}/records(/bulk)   /v2/zones/{id}/records/{recordId}
/v2/zones/{id}/rrsets           /v2/zones/{id}/rrsets/{name}/{type}
/v2/users                     /v2/groups(含 members/zones 子资源)
/v2/permissions               /v2/permission-templates
/v2/zone-templates(含 /records 子资源)

注意:PA_API_ENABLED=false 只挡 /api/v2/*,/api/docs 仍可访问(配置篇 7.1 实测);想彻底关文档页需同时 PA_API_DOCS_ENABLED=false。

四、zones 资源实操

4.1 创建 zone:POST /api/v2/zones

请求体三要素:name(FQDN)、type(枚举 MASTER/SLAVE/NATIVE,必须大写)、可选 template(zone 模板 id)与 master(SLAVE 时的主服务器地址)。实测:

$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" \
    -H 'Content-Type: application/json' \
    -d '{"name":"api-test.example.com","type":"MASTER"}' \
    http://127.0.0.1:8131/api/v2/zones
{"success":true,"data":{"zone_id":3},"message":"Zone created successfully"}
[201]

返回体只给 zone_id,不回显 SOA——SOA 已按配置篇 3.2 的 DNS_NS1/DNS_HOSTMASTER 默认值自动生成(见 5.1 证明)。

4.2 列表与详情:GET /api/v2/zones、GET /api/v2/zones/{id}

$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/zones
{"success":true,"data":{"zones":[{"id":3,"name":"api-test.example.com","type":"MASTER","created_at":null},{"id":1,"name":"example.com","type":"MASTER","created_at":null}]},"message":"Zones retrieved successfully","meta":{"timestamp":"2026-10-26 15:28:19"}}
[200]
$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/zones/3
{"success":true,"data":{"zone":{"id":3,"name":"api-test.example.com","type":"MASTER","masters":null,"account":null,"description":null,"created_at":null}},"message":"Zone retrieved successfully"}
[200]

4.3 删除 zone:DELETE /api/v2/zones/{id}

$ curl -s -o /dev/null -w '[%{http_code}]\n' -X DELETE -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/zones/3
[204]

成功返回 204 无报文体——脚本判断成败要按状态码而不是解析 JSON。级联删除 zone 下全部记录。

4.4 方法不匹配实测:PATCH 返回 405

对单条记录尝试 PATCH(规范只定义了 PUT):

$ curl -s -w '\n[%{http_code}]\n' -X PATCH -H "X-API-Key: pwa_a5b5...(略)" \
    -H 'Content-Type: application/json' -d '{}' http://127.0.0.1:8131/api/v2/zones/3/records/11
{"error":true,"message":"Method not allowed"}
[405]

Poweradmin v2 API 的错误体有两种形态:业务错误用 {"success":false,...}(如 401),框架层错误用 {"error":true,...}(如 405 与 API 关闭的 403)——写统一重试逻辑时要兼容两者。

五、records 与 rrsets 实操

5.1 创建记录:POST /api/v2/zones/{id}/records

$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" \
    -H 'Content-Type: application/json' \
    -d '{"name":"www.api-test.example.com","type":"A","content":"203.0.113.77","ttl":3600}' \
    http://127.0.0.1:8131/api/v2/zones/3/records
{"success":true,"data":{"record":{"id":11,"zone_id":3,"name":"www","type":"A","content":"203.0.113.77","ttl":3600,"priority":0,"disabled":false,"auth":true,"ptr_created":false}},"message":"Record created successfully"}
[201]

两个实测细节:

  • name 自动归一化:请求传 FQDN www.api-test.example.com,落库与回显均为相对名 www;传相对名同样接受;
  • SOA 自动生成与序列号递增:建 zone 时自动写入 SOA(串号 YYYYMMDD00),每次记录变更 +1。该 zone 的记录列表回显:
$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/zones/3/records
{"success":true,"data":{"records":[{"id":11,"name":"www.api-test.example.com","type":"A","content":"203.0.113.77","ttl":3600,"priority":0,"disabled":false,"auth":true},{"id":10,"name":"api-test.example.com","type":"SOA","content":"ns1.example.com hostmaster.example.com 2026102601 28800 7200 604800 86400","ttl":86400,"priority":0,"disabled":false,"auth":true}]},"message":"Records retrieved successfully"}
[200]

5.2 TXT 记录:API 与界面的行为相反(实测对照)

界面(部署篇坑 4)要求 TXT 内容必须自带双引号,不带直接报错;API 端不带引号也能成功,Poweradmin 落库时自动补上:

$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" \
    -H 'Content-Type: application/json' \
    -d '{"name":"txt2","type":"TXT","content":"no-quotes-test","ttl":300}' \
    http://127.0.0.1:8131/api/v2/zones/4/records
{"success":true,"data":{"record":{"id":15,"zone_id":4,"name":"txt2","type":"TXT","content":"no-quotes-test","ttl":300,"priority":0,"disabled":false,"auth":true,"ptr_created":false}},"message":"Record created successfully"}
[201]

回显不带引号,但数据库里存的是带引号的规范形态(实测 SQL 查询):

MariaDB [powerdns]> SELECT id,name,type,content FROM records WHERE id=15;
+----+----------------------+------+-------------------+
| id | name                 | type | content           |
+----+----------------------+------+-------------------+
| 15 | txt2.api2.example.com | TXT  | "no-quotes-test" |
+----+----------------------+------+-------------------+

脚本化批量写 TXT 时可直接传裸值,无需照界面习惯手工加引号;带引号传入同样合法。

5.3 更新记录:PUT /api/v2/zones/{id}/records/{recordId}

$ curl -s -w '\n[%{http_code}]\n' -X PUT -H "X-API-Key: pwa_a5b5...(略)" \
    -H 'Content-Type: application/json' \
    -d '{"name":"www","type":"A","content":"203.0.113.88","ttl":300}' \
    http://127.0.0.1:8131/api/v2/zones/3/records/11
{"success":true,"data":{"record":{"id":11,"zone_id":3,"name":"www","type":"A","content":"203.0.113.88","ttl":300,"priority":0,"disabled":false,"auth":true}},"message":"Record updated successfully"}
[200]

更新后 SOA 串号由 2026102601 递增到 2026102602(rrsets 接口回显佐证,见 5.4)。注意 PUT 是全量替换:省略 disabled 等字段会回到默认值,回显里 priority:0 亦由此而来。

5.4 rrsets 视角:GET /api/v2/zones/{id}/rrsets

按「名字+类型」聚合的 rrset 视图(对齐 PowerDNS 的 rrset 概念,批量下发场景更直观):

$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/zones/3/rrsets
{"success":true,"data":{"rrsets":[{"name":"www.api-test.example.com","type":"A","ttl":300,"records":[{"content":"203.0.113.88","priority":0,"disabled":false}]},{"name":"api-test.example.com","type":"SOA","ttl":86400,"records":[{"content":"ns1.example.com hostmaster.example.com 2026102602 28800 7200 604800 86400","priority":0,"disabled":false}]}]},"message":"RRSets retrieved successfully"}
[200]

同名同类型的全部记录聚合在一个 rrset 里,records 数组即该 rrset 的成员;PATCH /rrsets 支持一次提交多个 rrset 的增改删(规范定义其请求体为 rrsets 数组,changetype 取 REPLACE/DELETE)。

5.5 删除记录:DELETE /api/v2/zones/{id}/records/{recordId}

$ curl -s -o /dev/null -w '[%{http_code}]\n' -X DELETE -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/zones/3/records/12
[204]

同样是 204 空体。与界面删除的两步确认流不同(部署篇坑 7),API 删除一步到位,没有确认环节——自动化脚本误删风险更高,建议先 GET 记录列表确认 id 再删。

六、用户与权限模板管理 API

v2 API 的管理面不止 DNS 数据,用户/组/权限模板同样全部 API 化(本轮全部实测 200):

$ curl -s -w '\n[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/users
{"success":true,"data":{"users":[{"user_id":1,"username":"admin","fullname":"Administrator","email":"admin@example.com","description":"System Administrator","active":true,"zone_count":4,"is_admin":true}]},"message":"Users retrieved successfully","meta":{"timestamp":"2026-10-26 15:38:35"}}
[200]
$ curl -s -o /dev/null -w '[%{http_code}]\n' -H "X-API-Key: pwa_a5b5...(略)" http://127.0.0.1:8131/api/v2/permission-templates
[200]

权限模板接口回显 v4.4.3 出厂自带的 10 个模板(用户 5 个 + 组 5 个):

1  Administrator   用户模板:完全管理权限
2  Zone Manager    用户模板:自有 zone 全权管理
3  Editor          用户模板:可改自有 zone 记录,不可改 SOA/NS
4  Viewer          用户模板:自有 zone 只读 + 搜索
5  Guest           用户模板:无权限占位(待审批)
6-10              Administrators / Zone Managers / Editors / Viewers / Guests(对应组模板)

典型用法链:POST /v2/users 建用户 → POST /v2/groups/{id}/members 入组 → POST /v2/groups/{id}/zones 把 zone 授权给组。组合配置篇 5.x 的密码策略与锁定参数即可完成一套纯 API 的租户开通流水线。

七、解析生效性排障实录(本轮最重要的发现)

7.1 现象:API 建的 zone 迟迟不解析

API 创建 zone 后立即用 dig 验证(在 PowerDNS 所在服务本机执行),长时间返回 NXDOMAIN,而界面上同样路径创建的 example.com 从未出现此问题。完整观测序列(全部实测):

$ curl -s -H "X-API-Key: pwa_a5b5...(略)" -H 'Content-Type: application/json' \
    -d '{"name":"api4.example.com","type":"MASTER"}' http://127.0.0.1:8131/api/v2/zones
{"success":true,"data":{"zone_id":6},"message":"Zone created successfully"}      # 201
$ dig @127.0.0.1 -p 53 api4.example.com SOA +short
(空输出)

逐一排除后确认:

  1. 数据已正确落库——Poweradmin 侧 API 回显正常,且 PowerDNS 自带 CLI 直读数据库全部可见:
$ pdnsutil list-all-zones
example.com
api2.example.com
api3.example.com
api4.example.com
$ pdnsutil list-zone api4.example.com
$ORIGIN .
api4.example.com	86400	IN	SOA	ns1.example.com hostmaster.example.com 2026102600 28800 7200 604800 86400
  1. 补记录、rectify 全部 zone 均无效——pdnsutil rectify-all-zones 回显 Rectified 4 zones. 后 dig 依旧 NXDOMAIN;
  2. 清缓存无效——pdns_control purge api4.example.com 返回 2(刷掉 2 条缓存项),dig 依旧 NXDOMAIN;
  3. 重启 PowerDNS 服务立即生效——重建服务后同一查询直接命中(实测回显):
$ dig @127.0.0.1 -p 53 api3.example.com SOA +short
ns1.example.com. hostmaster.example.com. 2026102600 28800 7200 604800 86400
  1. 行为不稳定——同一 API 路径创建的多个 zone 中,有一个在数分钟后自行恢复解析(未做任何操作),其余观测超过 8 分钟仍未恢复;重启是唯一每次都可靠的手段。具体缓存机制未在 PowerDNS 4.9.18 文档中定位到明确条目,本文只记录观测事实与可复现的处置方法。

运维口径:SQL 直连模式下用 API/脚本批量建 zone 后,把「dig +short 验证 → 不通过则重启 PowerDNS 进程」写进流水线收尾步骤(部署形态对应 docker compose restart pdns 或 systemd restart pdns)。界面手工建 zone 无此现象。

7.2 连带发现:PowerDNS webserver ACL 拦截了 Poweradmin

排查 7.1 时在 PowerDNS 日志里发现反复出现的告警(实测节选):

[webserver] Webserver closing socket: remote (10.89.60.5) does not match the set ACL(127.0.0.1/32, ::1/128)

部署篇的 pdns.conf 未设 webserver-acl,PowerDNS 默认只允许 127.0.0.1/32, ::1/128 访问其 HTTP API——而 Poweradmin 运行在另一台主机(10.89.60.5),所有 PA_PDNS_API_URL 指向的调用(PowerDNS 状态卡片、DNSSEC 签名/rectify 触发)全部被连接重置:

$ curl -sv -X PUT -H "X-API-Key: pa-pdns-api-key-4.4.3" \
    http://pdns:8081/api/v1/servers/localhost/zones/api4.example.com/rectify 2>&1 | tail -2
* Request completely sent off
* Recv failure: Connection reset by peer

修复:pdns.conf 增加一行(内网环境放开,公网实例请只放行 Poweradmin 所在网段):

webserver-acl=0.0.0.0/0,::/0

修正后同一请求返回 200。这也解释了配置篇 4.3 中「界面 DNSSEC 区块出现但签名操作依赖 pdns_api 连通性」的前置条件:SQL 直连模式下 PA_PDNS_API_URL/PA_PDNS_API_KEY 配置正确 ≠ 网络可达,ACL 是独立的一层。

八、端点与方法速查

操作方法与路径成功码备注
列 zonesGET /api/v2/zones200支持分页参数
建 zonePOST /api/v2/zones201type 必须大写枚举
看/改/删 zoneGET/PUT/DELETE /api/v2/zones/{id}200/200/204删除级联记录
列/建记录GET/POST /api/v2/zones/{id}/records200/201name 自动归一化为相对名
改/删单条记录PUT/DELETE .../records/{recordId}200/204PUT 全量替换;PATCH 是 405
批量记录POST .../records/bulk200单次上限 api.bulk_max_operations(见配置篇)
rrset 批量下发GET/PATCH/POST/PUT .../rrsets200PowerDNS 风格 changetype
zone 元数据GET/PUT/DELETE .../metadata(/kind)200SOA-EDIT 等落在 PowerDNS metadata
zone 所有者GET/POST .../owners 等200面板层授权,非 DNS 数据
用户/组/权限模板/v2/users、/v2/groups、/v2/permission-templates200/201租户管理流水线
zone 模板/v2/zone-templates(含 records 子资源)200/201建新 zone 时按 template id 引用

通用约定:鉴权头 X-API-Key;写操作请求体 Content-Type: application/json;删除类成功返回 204 无报文体;type 字段一律大写(MASTER、A、TXT)。

九、参考链接