WireGuard 路由至家用 LAN 的設定方法
WireGuard handshake 正常,卻無法連線至 192.168.20.10?依序檢查 AllowedIPs、ip_forward、回程路由與 masquerade,4 項設定缺一不可。
為什麼家用 LAN 無法透過 WireGuard 回應
若要透過 WireGuard 路由至家用 LAN,必須正確設定 4 個彼此獨立的項目。即使其中 4 項只有 3 項正確,仍會得到看似完全正常的通道,因此這類故障特別難以判斷。wg show 顯示最近有 handshake,ping 10.8.0.1 在幾毫秒內回應,但 ping 192.168.20.10 完全沒有任何回應。
以下是封包依序經過的完整清單。LAN 代表 local area network,也就是家用路由器後方的私有網路。
- 用戶端的
AllowedIPs必須涵蓋遠端子網路,否則封包不會進入通道。 - 伺服器的
AllowedIPs必須涵蓋用戶端的通道位址,否則封包解密後會立即遭到丟棄。 - 伺服器上的
net.ipv4.ip_forward必須設為1,因為 Linux 會丟棄任何不是傳送至本機的封包。 - LAN 必須知道如何經由伺服器上的 masquerade 規則或家用路由器上的靜態路由,將封包送回
10.8.0.0/24。
上述每一項設定發生錯誤時,都不會顯示錯誤訊息。系統不會記錄任何內容,也不會發出警告,而 handshake 在整個過程中仍會持續正常運作。請依序檢查這些項目,通常約 1 分鐘內就能找出故障設定。
本指南使用的網路
以下所有位址都是範例。請替換成您自己的位址,並保持替換後的一致性,因為設定只更新一半是這個問題最常見的原因之一。
- 家用 LAN 是
192.168.20.0/24。家用路由器是192.168.20.1。 - WireGuard 伺服器是家用 LAN 上的一台 Linux 主機。其 LAN 介面
enp1s0擁有192.168.20.5,通道介面wg0擁有10.8.0.1。 - 您要連線的主機是位於
192.168.20.10的 NAS(網路附加儲存裝置)。 - 用戶端是位於其他地方的筆電,在通道內使用
10.8.0.2。
伺服器是 LAN 上的主機,不是路由器本身。這是一般情況,例如 Raspberry Pi 或舊的迷你電腦。這對規則 4 很重要:除非您告知路由器,否則路由器不知道通道的存在;LAN 上的每台主機也都會將子網路外的流量傳送至該路由器。
如果您的家用網路沒有公開 IP 位址,這些設定無法單獨運作,因為網際網路上的任何主機都無法向您的家中建立 handshake。將 VPS 放在中間的章節會說明這種情況。相同的 4 條規則仍然適用,但需要另外正確處理一個 peer。
AllowedIPs 代表兩種不同的意義
同一項設定會執行兩項工作,而在兩端以相同方式解讀,正是這類問題大多數工單的根本原因。WireGuard 將此機制稱為 cryptokey routing,詳細說明請參閱 WireGuard 如何將公開金鑰繫結至 IP 範圍。
從對外傳送的方向解讀,AllowedIPs 是路由表。 wg-quick 會將每個項目轉換為指向 wg0 的路由。只有在某個 peer 宣告包含該位址的範圍時,前往 192.168.20.10 的封包才會加密並傳送給該 peer。若只列出 10.8.0.0/24,筆電會改從本機 Wi-Fi 傳送 LAN 流量,結果可能封包遺失,或抵達完全不同的 192.168.20.10。
從對內接收的方向解讀,AllowedIPs 是存取控制清單。 WireGuard 從 peer 解密封包後,會將內部來源位址與該 peer 的 AllowedIPs 比對;若不相符,就會丟棄封包。除非啟用核心除錯訊息,否則不會記錄任何內容。唯一的線索是 wg0 上的接收錯誤計數,ip -s link show wg0 會顯示此計數,而封包就會直接消失。
因此,兩個設定檔不會是彼此的鏡像。用戶端列出希望透過伺服器到達的目的地。伺服器則列出允許該用戶端作為來源位址使用的範圍。
成對的設定檔
用戶端,/etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
[Peer]
PublicKey = <server public key>
Endpoint = home.example.com:51820
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24
PersistentKeepalive = 25伺服器,/etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <server private key>
Address = 10.8.0.1/24
ListenPort = 51820
[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.8.0.2/32家用路由器也需要設定連接埠轉送,將 UDP 51820 轉送至 192.168.20.5;否則交握程序不會開始,而且用戶端日誌會記錄 Handshake for peer 1 did not complete after 5 seconds, retrying。本指南假設你已經完成這項設定。
這與一般的完整通道設定有 4 行不同,而且每一行都有特定用途。
- 用戶端使用
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24,取代0.0.0.0/0, ::/0。這是分割通道:通道子網路與家用 LAN 會經由wg0傳送,其他流量則維持本機路由。網頁瀏覽不會經過家中的連線;如果你只需要存取 NAS,通常這正是你要的設定。 - 伺服器使用
AllowedIPs = 10.8.0.2/32,指定單一位址而不是一段範圍。在該處填入10.8.0.0/24,該單一用戶端即可使用通道中的任一位址。之後若加入具有重疊範圍的第二個 peer,流量會移至最後設定的 peer,而且任何地方都不會顯示錯誤。 - 只在用戶端使用
PersistentKeepalive = 25。用戶端位於 NAT(網路位址轉譯)後方,其路由器在沉默 1 或 2 分鐘後會忘記 UDP 對應,因此伺服器將無法再連入用戶端。伺服器具有公開位址,不需要 keepalive。 - 目前尚未加入
DNS =行。加入這一行會變更整台用戶端機器的名稱解析。下方的 DNS 區段會先說明其作用,再決定是否啟用。
完整通道也能連到 LAN,因為 0.0.0.0/0 會符合所有位址。但這會讓所有流量都經過通道,並造成無法從用戶端修正的子網路衝突。
為什麼兩端使用相同子網路會導致連線失效
選擇幾乎沒有人使用的家用子網路,例如 192.168.20.0/24 或 10.44.7.0/24。192.168.1.0/24 和 192.168.0.0/24 是大多數消費型路由器的出廠預設值,因此你的筆記型電腦遲早會連上咖啡廳或飯店網路,而該網路正好使用相同的位址範圍。
位址衝突會直接導致連線失效,而且在兩種情況下的表現不同。使用 split tunnel 時,wg-quick 會嘗試為已存在於 Wi-Fi 介面的前綴新增路由。如果現有路由的 metric 相同,ip route add 會拒絕這項操作,導致介面無法啟動:
RTNETLINK answers: File exists在由 NetworkManager 管理的筆記型電腦上,Wi-Fi 路由通常會使用例如 600 的 metric,因此新增路由會成功,且 metric 為 0 的 tunnel 路由會優先使用。NAS 可以回應,但咖啡廳網路中位於該範圍內的所有主機,通常也包括其 DNS 伺服器,現在都會經由 tunnel 傳送。
使用 full tunnel 時,wg-quick 會安裝 policy routing 規則,刻意讓更具體的路由避開主路由表。區域的 192.168.1.0/24 路由會優先於 tunnel,因此所有前往遠端 LAN 的封包都會經由本機連線傳送。tunnel 已啟動,handshake 也正常,但仍無法連線到 NAS。真正的修正方式只有重新編號家用 LAN。
將伺服器變成路由器
ip route show default
printf 'net.ipv4.ip_forward = 1\n' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward第一個指令會顯示 LAN 介面的實際名稱。目前的映像檔通常使用 enp1s0 或 ens3 這類名稱,很少使用 eth0。若 masquerade 規則指定錯誤的介面,就不會比對到任何封包。最後一個指令應輸出 net.ipv4.ip_forward = 1。單獨執行 sudo sysctl -w 也會設定相同值,但下次重新開機後就會失效,這正是典型的「直到星期二前都正常」問題。
轉送功能也必須通過防火牆。若 Ubuntu 啟用了 ufw,除非在 /etc/default/ufw 中設定 DEFAULT_FORWARD_POLICY="ACCEPT",否則轉送封包會遭到丟棄。Docker 使用預設的 iptables backend;當 Docker 在啟動時必須開啟轉送功能,會自行設定相同的政策,除非在 /etc/docker/daemon.json 中設定 ip-forward-no-drop。因此,在從未手動設定防火牆的伺服器上,若 sudo iptables -S FORWARD | head -1 輸出 -P FORWARD DROP,通常表示這是 Docker 設定的;但 provisioning script 或其他防火牆管理工具也可能進行相同設定。無論原因為何,隧道流量都需要明確的允許規則。
為什麼回覆封包永遠回不來
正確設定規則 1 到 3 後,ping 確實會抵達 NAS。但你仍然看不到回覆,因為回覆沒有回程路徑。NAS 會回覆到 10.8.0.2;這個位址不在 NAS 自己的子網路內,因此會將封包交給預設閘道,也就是家用路由器 192.168.20.1。該路由器從未得知 10.8.0.0/24 的路由,因此會將回覆轉送給自己的預設閘道,也就是網際網路連線,封包最後會在那裡遭到丟棄。請求封包抵達了,但回覆封包被丟棄。
選項 A:在 WireGuard 伺服器上進行 masquerade。 伺服器會將所有轉送出 enp1s0 的 tunnel 封包來源位址改寫為 192.168.20.5,也就是該介面自己的 LAN 位址。NAS 會將請求視為來自同一子網路中的鄰近主機,直接回覆伺服器;伺服器再還原位址並將封包送回 tunnel。LAN 上的其他設定都不需要變更。
將規則放在伺服器的 [Interface] 區塊中,讓它隨介面一同建立和移除:
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o enp1s0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o enp1s0 -j MASQUERADE如果主機已由 nftables 管理,請改寫入 /etc/nftables.conf:
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" counter masquerade
}
}保留 counter 關鍵字。若缺少此關鍵字,sudo nft list ruleset 會列出規則,但不會顯示封包計數;該計數正是判斷規則是否實際套用的依據。
Masquerade 還有一項容易忽略的優點。許多主機執行的防火牆只接受來自自身子網路的連線。Windows 檔案分享預設就是如此,數種 NAS 管理面板也採用相同方式。在這類主機上,即使路由完全正確,來自 10.8.0.2 的封包仍會遭到丟棄。套用 masquerade 後,來源位址會變成 LAN 位址,因此這些規則便能相符。代價是所有 tunnel client 在每台 LAN 主機的日誌中都會顯示為 192.168.20.5,因此無法區分不同 client,LAN 裝置上的每個 client 規則也無法運作。
選項 B:在家用路由器上設定靜態路由。 告訴路由器 10.8.0.0/24 位於 192.168.20.5 後方。在 Linux 路由器上只需執行一個命令:
sudo ip route add 10.8.0.0/24 via 192.168.20.5消費級路由器通常會在進階設定中提供名為 Static Routes 或 Routing 的頁面:目的地 10.8.0.0、遮罩 255.255.255.0、閘道 192.168.20.5。請將設定儲存至路由器的持久設定中,因為在 Linux 主機上輸入的 ip route add 會在下次重新開機時消失。
這種方式會保留實際的 client 位址,因此 LAN 上的日誌和每個 client 的規則仍具備明確意義。它需要路由器支援靜態路由,而且只對將該路由器設為預設閘道的主機有效。任何使用子網路範圍防火牆的主機,仍須為 10.8.0.0/24 個別建立規則。請先使用 masquerade,因為它不需要變更目前由你控制的主機以外的設定;當你需要保留實際 client 位址時,再改用靜態路由。
如何找出四項設定中哪一項有問題
從用戶端開始逐層向外檢查。每個步驟都能確認封包是否已到達該處。
封包是否進入 tunnel? 在用戶端執行:
ip route get 192.168.20.10輸出應該會列出 dev wg0。如果列出的是 Wi-Fi 介面,表示規則 1 有誤:用戶端的 AllowedIPs 未涵蓋 LAN 子網路;或者相同網段的本機路由優先採用,也就是前文所述的子網路衝突。ping: connect: Network is unreachable 也會指向同一行設定。
封包是否抵達伺服器? 在伺服器執行以下指令,然後從用戶端對 NAS 執行 ping:
sudo tcpdump -ni wg0 icmp正常的路徑會顯示 IP 10.8.0.2 > 192.168.20.10: ICMP echo request。其中 ICMP 是 internet control message protocol,也是 ping 使用的協定。如果 handshake 正常但這裡沒有任何輸出,幾乎都表示規則 2 有問題:該 peer 的伺服器端 AllowedIPs 未包含 10.8.0.2,因此封包在解密前就遭到丟棄,從未抵達 wg0。如果伺服器上的 ip -s link show wg0 RX 錯誤計數隨每次 ping 增加,即可確認這一點。若計數沒有變化,請檢查用戶端是否有防火牆阻擋前往 tunnel 的輸出流量。
封包是否離開伺服器並前往 LAN? 在伺服器上監看 LAN 端:
sudo tcpdump -ni enp1s0 icmp如果在 wg0 看得到請求,但這裡沒有任何輸出,表示規則 3 有問題,可能是轉送功能未啟用,或 FORWARD 規則丟棄了封包。較少見的情況是嚴格反向路徑篩選(rp_filter)捨棄了封包;當伺服器返回來源位址的路由指向不同介面時,就會發生這種情況。如果這裡看得到來源為 10.8.0.2 的請求,但沒有回覆,通常表示規則 4 有問題,回覆封包沒有返回路由。不過,忽略 ping 的主機,或只回應自己子網路內請求的主機,也會呈現完全相同的結果。如果這裡看得到來源為 192.168.20.5 的請求,但沒有回覆,表示 masquerade 規則正常運作,而目標主機本身沒有回應。請檢查 NAS 上的防火牆;如果是 Linux 主機,也請確認 net.ipv4.icmp_echo_ignore_all 是否設定為忽略 ping。只要將 icmp 替換成 port 445 或所需檢查的其他連接埠,同一套檢查流程也適用於其他服務。
我能連到 IP,但無法解析名稱
ssh 192.168.20.10 可運作,但 ssh nas.home.arpa 失敗:
ssh: Could not resolve hostname nas.home.arpa: Name or service not knownVPN tunnel 沒有問題。名稱解析使用的是另一條路徑,而你的筆記型電腦仍在查詢從本機 Wi-Fi 取得的 resolver。該 resolver 不知道你家中網路上的名稱。
家中名稱要能正常運作,必須同時滿足兩個條件。resolver 的位址必須位於用戶端的 AllowedIPs 之內,否則 DNS(domain name system)查詢不會進入 tunnel。此外,resolver 必須願意回應來源位址為 10.8.0.2 的查詢。許多家用 resolver 預設會拒絕這類查詢:使用 local-service 執行的 dnsmasq 只會回應直接連接子網路送出的查詢,而 Pi-hole 隨附的 listening mode 也只允許本機請求。masquerade 規則會隱藏這個問題,因為查詢經過改寫後,會以 192.168.20.5 作為來源送達。
用戶端只需設定一行:
DNS = 192.168.20.1在 Linux 用戶端上,必須加入 openresolv 或等效設定,否則 wg-quick 會因 resolvconf: command not found 而停止。設定前,先確認 systemd-resolved 主機上的實際行為:wg-quick 會獨佔註冊這些伺服器,因此 tunnel 啟用期間,筆記型電腦上的查詢會送往家中的 resolver,而不只是家中的名稱。只有其他介面宣告了更具體網域的名稱,才會繼續送往其他位置。使用 resolvectl status wg0 檢查結果。如果你希望家中名稱由家中 resolver 解析,其他名稱則由本機解析,這就是 split DNS;修正 WireGuard tunnel 上的 DNS 會完整說明設定方式。
家中沒有公開 IP?在中間放置 VPS
如果路由器的狀態頁面顯示 WAN 位址位於 100.64.0.0/10 內,表示你位於 CGNAT(carrier grade network address translation)後方,來自網際網路的 handshake 無法抵達家中。若該處顯示私有的 192.168.x.x 位址,通常表示你的路由器前方還有第二台路由器,也就是 double NAT:將 ISP 提供的 modem router 設為 bridge mode,或在該設備上也轉送 UDP 51820。若你無法變更該設備,或它自己的 WAN 位址也不是公開位址,請將此情況視為 CGNAT。對外發出的 handshake 仍可正常運作,因此解決方式是加入一個具備公開位址的第三個節點。由小型 VPS 執行 hub,家中的主機主動連線至該 VPS。
四項規則不變。現在這些規則會跨越兩個 hop,因此需要記錄的設定加倍。
- 在 VPS 上,家中主機的 peer 項目取得
AllowedIPs = 10.8.0.3/32, 192.168.20.0/24:包括它自己的 tunnel 位址,以及允許它代表其通訊的子網路。 - 在 VPS 上,laptop 的 peer 項目維持
AllowedIPs = 10.8.0.2/32。 - 在 laptop 上,VPS peer 取得
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24,因為所有流量現在都會前往 hub。 - 在家中主機上,VPS peer 取得
AllowedIPs = 10.8.0.0/24,而家中主機承載PersistentKeepalive = 25,因為它現在是位於 NAT 後方的一側。 - VPS 也需要
net.ipv4.ip_forward = 1,其 forward chain 必須允許wg0到wg0,因為 laptop 的流量會在同一個介面上進出。針對一般 full-tunnel VPS 撰寫的防火牆規則,正好會封鎖這種流量。
如果你尚未建立 VPS 端,在 VPS 上設定 WireGuard 說明 key generation、防火牆與 systemd unit。若要了解如何連線至無法接受傳入連線的主機,請參閱 從 CGNAT 後方建立 reverse tunnel。當手動管理 peer 位址不再方便時,執行 Tailscale subnet router 可透過自動化位址管理,執行相同的路由工作。
讓設定在重新開機後持續生效
sudo systemctl enable --now wg-quick@wg0
sudo systemctl enable --now nftables
sudo wg showenable --now 是許多人會略過的步驟。手動執行的 wg-quick up wg0 在下次 kernel 升級並重新開機後就會消失。wg show 應列出該 peer,並顯示最近的 latest handshake 資訊,以及雙向皆為非零的傳輸計數器。
之後新增第二個 client 不需要重新啟動,因為重新啟動會中斷目前所有連線:
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip 會輸出不含只有 wg-quick 能理解之金鑰的設定,而 syncconf 會在不中斷即時工作階段的情況下套用差異。它只會更新 peer。變更 Address 或新增 PostUp 行,仍需要完整執行 down 和 up。
FAQ
為什麼我可以 ping WireGuard 伺服器,卻無法連到家用 LAN 上的其他裝置?
ping 10.8.0.1 只能證明通道已建立。LAN 的其餘部分是路由問題。用戶端的 AllowedIPs 必須包含 192.168.20.0/24,否則封包不會進入通道。伺服器必須將 net.ipv4.ip_forward 設為 1,否則會丟棄所有不是傳送給自己的封包。此外,LAN 必須有返回 10.8.0.0/24 的路由。在伺服器上執行 sudo tcpdump -ni enp1s0 icmp,同時從用戶端進行 ping:若看到來源為 10.8.0.2 的請求送出,但沒有回應返回,通常表示缺少返回路徑。
我需要在家用路由器上設定靜態路由嗎?
只有在不使用 masquerade 規則時才需要。WireGuard 伺服器上的 masquerade 規則會將通道路由流量的來源位址改寫為伺服器自己的 LAN 位址,因此 LAN 主機會回覆自己已知且可達的鄰近裝置,路由器也不會介入。另一個選擇是設定一條經由伺服器 LAN 位址通往 10.8.0.0/24 的靜態路由。若你希望 LAN 主機的日誌保留實際用戶端位址,或希望在 LAN 主機上套用個別用戶端的防火牆規則,這樣做就值得。
為什麼兩端使用相同子網路會使通道中斷?
你的筆電無法同時使用兩條通往同一個前綴的路由。如果區域網路配發 192.168.1.0/24,而家用 LAN 也是 192.168.1.0/24,分割通道的 wg-quick up 會在 ip route add 以 RTNETLINK answers: File exists 拒絕時失敗;或者在 Wi-Fi 路由具有較高 metric 的情況下,通道雖然建立,卻會將該範圍內的本地主機隱藏在通道後方。完整通道則可以建立,但 wg-quick 會安裝 policy rules,使主路由表中較具體的路由失效,因此本地網路會優先使用,而遠端 LAN 仍然無法連線。請將家用 LAN 重新編號為較少見的網段,例如 192.168.20.0/24。用戶端沒有可行的修正方式。
我可以透過 IP 位址連到 NAS,卻無法使用名稱連線。缺少什麼設定?
名稱解析不會自行遵循通道。將 DNS = 192.168.20.1(家用 resolver)加入用戶端的 [Interface] 區塊,並確認該位址位於 peer 的 AllowedIPs 內,否則查詢不會進入通道。接著確認 resolver 會回應來自自身子網路以外的查詢,因為 dnsmasq 搭配 local-service,以及 Pi-hole 的僅限本機監聽模式,都會拒絕這類查詢。WireGuard 伺服器上的 masquerade 規則可透過改寫查詢的來源位址來避開這個問題。
我的家用連線沒有公開 IP。仍然可以連到 LAN 嗎?
可以,但需要第三個節點。位於 CGNAT 後方時,路由器的 WAN 位址不是公開位址,因此網際網路上的 peer 無法主動與它建立 handshake,但由內部發起的 handshake 通常不受影響。在具有公開位址的 VPS 上執行 WireGuard,讓家用主機透過 PersistentKeepalive = 25 主動連線到 VPS,並在 VPS 上家用主機的 peer 項目中設定一個 AllowedIPs,其中包含該主機的通道位址及 192.168.20.0/24。接著,VPS 必須啟用 forwarding,並加入允許 wg0 通往 wg0 的 forward 規則。