【PowerDNS】
Lua records
https://doc.powerdns.com/authoritative/lua-records/index.html
-- 返回请求方 IP
who:toString()
-- 根据请求方 IP 不同返回不同地址
view({
{{'10.10.0.0/16'}, {'10.10.1.1'}}
{{'0.0.0.0/0'}, {'192.0.2.1'}}
})
-- 返回存活的 IP
-- 异步检测 - 如果都不存活则都返回
ifportup(443, {'192.0.2.1', '192.0.2.2'})
-- 使用 URL 检测存活 - stringmatch 为可选的内容匹配条件
ifurlup("example.com/status", { {"192.0.2.20", "203.0.113.4"}, {"203.0.113.2"} },{stringmatch='ok'})
-- 基于请求方的 HASH(bestwho) 进行返回
pickwhashed({{10,'192.168.0.1'},{100,'192.168.0.2'}})
-- 随机返回 - 有带权重版
pickrandom({{'192.168.0.1'},{'192.168.0.2'}})
pickwrandom({{10,'192.168.0.1'},{100,'192.168.0.2'}})
pdnsutil
下面是 pdnsutil(PowerDNS 的指令工具,用於 DNS 區域與 DNSSEC 管理)的一份常用語法與操作範例教學。你可以把它當作 cheat-sheet 或操作對照表。
(若你的 PowerDNS 版本略有不同或後續改版,部分語法可能略有出入,建議搭配 man pdnsutil 或官方文檔確認) (doc.powerdns.com)
基本語法結構與通用選項
pdnsutil [OPTIONS] COMMAND [ARGUMENTS...]
常見的選項:
| 選項 | 說明 |
|---|---|
-h, --help |
顯示協助訊息 |
-v, --verbose |
輸出較詳細訊息 |
--force |
強制執行(覆寫、跳過某些檢查等) |
--config-name <NAME> |
指定虛擬設定名稱 |
--config-dir <DIR> |
指定 pdns.conf 所在目錄(預設 /etc/powerdns) |
常見區域 (zone) 管理指令
以下指令幾乎涵蓋新增、查看、編輯、刪除、檢查等需求。
| 功能 | 指令範例 | 說明 / 備註 |
|---|---|---|
| 建立區域 | pdnsutil create-zone example.com |
建立一個空的 zone example.com,內部會自動加入一条 NS 記錄 (blog.powerdns.com) |
| 刪除區域 | pdnsutil delete-zone example.com |
將該 zone 從後端資料庫中移除 |
| 顯示區域內容 | pdnsutil show-zone example.com |
列出 zone 的所有 resource record 和 meta 資訊 (blog.powerdns.com) |
| 列出區域 | pdnsutil list-zone example.com |
列出該區域的所有資料記錄 (blog.powerdns.com) |
| 列出所有區域 | pdnsutil list-all-zones |
顯示所有已註冊的 zone 名稱 (Debian Manpages) |
| 檢查單個區域 | pdnsutil check-zone example.com |
檢查該 zone 是否有錯誤(語法、衝突等) (cPanel Support) |
| 檢查所有區域 | pdnsutil check-all-zones |
對所有 zones 執行檢查 (CloudWizard.nl) |
| 編輯區域(交互式) | pdnsutil edit-zone example.com |
進入文字編輯介面,可以手動修改 zone 的所有記錄(系統會做基本檢查) (blog.powerdns.com) |
| 增加記錄 | pdnsutil add-record example.com sub A 3600 1.2.3.4 |
在 example.com 區域新增一條 A 紀錄: sub.example.com TTL 3600 指向 1.2.3.4 (blog.powerdns.com) |
| 更新(替換)整組記錄 | pdnsutil replace-rrset example.com sub A 3600 5.6.7.8 |
將 sub 的 A 類型記錄整組替換為新的值(如果有多條) (blog.powerdns.com) |
| 刪除一組記錄 | pdnsutil delete-rrset example.com sub A |
刪除 sub 的所有 A 類型記錄(整個 RRset) (HackMD) |
| 增加序號 | pdnsutil increase-serial example.com |
手動把 SOA 的 Serial 加一(通常在變更後需告知 slave) (CloudWizard.nl) |
| 設定 zone 類型 | pdnsutil set-kind example.com MASTER |
設定該 zone 為主(zone master)、從(zone slave)或其他類型 (CloudWizard.nl) |
| 設定 meta (zone metadata) | pdnsutil set-meta example.com ALLOW-AXFR-FROM AUTO-NS |
為該 zone 加上 metadata,例如允許哪些 IP 做 AXFR 等 (blog.powerdns.com) |
DNSSEC / 金鑰 (key) 管理指令
如果你要為 zone 啟用 DNSSEC、管理金鑰或做 key rollover 等,這些指令就會派上用場。 (doc.powerdns.com)
| 功能 | 指令範例 | 說明 / 備註 |
|---|---|---|
| 啟用 DNSSEC(簽名 zone) | pdnsutil secure-zone example.com |
為該 zone 建立金鑰並簽名(預設配置) (doc.powerdns.com) |
| 列出該 zone 的金鑰 | pdnsutil list-keys example.com |
顯示該 zone 所有的 KSK / ZSK 金鑰資訊 (SIDN - Het bedrijf achter .nl) |
| 新增金鑰 | pdnsutil add-zone-key example.com ZSK active 2048 rsasha256 |
為例:在 zone example.com 新增一組 ZSK 金鑰,256 RSA, 2048 位元長度。可調整參數。 (Debian Manpages) |
| 啟用金鑰 | pdnsutil activate-zone-key example.com 12345 |
啟用某一個金鑰(以其 key-id 為識別) (Debian Manpages) |
| 停用金鑰 | pdnsutil deactivate-zone-key example.com 12345 |
停用該金鑰(使其不再用於簽名) (Debian Manpages) |
| 匯出公鑰與 DS 記錄 | pdnsutil export-zone-ds example.com |
匯出該 zone 所有 KSK 的 DS 記錄(可提交給上層域名註冊商) (SIDN - Het bedrijf achter .nl) |
| 匯出金鑰 | pdnsutil export-zone-key example.com 12345 |
匯出該 zone 指定金鑰(含私鑰)到檔案或標準輸出 (SIDN - Het bedrijf achter .nl) |
| 移除金鑰 | pdnsutil remove-zone-key example.com 12345 |
將指定金鑰從 zone 中移除(不再保留) (SIDN - Het bedrijf achter .nl) |
| 停用 DNSSEC(取消簽名) | pdnsutil disable-dnssec example.com |
將 zone 還原為不使用 DNSSEC 狀態 (SIDN - Het bedrijf achter .nl) |
| 修正 (rectify) zone 資料庫 | pdnsutil rectify-zone example.com |
重建 zone 在資料庫中的 internal 欄位(如 auth / ordername) (SIDN - Het bedrijf achter .nl) |
| 修正所有 zone | pdnsutil rectify-all-zones |
對所有已存在的 zone 執行 rectify 操作 (SIDN - Het bedrijf achter .nl) |
一些操作流程範例
以下是比較常見的操作流程,依步驟示範怎麼做:
範例:建立一個新的 zone 並設定記錄 + 啟用 DNSSEC
假設 domain 為 example.com:
pdnsutil create-zone example.com
pdnsutil set-kind example.com MASTER
pdnsutil add-record example.com @ NS ns1.example.com.
pdnsutil add-record example.com @ NS ns2.example.com.
pdnsutil add-record example.com ns1 A 1.2.3.4
pdnsutil add-record example.com ns2 A 5.6.7.8
pdnsutil replace-rrset example.com . SOA 'ns1.example.com. hostmaster.example.com. 1 10800 3600 604800 3600'
pdnsutil secure-zone example.com
pdnsutil increase-serial example.com
pdns_control notify example.com
這樣做完後,你可以用 pdnsutil show-zone example.com 檢查結果。 (CloudWizard.nl)
範例:修改已有記錄
假設要把 www.example.com 的 A 紀錄從 1.2.3.4 換成 5.5.5.5:
pdnsutil replace-rrset example.com www A 3600 5.5.5.5
pdnsutil increase-serial example.com
pdns_control notify example.com
或若要直接刪除 www 的 A 紀錄:
pdnsutil delete-rrset example.com www A
pdnsutil increase-serial example.com
pdns_control notify example.com
最後別忘了通知 slave 或其他 server 更新:pdns_control notify example.com。
注意事項與建議
-
後端限制
某些後端 (特別是 BIND 後端) 可能不支援修改操作(只能透過 zone transfer)或不支援pdnsutil操作。要確認你使用的後端是否允許這些操作。 (Reddit) -
版本差異
不同 PowerDNS 版本對於 DNSSEC 的預設邏輯、支援的演算法、金鑰長度限制等可能不同。建議查看你系統上man pdnsutil或官方版本對照表。 (Debian Manpages) -
序號 (Serial) 管理
在變更 zone 後,務必更新 SOA 的 serial(可用increase-serial或手動編輯 SOA)並通知 slave。否則 slave 可能不會接收到變更。 -
測試 / 錯誤檢查
使用check-zone/check-all-zones來檢查語法或邏輯錯誤,以免上線後 DNS 無法解析。 (cPanel Support) -
備份與金鑰匯出
在做 DNSSEC 金鑰 rollover 或改動前,建議匯出金鑰備份(export-zone-key)及對 DS 記錄操作要小心,避免中斷解析鏈。
🧩 一、PowerDNS 查詢流程(簡化版)
當你執行:
dig app-wekan.momoshop.com.tw
時,PDNS 的查詢處理順序如下:
-
找 zone(domains 表)
-
從
domains.name開始,找最匹配的 zone(例如momoshop.com.tw)。
-
-
在 records 表中找符合的 name
-
順序為:
-
精確比對
name='app-wekan.momoshop.com.tw' -
如果找不到,則依序比對:
-
*.momoshop.com.tw -
*.com.tw -
*.tw
-
-
-
每一層可能都會回傳多筆記錄。
-
-
根據 type 過濾
-
只會選出符合查詢類型(例如 A)或可解譯的 LUA record。
-
-
若有 LUA 記錄,執行 Lua 腳本
-
若多筆 LUA record,PDNS 會依「資料庫記錄順序(id 由小到大)」逐筆執行;
-
直到第一筆成功產生合法的回應為止。
-
-
若多筆普通記錄(A, CNAME)
-
會全部回傳;
-
若 TTL 不同,取最小 TTL 為最終 TTL。
-
-
若無任何匹配
-
回覆
NXDOMAIN或SERVFAIL(視 zone 與設定而定)。
-
🧠 二、LUA Record 的詳細執行順序
當 PDNS 找到多筆 type='LUA' 的記錄時:
| 順序 | 行為 |
|---|---|
| 1️⃣ | 按照 id 由小到大排序 |
| 2️⃣ | 依序執行每筆 LUA 程式 |
| 3️⃣ | 第一個「有成功回傳記錄」的 LUA 結果會被採用 |
| 4️⃣ | 若 LUA 執行失敗(語法錯誤或回傳空值) → 嘗試下一筆 |
| 5️⃣ | 全部失敗 → 回 SERVFAIL |
✅ 範例:
假設你有:
id | name | type | content
---+--------------------+------+------------------------------------------
1 | *.momoshop.com.tw | LUA | return getdns("10.2.1.11", qname, qtype)
2 | *.momoshop.com.tw | LUA | return getdns("8.8.8.8", qname, qtype)
查詢 app.momoshop.com.tw 時:
-
PDNS 會先執行第 1 筆(id=1),
如果 10.2.1.11 無法回應或錯誤 → 自動執行第 2 筆(id=2)。 -
若第 1 筆成功,第二筆不會被執行。
👉 因此你可以用多筆 LUA record 來實作「多層 fallback」。
⚙️ 三、普通記錄(非 LUA)的多筆處理邏輯
若同一個 name 有多筆非 LUA 記錄(例如多個 A 記錄):
| 記錄 | TTL | IP |
|---|---|---|
| app.momoshop.com.tw | 60 | 10.2.1.11 |
| app.momoshop.com.tw | 120 | 10.2.1.12 |
PDNS 會:
-
同時回傳兩筆;
-
最終 TTL = min(60, 120) = 60。
回覆結果:
app.momoshop.com.tw. 60 IN A 10.2.1.11
app.momoshop.com.tw. 60 IN A 10.2.1.12
🔍 四、查詢過程中,實際的比對順序(Wildcard / LUA 混合時)
PowerDNS 在有通配符與 LUA 混用的情況下,查詢順序如下:
| 優先順序 | 比對型態 | 是否執行 LUA |
|---|---|---|
| 1️⃣ | 精確比對(如 app.momoshop.com.tw) |
是(若 type=LUA) |
| 2️⃣ | wildcard 比對(如 *.momoshop.com.tw) |
是(若 type=LUA) |
| 3️⃣ | 更高層 wildcard(如 *.com.tw) |
是(若 type=LUA) |
| 4️⃣ | zone SOA / NS | 否 |
| 5️⃣ | 沒有符合 → NXDOMAIN 或 SERVFAIL |
🧩 五、如何查看 PowerDNS 真實回應順序
你可以用:
pdnsutil list-zone momoshop.com.tw
觀察記錄順序。
PowerDNS 的執行順序就是按照這個清單的排列(id遞增)。
若你要控制順序,可以直接修改 id 或刪除後重新新增。
🧪 六、測試方式建議
建立多筆 LUA fallback:
INSERT INTO records (domain_id, name, type, content, ttl)
VALUES
(3, '*.momoshop.com.tw', 'LUA', 'return getdns("10.2.1.11", qname, qtype)', 60),
(3, '*.momoshop.com.tw', 'LUA', 'return getdns("8.8.8.8", qname, qtype)', 60);
測試:
journalctl -u pdns -f &
dig @127.0.0.1 -p 5501 app.momoshop.com.tw
你會看到:
Executing LUA record id=1: getdns(10.2.1.11)
Failed: timeout
Executing LUA record id=2: getdns(8.8.8.8)
Success → returning result
🔚 結論摘要
| 類型 | 行為 | 回應方式 |
|---|---|---|
| 多筆 A 記錄 | 全部回傳 | TTL 取最小 |
| 多筆 LUA 記錄 | 依 ID 順序執行 | 第一筆成功即返回 |
| 通配符 vs 精確匹配 | 精確優先於通配符 | |
| LUA 與非 LUA 混用 | LUA 先執行 | 若 LUA 無結果再回傳靜態記錄 |
是否要我幫你畫一張「PowerDNS 回應決策順序」的流程圖(含 fallback 流程)?
會清楚標出「多筆 LUA record 執行順序 → fallback 判斷 → 最終回應」。