WireGuard DNS 失效的 3 種原因與修正方法
WireGuard tunnel 已建立卻無法解析 DNS,或查詢外洩至本機路由器?本文辨識 3 種失敗模式,涵蓋 resolver 無回應、路由錯誤與 resolver manager 覆寫設定。
WireGuard tunnel 建立後 DNS 立即失效的原因
透過 WireGuard 使用 DNS 有 3 種失敗情況,各自需要不同的修正方式。可能是完全無法解析,或名稱可以解析但查詢封包離開電腦時未通過 tunnel,也可能是用戶端自己的 resolver manager 在介面啟動數秒後覆寫設定。問題幾乎從來不在 tunnel 本身,而在於告訴用戶端應查詢哪個 resolver 的那一行,以及決定封包如何前往該 resolver 的路由。
WireGuard 傳送 IP 封包,完全不了解 DNS(domain name system,也就是將 example.com 等名稱轉換為 IP 位址的服務)。用戶端 [Interface] 區塊中的 DNS = 行不是 WireGuard 設定。它會由 wg-quick 讀取;wg-quick 是負責啟動介面的 shell wrapper,而 wg-quick 會在 tunnel 運作期間修改用戶端的 resolver 設定,並在 tunnel 關閉時於 wg-quick down 還原。因此,以下每個問題都是路由問題或 wg-quick 問題,絕不是加密問題。如果 tunnel 尚未建立,請先參閱在自己的 VPS 上自行託管 WireGuard VPN,再返回本頁。
在處理 DNS 前,先確認 tunnel 運作正常。
sudo wg show
ping -c 3 10.8.0.1
ping -c 3 1.1.1.1wg show 應列出具有最新 latest handshake 的 peer,且兩個 ping 都應收到回應。如果 ping 1.1.1.1 發生逾時,最可能是 forwarding 或 NAT(network address translation)問題,而不是 DNS 問題;修改多少 resolver 設定都無法解決。會丟棄 ICMP 的防火牆也會造成相同的逾時,因此請先透過 curl -sI https://1.1.1.1 確認,再開始修改 forwarding 規則。如果 ping 有回應,但開始傳送實際流量後 throughput 大幅下降,則是另一個問題;WireGuard 速度緩慢幾乎都可追溯至 MTU,與本頁內容無關。本頁所有範例都使用 10.8.0.0/24 作為 tunnel subnet,並使用 10.8.0.1 作為伺服器的 tunnel 位址。請替換為你自己的值。
失敗 1:完全無法解析,因為解析器從未回應
症狀很明確。ping 1.1.1.1 可正常運作,而 curl https://example.com 會回傳:
curl: (6) Could not resolve host: example.com直接從用戶端向通道解析器提出查詢。dig 來自 Ubuntu 和 Debian 的 dnsutils 套件。
dig +short @1.1.1.1 example.com
dig +short @10.8.0.1 example.com第一個命令會回傳位址,表示封包可透過通道到達網際網路。第二個命令沒有回傳內容,並顯示 ;; communication timed out; no servers could be reached。診斷結果就是這樣:用戶端指向 10.8.0.1,而 10.8.0.1 沒有在 UDP port 53 回應。
這通常有兩個原因。伺服器上沒有執行解析器,或伺服器防火牆在查詢抵達前就將其丟棄。請在伺服器上檢查這兩點。
sudo ss -ulnp | grep ':53'
sudo nft list ruleset正常執行且繫結正確的解析器會顯示包含 10.8.0.1:53 或 0.0.0.0:53 的行。在 Ubuntu 上,常見的問題是 127.0.0.53:53:這是 systemd-resolved 的 stub listener,會繫結回送位址,並刻意不允許其他機器連線。將 VPN 用戶端指向只使用此 stub 的伺服器,就會產生完全相同的逾時。
解決方式是讓解析器監聽通道位址,並加入一條允許 peer 存取解析器的防火牆規則。
sudo apt update && sudo apt install -y unbound
printf 'server:\n interface: 10.8.0.1\n access-control: 10.8.0.0/24 allow\n' \
| sudo tee /etc/unbound/unbound.conf.d/wireguard.conf
sudo systemctl restart unbound
sudo ss -ulnp | grep '10.8.0.1:53'接著只開放通道流量使用的 port。使用 nftables 時,將以下兩行加入 /etc/nftables.conf 中的 input chain,然後使用 sudo systemctl reload nftables 重新載入。
udp dport 53 iifname "wg0" accept
tcp dport 53 iifname "wg0" accept使用 ufw 時,sudo ufw allow in on wg0 to any port 53 會執行相同工作。切勿將 port 53 開放給公開網際網路。掃描器會在數天內找到開放的遞迴解析器,並用來放大阻斷服務攻擊;你的供應商可能會比你更早注意到這些流量。
從用戶端重新執行 dig +short @10.8.0.1 example.com。如果輸出包含位址,表示解析器路徑正常,接下來只需讓用戶端使用該解析器。將該行加入用戶端的 [Interface] 區塊,然後使用 sudo wg-quick down wg0 && sudo wg-quick up wg0 重新啟動介面。
[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1失敗二:DNS 洩漏,因為 split tunnel 不會路由 resolver
這個問題更嚴重,因為表面上一切都正常。名稱可以解析,網頁也能載入,但查詢會以明文通過你原本不想信任的區域網路。
有兩種設定會造成這個問題。第一種是用 AllowedIPs = 0.0.0.0/0, ::/0 的用戶端,但沒有 DNS = 行。wg-quick 會在自己的 routing table 中安裝預設路由,並使用 suppress_prefixlength 0 加入規則。這會刻意保留更具體的本機路由,讓機器仍可連線到自己的印表機。用戶端透過 DHCP 取得的 resolver 通常是 192.168.1.1 的路由器,且會符合其中一條本機路由。你的流量會經過 tunnel,但區域網路仍會收到你查詢的完整名稱清單。
第二種是 split tunnel:使用 AllowedIPs = 10.8.0.0/24 搭配 DNS = 9.9.9.9。由於 9.9.9.9 不在 AllowedIPs 內,用戶端沒有可透過 tunnel 到達該 resolver 的路由,因此查詢會如同第一種情況,經由本機連線送出。
請確認實際回應查詢的是哪個 resolver。whoami.akamai.net 是一個公開測試名稱,會以提出查詢的 recursive resolver 之 IP 位址回應,因此你可以將其結果與伺服器的公開位址比較。
resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53resolvectl status 會為每個連線列印一個區塊。如果 ethernet 或 wireless 連線的區塊仍顯示 Current DNS Server: 192.168.1.1,而 wg0 區塊沒有顯示任何項目,這就是洩漏。dig +short whoami.akamai.net 回傳你的家用寬頻位址,而不是伺服器的位址,則可從遠端確認這一點。tcpdump 行是判定結果的關鍵:正常輸出會讓離開機器的所有 port 53 封包都通過 wg0;發生洩漏時,封包則會通過 wlan0 或 enp3s0。往返 lo 與 127.0.0.53 的封包,是應用程式與本機 systemd-resolved stub 通訊所產生的封包,不算 DNS 洩漏。
修正方式分成兩部分,兩者都必須完成。將 DNS 設為位於 tunnel 內的位址,並確認該位址位於 AllowedIPs 內。
[Interface]
Address = 10.8.0.2/32
DNS = 10.8.0.1
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.8.0.0/24, 10.20.0.0/1610.8.0.1 位於 10.8.0.0/24 內,因此查詢會加密並傳送至伺服器。如果你堅持在 split tunnel 上使用公開 resolver,請將它加入為 host route:AllowedIPs = 10.8.0.0/24, 9.9.9.9/32。如此一來封包會經過 tunnel,但區域網路仍可從先前的連線看出你選用了哪個 provider。自行執行 resolver 可以避免這個問題。
Resolver 指派是手動建置 WireGuard 與協調式 mesh 之間的明顯差異之一,也是 WireGuard 與 Tailscale 的比較 中的取捨。執行 自架 Headscale control server,即可取得這種協調能力,而不必將 key material 交給第三方。如果你擔心的是最後這一點,請注意 Tailscale 不會持有用來加密流量的 keys;更值得追問的是,遭入侵的協調伺服器或遭竊的身分帳戶,可能為你的網路增加哪些能力。
Linux 用戶端上的 resolvconf 與 systemd-resolved 互相衝突
macOS、Windows、iOS 和 Android 用戶端會透過官方應用程式套用 DNS =,通常不會造成太多問題。Linux 的情況較複雜,因為設定是由 shell script 套用,而該 script 必須推測系統使用多種 resolver manager 中的哪一種。
第一個問題會直接顯示錯誤。sudo wg-quick up wg0 停止並顯示:
resolvconf: command not foundwg-quick 會呼叫 resolvconf,但系統未安裝該 binary。在 Debian 12 以及 Ubuntu 24.04 或更新版本中,與 systemd-resolved 通訊的 resolvconf command 包含在 systemd-resolved package 中,因此請安裝該 package,再次啟用介面。Ubuntu 22.04 沒有獨立的 package;請在該版本自行使用 sudo ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf 建立相容性連結,並略過安裝指令。
sudo apt install -y systemd-resolved
sudo systemctl restart systemd-resolved
sudo wg-quick down wg0 && sudo wg-quick up wg0
resolvectl status wg0第二個問題不會顯示錯誤,也是最容易耗掉一整晚時間的問題。介面已啟用,resolvectl status wg0 正確顯示 DNS Servers: 10.8.0.1,但 DNS lookup 仍然送往舊的 resolver。systemd-resolved 並沒有忽略 wg0。不符合任何 routing domain 的名稱,會送往每個被視為 default route 的 link 上設定的 DNS server;具有 DNS server、但沒有 route-only domain 的 link,預設會被視為其中一個 default route。因此,查詢會同時送往 10.8.0.1 和無線 link 上的 router,由第一個成功回應者提供結果。router 只隔一個 hop,通常會先回應;無論結果如何,查詢都已經以明文送出。屬於無線 link 自有 search domain 的名稱,例如 lan,只會送往該 link,因為最具體的 routing domain 優先。
當 wg-quick 透過 systemd 自有的 resolvconf wrapper 套用 DNS = 時,它已經會要求 catch-all routing domain。因此,這個問題主要出現在其他 resolvconf implementation 或 network manager 套用設定時。明確設定可避免依賴推測。
在同一個步驟中設定 resolver,並宣告 catch-all routing domain。%i 會展開為介面名稱,因此此區塊可直接套用於任何介面。
[Interface]
Address = 10.8.0.2/32
PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.
PostDown = resolvectl revert %i以這種方式使用 PostUp 時,請刪除 DNS = 那一行,否則會有兩個機制寫入 resolver state,最後只會由其中一個機制負責清理。~. argument 是關鍵部分:它會使 wg0 成為所有名稱的 route-only domain,因此 systemd-resolved 不會再把不符合更具體 domain 的名稱送往其他 link。屬於其他 link 自有 domain 的名稱,例如 lan,仍會送往該 link;這通常正是本機主機所需的行為。請加以確認。
resolvectl status wg0正常輸出會包含 DNS Servers: 10.8.0.1 和 DNS Domain: ~.。如果缺少 ~.,表示 resolvectl domain 部分未執行,wg0 又會與其他 link 競爭。請勿使用 default route flag 作為檢查依據:即使沒有 ~.,它仍會顯示 yes(或在 Protocols 行顯示 +DefaultRoute,視 systemd 版本而定),因為沒有 route-only domain 的 link 本來就會被視為 default route。
還有一種情況值得特別說明。如果 /etc/resolv.conf 是一般檔案,而不是指向 /run/systemd/resolve/stub-resolv.conf 的 symlink,表示該檔案由其他元件管理,通常是 NetworkManager 或 container runtime。請先執行 ls -l /etc/resolv.conf,再進行其他除錯;因為會在每次網路變更時重新寫入該檔案的工具,可能在最不適當的時機撤銷你的設定。
升級:在隧道另一端自行架設過濾 DNS resolver
查詢能穩定經由隧道傳送後,遠端的 resolver 就會成為控制點。在該處執行 AdGuard Home,可讓所有已連線裝置使用封鎖清單過濾功能並查看查詢日誌,不需要安裝用戶端軟體,也不必逐台設定。官方安裝指令碼經確認截至 October 2026,只有一行。
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v首次執行時,設定精靈會監聽 port 3000。請透過隧道連線至 http://10.8.0.1:3000,不要將該 port 公開;接著在精靈中,將 DNS 監聽位址與管理介面監聽位址都設為 10.8.0.1。如果 failure one 的 unbound 仍使用相同位址,請先以 sudo systemctl disable --now unbound 停止它,因為兩者會在同一個位址上爭用 UDP port 53,導致 AdGuard Home 以 listen udp 10.8.0.1:53: bind: address already in use 結束。
如果用戶端設定已經指定 DNS = 10.8.0.1,就不需要修改。現在,查詢日誌會顯示所有 peer 傳送到此處的查詢,除非你將某個用戶端排除在日誌記錄之外。這是實際的隱私決策,而不是毫無代價的好處:你會把信任對象從網際網路服務供應商轉移到自己身上,也必須負責讓該主機維持修補更新。暴露於網際網路的伺服器必須先完成基本防護,而新 VPS 的前 10 分鐘涵蓋了這些事項。
FAQ
為什麼 WireGuard tunnel 已連線,但無法解析名稱?
tunnel 只傳送封包,完全不處理名稱。因此,tunnel 正常但查詢失敗,表示指定的 resolver 沒有回應。請在 client 上使用 dig +short @10.8.0.1 example.com 測試。收到 communication timed out 回覆表示 tunnel 位址上沒有 resolver 監聽,常見原因是 systemd-resolved stub 只繫結在 127.0.0.53,或 server firewall 丟棄從 wg0 進入的 UDP port 53。先修正 listener,再只對 wg0 開放該 port。
如何確認 DNS 是否透過 WireGuard 外洩?
在 client 上執行 sudo tcpdump -ni any -c 10 port 53,並在瀏覽時查看介面欄位。所有離開主機的封包都應位於 wg0;位於 lo、往返 127.0.0.53 的封包是本機 systemd-resolved stub,屬於正常現象。如果封包出現在 wireless 或 ethernet 介面上,表示查詢正以明文離開。dig +short whoami.akamai.net 可提供第二個確認結果,因為它會回覆提出查詢的 recursive resolver 之 public address。因此,若回覆不是 server 的 address,即可確認發生 DNS 外洩。
使用 split tunnel 時,是否仍需要 DNS = 行?
需要,而且 resolver address 也必須位於 AllowedIPs 內,否則 client 沒有通往該位址的路由。使用 AllowedIPs = 10.8.0.0/24 時,位於 10.8.0.1 的 resolver 會涵蓋在路由內,查詢也會加密。像 9.9.9.9 這類 public resolver 不在涵蓋範圍內,因此即使 DNS 行看似正確,查詢仍會經由本機連線離開。
為什麼 resolvectl 顯示正確的 server,但查詢仍然送往其他地方?
systemd-resolved 會同時將不符合任何 routing domain 的名稱送往所有 default-route link,包括 wg0,並採用第一個成功的回覆;該回覆通常來自本機 router。在 client 的 [Interface] 區塊加入 PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.,並移除 DNS = 行。接著,resolvectl status wg0 應回報 DNS Domain: ~.;任何沒有被其他 link 以更具體網域宣告的名稱,都只會送往 wg0。
多個 client 發生問題時,應先修正哪一個?
先修正一台 Linux client,因為只有它能清楚顯示運作機制。resolvectl status 和 tcpdump 會告訴你哪個 resolver 回覆,以及封包經由哪個介面傳送。手機與桌面應用程式會套用相同的 DNS 和 AllowedIPs 值,但不會顯示底層運作細節。因此,Linux client 正確後,你就是在複製一份已經驗證過的設定。