# 【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 &amp; 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()

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

```

這會讓 `www` 這個名稱隨機回應 `192.0.2.1` 或 `192.0.2.2`，  
前提是這些 IP 的 **TCP 443 port 是開放的**。

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

多組 IP 可分優先層級，例如：

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

```

這表示：

- 若第一組中的任一 IP 可用 → 回第一組；
- 否則 → 試第二組。

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

---

### 2️⃣ pickclosest()

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

```

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

也可以與 `ifportup()` 結合：

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

```

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

---

### 3️⃣ 複雜 Lua 程式碼範例

```dns
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 的例子

```dns
*.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 後端一起使用。

由於內容同時包含單引號與雙引號，  
在 `INSERT` 或 `UPDATE` 時需特別轉義。

範例如下：

```sql
-- 建立 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 程式>

```

例如：

```dns
www IN LUA A "pickclosest({'1.1.1.1','8.8.8.8'})"

```

- 可設定 TTL，PowerDNS 會照常套用；
- 可被 DNSSEC 簽章（需在同一伺服器即時簽署）。

---

## 五、更強大的範例

```dns
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 可用 → 回第一組；否則 → 回第二組。

與地理導向結合：

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

```

對應的 `usa` 名稱配置如下：

```dns
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()` 引用共用設定：

```dns
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」章節中詳細列出）

---

## 🔚 總結

<table id="bkmrk-%E5%8A%9F%E8%83%BD-%E8%AA%AA%E6%98%8E-lua-record-pow"><thead><tr><th>功能</th><th>說明</th></tr></thead><tbody><tr><td>**LUA Record**</td><td>PowerDNS 內建的動態 DNS 回覆機制</td></tr><tr><td>**語言**</td><td>Lua</td></tr><tr><td>**用途**</td><td>智能選址、健康檢查、地理導向、服務偵測</td></tr><tr><td>**啟用方式**</td><td>`enable-lua-records=yes` 或 `ENABLE-LUA-RECORDS=1`</td></tr><tr><td>**安全性**</td><td>僅限信任 zone；建議使用 TSIG</td></tr><tr><td>**效能優化**</td><td>`enable-lua-records=shared` 可大幅提高 QPS</td></tr></tbody></table>

---

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