跳到主內容

【PowerDNS】Lua Records

這是一份 PowerDNS 官方文件的完整中文翻譯。
它說明了 LUA Record(Lua 動態記錄) 的用途、語法、應用範例、進階用法與安全注意事項。
我保留了原文段落結構與技術名詞,方便你比對。


🧩 PowerDNS — Lua Records 中文說明文件

一、概述

為了提供動態行為(例如全球負載平衡 Global Server Load Balancing),
PowerDNS Authoritative Server 自 4.2 版起 支援 動態 DNS 記錄(LUA Records)

這些記錄中包含了小段設定程式(Lua 程式碼),
可根據「查詢者 IP」、「EDNS Client Subnet」、「伺服器可用性」等因素,
動態決定要回應哪個 IP 或 CNAME。

功能範圍從簡單到複雜,
可實現多池(multi-pool)地理與權重負載平衡(Geo & Weighted Load Balancing)。

雖然使用者不必了解內部原理,
但 PowerDNS 的動態記錄本質上是 嵌入在 DNS 內的小型 Lua 腳本

💡 注意:
這是 PowerDNS 專屬功能,目前尚未成為 IETF 或其他標準機構的標準。
PowerDNS 官方承諾會致力於互通性,並希望最終能成為被廣泛支持的標準。

要啟用此功能,可在設定檔中加入:

enable-lua-records=yes

或對單一 zone 設定 metadata:

ENABLE-LUA-RECORDS=1

若要使用地理位置功能,請確認 launch 設定中包含:

launch=gpgsql,geoip

⚠️ 警告:
使用 AXFR 傳輸包含長 Lua 記錄的 zone 時,請確保每筆記錄內容(TXT 型態傳輸)
不超過 255 bytes。超過時會被分割成多段,導致次伺服器解析錯誤。


二、範例說明

1️⃣ ifportup()

www IN LUA A "ifportup(443, {'192.0.2.1', '192.0.2.2'})"

這會讓 www 這個名稱隨機回應 192.0.2.1192.0.2.2
前提是這些 IP 的 TCP 443 port 是開放的

若其中一個 IP 不再回應 443,則僅回傳另一個。
若全部都不通,則全部地址會被回傳。

多組 IP 可分優先層級,例如:

www IN LUA A "ifportup(443, {{'192.0.2.1', '192.0.2.2'}, {'192.0.3.1'}})"

這表示:

  • 若第一組中的任一 IP 可用 → 回第一組;

  • 否則 → 試第二組。

因為 DNS 查詢要求極快回應,PowerDNS 不會即時檢查連線狀態,
而是由背景程序定期檢測這些 IP 是否可用。


2️⃣ pickclosest()

www IN LUA A "pickclosest({'192.0.2.1','192.0.2.2','198.51.100.1'})"

這會根據查詢者的地理位置(GeoIP),
選擇最靠近使用者的 IP 回覆。

也可以與 ifportup() 結合:

www IN LUA A ("ifportup(443, {'192.0.2.1', '192.0.2.2', '198.51.100.1'}, {selector='pickclosest'})")

這會在可用的 IP 中挑出距離使用者最近的一個。


3️⃣ 複雜 Lua 程式碼範例

www IN LUA A ";if country('US') then return {'192.0.2.1','192.0.2.2','198.51.100.1'} else return '192.0.2.2' end"

可同時回傳單一字串或多筆字串(array)。


4️⃣ 使用 qname 的例子

*.example.net 10 IN LUA TXT "; return 'Got a TXT query for ' .. qname:toString() .. '; First label is: ' .. qname:getRawLabels()[1]"

注意:qtype 無法從 Lua 內直接取得(它是固定的)。
可使用預設變數(Preset Variables)取得其他查詢相關資訊。


三、在 SQL 後端使用 Lua Records

LUA Records 可與一般的 gmysql / gpgsql 通用 SQL 後端一起使用。

由於內容同時包含單引號與雙引號,
INSERTUPDATE 時需特別轉義。

範例如下:

-- 建立 zone
INSERT INTO domains (id, name, type) VALUES (1, 'example.com', 'NATIVE');

-- 啟用 Lua 記錄功能
INSERT INTO domainmetadata (domain_id, kind, content)
VALUES (1, 'ENABLE-LUA-RECORDS', 1);

-- 建立 pickclosest() 記錄
INSERT INTO records (domain_id, name, type, content, ttl)
VALUES (
  1,
  'www.example.com',
  'LUA',
  'A "pickclosest({''192.0.2.1'',''192.0.2.2'',''198.51.100.1''})"',
  600
);

這三筆 SQL:

  • 建立 example.com zone,

  • 啟用 Lua Records,

  • 新增一筆 www.example.com 的動態 A 記錄。


四、LUA Record 格式

💡 名稱由來:
“Lua” 在葡萄牙文中意為「月亮」,不是縮寫
但 DNS 慣例記錄類型一律大寫,因此用 LUA

一筆 LUA Record 結構如下:

LUA <query_type> <Lua 程式>

例如:

www IN LUA A "pickclosest({'1.1.1.1','8.8.8.8'})"
  • 可設定 TTL,PowerDNS 會照常套用;

  • 可被 DNSSEC 簽章(需在同一伺服器即時簽署)。


五、更強大的範例

west IN LUA A ("ifurlup('https://www.lua.org/', {{'192.0.2.1', '192.0.2.2'}, {'198.51.100.1'}}, {stringmatch='Programming in Lua'})")

這會:

  • 嘗試對 www.lua.org 發出 HTTPS;

  • 若回應內容包含 Programming in Lua,則視為可用;

  • 第一組 IP 可用 → 回第一組;否則 → 回第二組。

與地理導向結合:

www IN LUA CNAME (";if(continent('EU')) then return 'west.powerdns.org' else return 'usa.powerdns.org' end")

對應的 usa 名稱配置如下:

usa IN LUA A ("ifurlup('https://www.lua.org/', {{'198.51.100.1'}, {'192.0.2.1', '192.0.2.2'}}, {stringmatch='Programming in Lua'})")

歐洲以外的訪客會優先使用 198.51.100.1
若失效則回到 192.0.2.1 / 192.0.2.2


六、進階主題

(1) 語法模式

若記錄內容未以 ; 開頭,PDNS 會自動加上 return
若要撰寫完整 Lua 腳本,請以分號 ; 開頭。

(2) 可引用其他記錄的設定

可用 include() 引用共用設定:

config IN LUA LUA ("settings={stringmatch='Programming in Lua'} EUips={'192.0.2.1','192.0.2.2'} USAips={'198.51.100.1'}")

usa IN LUA A (";include('config') return ifurlup('https://www.lua.org/', {USAips, EUips}, settings)")
west IN LUA A (";include('config') return ifurlup('https://www.lua.org/', {EUips, USAips}, settings)")

七、安全與同步機制

  • LUA Record 是查詢時即時生成;

  • 可透過 AXFR 同步至其他 PDNS 伺服器;

  • 但無法直接查詢(防止外部洩露負載邏輯)。

⚠️ 警告:

  • 絕對不要從不信任的來源匯入含 LUA Record 的 zone!
    惡意 Lua 可能導致伺服器崩潰甚至被接管。

  • 即使是可信來源也請使用 TSIG 保護 AXFR 傳輸。

若有 DNSSEC 簽章需求,
LUA Record 必須由主伺服器即時簽署(無法預先簽署 zone)。


八、共享 Lua 狀態模式(shared mode)

預設情況下,PDNS 每次查詢都會建立新的 Lua 執行環境。
這樣每筆 LUA Record 彼此獨立,不會污染變數。
但建立 Lua state 很耗時。

可啟用共用模式:

enable-lua-records=shared
  • 每個執行緒建立一個 Lua 狀態並重複使用;

  • 約可提升 QPS 10 倍;

  • 各執行緒 Lua 環境獨立(UDP 為多實例,TCP 共用一個)。


九、Reference(函式與物件類別)

PowerDNS 提供的 Lua 內建 API 包含:

預設變數(Preset Variables)

  • qname, qtype, remoteip, ednssubnet

可用函式

  • getdns(), ifportup(), pickclosest(), country(), continent()

  • ifurlup(), include(), pdnslog(), 等

物件類別

  • DNSName, ComboAddress, DNSResourceRecord, DNSHeader, QType

  • Netmask, ComboAddressSet, DNSRecord, 及輔助函式群組

(這些內容在官方文件的「LUA Reference」章節中詳細列出)


🔚 總結

功能 說明
LUA Record PowerDNS 內建的動態 DNS 回覆機制
語言 Lua
用途 智能選址、健康檢查、地理導向、服務偵測
啟用方式 enable-lua-records=yesENABLE-LUA-RECORDS=1
安全性 僅限信任 zone;建議使用 TSIG
效能優化 enable-lua-records=shared 可大幅提高 QPS

是否要我幫你把這篇翻譯版整理成 可直接放進公司內部 Wiki 的 Markdown 版(含語法高亮與範例區塊)
我可以自動加上標題層級、代碼框、注意區塊與翻譯對照欄位。