跳轉到

Zone Aliasing 原理

Zone aliasing 是 ShadowDNS 的核心機制:root domain 完整載入記憶體,備援網域只是一個指向 root 的指標,查詢時透過 in-bailiwick rewriting 即時產生「看起來像完整備援 zone」的回應。本頁說明查詢處理管線的四個階段與改寫規則。

查詢處理管線

Client query
     |
     v
[ View Matcher ]
     |   依宣告順序評估 match-clients 規則(GeoIP country、
     |   GeoIP ASN、IP/CIDR、any)。First match wins,回傳 view 名稱。
     |
     v
[ Alias Resolver ]
     |   檢查被查詢的 zone 是否為備援 alias。是的話,先把查詢名稱
     |   從 backup.domain 改寫為 root.domain 再查詢,
     |   並記下原始備援名稱供回應使用。
     |
     v
[ Zone Lookup ]
     |   在選定 view 的記憶體 zone tree(map[ownerName][]RR)中
     |   尋找符合的 owner entry,每個 owner name O(1) 查詢。
     |   無精確命中時,依 RFC 4592 嘗試 wildcard 比對:由左而右
     |   逐層剝除 label,直到找到 `*.<parent>` entry,或被既有
     |   名稱阻擋(ENT 規則)。
     |
     v
[ In-Bailiwick Rewrite ]
     |   把 owner name 改寫回備援網域。RDATA 中含 DNS 名稱的欄位
     |   (CNAME target、NS、MX、SRV、SOA MNAME/RNAME):若 target
     |   指向 root zone 內部,改寫為備援 zone;指向其他位置的 target
     |   (例如第三方 CDN 主機名)保持不變。
     |
     v
Response sent to client

各階段細節

View Matcher

每個 view 的 match-clients 區塊在啟動時編譯為有序的規則 slice。規則由左至右評估,第一條命中 client 來源 IP 的規則決定 view;沒有任何 view 命中時回應 REFUSED。GeoIP 查詢使用直接讀入記憶體的 MaxMind mmdb;mmdb 檔案在每次 SIGHUP reload 時重新開啟,MaxMind 的每月更新不需重啟 process 即可生效。

Alias Resolver

查詢時,resolver 對 alias map(啟動時從 shadowdns.yamlaliases 區段建立)做 longest-suffix match。備援 zone entry 是一個薄指標 —— resolver 剝除備援後綴、替換為 root 後綴,把改寫後的名稱交給 zone lookup。原始備援名稱會保留下來,供改寫階段還原。

Zone Lookup

Zone 資料以 map[viewName]map[zoneName]*Zone 儲存,每個 Zone 持有 map[ownerName][]dns.RR。所有結構在啟動後唯讀,讀取路徑不需要任何鎖。

精確比對無結果時,依 RFC 4592 退回 wildcard 比對:從查詢名稱逐一剝除 DNS label,探測 map 中是否存在 *.<parent> entry,直到抵達 zone origin,或被阻擋進一步遍歷的既有名稱擋下(empty non-terminal 規則)。支援 CNAME wildcard 合成與正確的回應 owner name 改寫。

備援覆寫紀錄(備援 zone 自有 zone file 提供的 TXT、MX、SRV)獨立儲存,在 root lookup 之後合併進結果。

In-Bailiwick Rewrite

改寫規則刻意保守:

對象 改寫行為
Owner name 一律改寫(依定義必然 in-bailiwick)
RDATA 中的 DNS 名稱(CNAME target、NS、MX、SRV、SOA MNAME/RNAME) 只在指向 root zone 內部時改寫 —— 確保改寫後的名稱也能透過同一套 alias 機制正確解析
指向外部的 RDATA 名稱(如第三方 CDN 主機名) 保持不變
A / AAAA 承載 IP 位址,永不改寫
TXT RDATA 視為不透明資料,永不改寫 —— 即使內容字串恰好等於 root domain 名稱

SOA 繼承與 zone transfer

  • 備援 zone 的 SOA 繼承自 root zone(serial 跟隨 root),因此 slave 能正確偵測變更。
  • AXFR(TCP 全量 zone transfer)對 root zone 與 alias zone 都支援;既有 BIND slave 不需任何修改。
  • NOTIFY 在啟動與 reload 後送往各 zone 的 NS record(可用 --no-notifyoptions { notify no; }; 停用)。NOTIFY 目標 IP 只取自 in-zone glue record,細節見從 BIND 遷移

設定範例

# shadowdns.yaml
aliases:
  example.com:          # root: fully loaded into memory
    - backup.example.com    # backup: a pointer to example.com
    - mirror.example.com

www.backup.example.com 的 A 查詢,回應與「載入了一份完整的 backup.example.com zone」完全相同 —— 但記憶體中只有 example.com 一份權威資料。

aliases 的完整規則(唯一性、self-alias 禁止、覆寫紀錄類型限制)見 shadowdns.yaml