跳到主內容

【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


注意事項與建議

  1. 後端限制
    某些後端 (特別是 BIND 後端) 可能不支援修改操作(只能透過 zone transfer)或不支援 pdnsutil 操作。要確認你使用的後端是否允許這些操作。 (Reddit)

  2. 版本差異
    不同 PowerDNS 版本對於 DNSSEC 的預設邏輯、支援的演算法、金鑰長度限制等可能不同。建議查看你系統上 man pdnsutil 或官方版本對照表。 (Debian Manpages)

  3. 序號 (Serial) 管理
    在變更 zone 後,務必更新 SOA 的 serial(可用 increase-serial 或手動編輯 SOA)並通知 slave。否則 slave 可能不會接收到變更。

  4. 測試 / 錯誤檢查
    使用 check-zone / check-all-zones 來檢查語法或邏輯錯誤,以免上線後 DNS 無法解析。 (cPanel Support)

  5. 備份與金鑰匯出
    在做 DNSSEC 金鑰 rollover 或改動前,建議匯出金鑰備份(export-zone-key)及對 DS 記錄操作要小心,避免中斷解析鏈。

 


🧩 一、PowerDNS 查詢流程(簡化版)

當你執行:

dig app-wekan.momoshop.com.tw

時,PDNS 的查詢處理順序如下:

  1. 找 zone(domains 表)

    • domains.name 開始,找最匹配的 zone(例如 momoshop.com.tw)。

  2. 在 records 表中找符合的 name

    • 順序為:

      • 精確比對 name='app-wekan.momoshop.com.tw'

      • 如果找不到,則依序比對:

        • *.momoshop.com.tw

        • *.com.tw

        • *.tw

    • 每一層可能都會回傳多筆記錄。

  3. 根據 type 過濾

    • 只會選出符合查詢類型(例如 A)或可解譯的 LUA record。

  4. 若有 LUA 記錄,執行 Lua 腳本

    • 若多筆 LUA record,PDNS 會依「資料庫記錄順序(id 由小到大)」逐筆執行;

    • 直到第一筆成功產生合法的回應為止。

  5. 若多筆普通記錄(A, CNAME)

    • 全部回傳

    • 若 TTL 不同,取最小 TTL 為最終 TTL。

  6. 若無任何匹配

    • 回覆 NXDOMAINSERVFAIL(視 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️⃣ 沒有符合 → NXDOMAINSERVFAIL  

🧩 五、如何查看 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 判斷 → 最終回應」。