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 設定。啟動介面的 shell wrapper 會讀取這一行,也就是 wg-quick;接著 wg-quick 會在 tunnel 運作期間修改用戶端的 resolver 設定,並在 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 逾時,問題是轉送或 NAT(network address translation),不是 DNS 問題;無論如何修改 resolver 設定都無法解決。本文的所有範例都使用 10.8.0.0/24 作為 tunnel 子網路,並使用 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.1DNS 洩漏:分割通道未將解析器的流量經由通道轉送
這個問題更嚴重,因為所有功能看起來都正常。名稱能解析,網頁能載入,但 DNS 查詢會以明文穿過你原本不信任的區域網路。
有兩種設定會造成這個問題。第一種是用戶端設定了 AllowedIPs = 0.0.0.0/0, ::/0,但沒有 DNS = 行。wg-quick 會在自己的路由表中安裝預設路由,並透過 suppress_prefixlength 0 新增規則。這會刻意保留更具體的本機路由,讓電腦仍能連線到印表機。用戶端透過 DHCP 取得的解析器通常是 192.168.1.1 的路由器,會符合其中一條本機路由。你的網路流量會經由通道傳送,但區域網路仍會收到你查詢的完整網域名稱清單。
第二種是分割通道:設定 AllowedIPs = 10.8.0.0/24,並搭配 DNS = 9.9.9.9。由於 9.9.9.9 不在 AllowedIPs 內,用戶端沒有可經由通道連線到它的路由,因此查詢會像第一種情況一樣,經由本機連線送出。
確認實際回應查詢的是哪個解析器。whoami.akamai.net 是一個公開測試名稱,會以發出查詢的遞迴解析器 IP 位址回應,因此你可以將結果與伺服器的公開位址比較。
resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53resolvectl status 會針對每個連線列印一個區塊。如果乙太網路或無線連線的區塊仍顯示 Current DNS Server: 192.168.1.1,而 wg0 區塊沒有顯示任何內容,這就是洩漏。dig +short whoami.akamai.net 回傳家用寬頻位址,而不是伺服器位址,也能從遠端確認這個問題。tcpdump 行是可用來定案的證據:正常輸出會將所有 53 埠封包送到 wg0,發生洩漏時則會送到 wlan0 或 enp3s0。
修正方式分成兩部分,兩者都必須完成。將 DNS 設為通道內的位址,並確認該位址位於 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 內,因此查詢會加密並送到伺服器。如果你堅持在分割通道上使用公開解析器,請將它加入為主機路由:AllowedIPs = 10.8.0.0/24, 9.9.9.9/32。封包之後會經由通道傳送,但區域網路仍可能從先前的工作階段看出你選用了哪個供應商。自行執行解析器即可避免這個問題。
Resolver 的指派方式,是手動建置 WireGuard 與協調式 mesh 之間明顯的差異之一,也是 WireGuard 與 Tailscale 的比較中的取捨。執行 自架 Headscale 控制伺服器,即可取得這項協調能力,而不必將金鑰材料交給第三方。如果你擔心的是最後這一點,請注意,Tailscale 不會持有用於加密網路流量的金鑰;更值得追問的是,遭入侵的協調伺服器或遭竊的身分帳戶,可能對你的網路增加哪些風險。
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。請安裝可與 systemd-resolved 通訊的實作,然後再次啟用介面。
sudo apt install -y openresolv
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 查詢仍送往舊的 resolver。systemd-resolved 會為每個 link 維護獨立的 resolver 清單,並為每次查詢選擇一個 link。除非將某個 link 標記為名稱解析的預設路由,否則它會持續使用無線 link 的 resolver,因為該 link 具有 search domain,而你的 link 沒有。
在同一個步驟中設定 resolver,並指定預設路由。%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 狀態,但只有其中一個機制會在之後清理。~. 引數是關鍵部分:它會將 wg0 標記為所有名稱的 routing domain,因此 systemd-resolved 會將所有查詢送到該處,而不是每次查詢都選擇不同的 link。請確認設定結果。
resolvectl status wg0正常輸出會包含 DNS Servers: 10.8.0.1 和 Default Route: yes。如果 Default Route 顯示 no,表示 resolvectl domain 部分未執行,系統又回到 link selection。
還有一種情況值得特別說明。如果 /etc/resolv.conf 是實體檔案,而不是指向 /run/systemd/resolve/stub-resolv.conf 的 symbolic link,表示該檔案由其他元件管理,通常是 NetworkManager 或容器 runtime。請先執行 ls -l /etc/resolv.conf,再進行其他排查,因為每次網路變更都會改寫該檔案的工具,可能在最不適當的時機撤銷你的設定。
升級:在 tunnel 上執行自有的過濾 DNS resolver
當查詢能可靠地經由 tunnel 傳送後,遠端的 resolver 就會成為控制點。在該處執行 AdGuard Home,即可讓所有已連線的裝置使用 blocklist 過濾功能並記錄查詢日誌,不需要安裝 client software,也不需要逐台設定。經確認截至 July 2026,官方安裝 script 只需一行。
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v首次執行時,設定精靈會監聽 port 3000。請經由 tunnel 透過 http://10.8.0.1:3000 存取設定精靈,不要將該埠公開開放;並在精靈中將 DNS listen address 與 admin listen address 都設為 10.8.0.1。如果失敗一中的 unbound 仍使用相同的 address,請先以 sudo systemctl disable --now unbound 停止它,因為同一個 address 上的 UDP port 53 無法由兩個程序同時繫結,第二個程序會以 listen udp 10.8.0.1:53: bind: address already in use 結束。
如果 client 設定檔原本已指定 DNS = 10.8.0.1,就不需要變更。現在,query log 會顯示每個 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。先修正監聽設定,再只為 wg0 開放該 port。
如何確認 DNS 是否透過 WireGuard 外洩?
在 client 上執行 sudo tcpdump -ni any -c 10 port 53,並在瀏覽時查看介面欄位。所有封包都應該走 wg0。如果封包出現在 wireless 或 ethernet 介面,表示查詢正以明文離開。dig +short whoami.akamai.net 可提供第二個判斷依據,因為它會回報實際提出查詢的 recursive resolver 的公開位址。因此,若回應不是你 server 的位址,即可確認發生 DNS 外洩。
使用 split tunnel 時,是否仍需要 DNS = 行?
需要,而且 resolver 位址也必須位於 AllowedIPs 內,否則 client 沒有通往該位址的路由。使用 AllowedIPs = 10.8.0.0/24 時,10.8.0.1 的 resolver 位於涵蓋範圍內,查詢會經過加密。像 9.9.9.9 這類公開 resolver 不在涵蓋範圍內,因此即使 DNS 行看起來正確,查詢仍會透過本機連結離開。
為什麼 resolvectl 顯示正確的 server,但查詢仍然送往其他地方?
systemd-resolved 會為每個連結維護一份 resolver 清單,並為每次查詢選擇一個連結。因此,wg0 上的正確項目可能遭到忽略,因為另一個連結負責名稱解析的預設路由。將 PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~. 加入 client 的 [Interface] 區塊,並移除 DNS = 行。接著,resolvectl status wg0 應會回報 Default Route: yes。
多個 client 發生問題時,應先修正哪一個?
先修正一台 Linux client,因為只有它能顯示實際運作機制。resolvectl status 和 tcpdump 會告訴你哪個 resolver 回應,以及封包經由哪個介面傳送。手機與桌面應用程式會套用相同的 DNS 與 AllowedIPs 值,但不會顯示底層設定。因此,Linux client 正常後,你只需複製一份已驗證的設定。