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,筆記型電腦就會改將 LAN 流量送出本機 Wi-Fi;封包可能因此遺失,或抵達完全不同的 192.168.20.10。
從輸入方向解讀時,AllowedIPs 是存取控制清單。 WireGuard 解密來自 peer 的封包後,會依該 peer 的 AllowedIPs 檢查封包內部的來源位址。若位址不相符,WireGuard 就會捨棄封包。這項捨棄不會產生日誌行,也不會增加任何計數器。封包只會直接消失。
因此,兩個設定檔不會是彼此的鏡像。用戶端會列出希望透過伺服器連線的目標。伺服器則會列出允許該用戶端使用的來源位址。
成對的設定檔
用戶端,/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 介面上已存在的前綴新增路由,ip route add 會拒絕這項操作,導致介面無法啟動:
RTNETLINK answers: File exists使用 full tunnel 時,wg-quick 會安裝 policy routing 規則,刻意讓更具體的路由不採用 main table。本機的 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 也會設定相同值,但下次重新開機後就會失效,這正是「原本能運作,到了星期二卻失效」的典型情況。
封包轉送也必須通過防火牆。在啟用 ufw 的 Ubuntu 上,除非在 /etc/default/ufw 中設定 DEFAULT_FORWARD_POLICY="ACCEPT",否則轉送封包會遭到丟棄。Docker 也會自行設定相同的政策。因此,在從未手動設定防火牆的主機上,如果 sudo iptables -S FORWARD | head -1 輸出 -P FORWARD DROP,表示這是 Docker 設定的,而 tunnel 流量仍需要明確的允許規則。
為什麼回應封包始終回不來
只要正確設定規則 1 到 3,ping 確實會抵達 NAS。但你仍然看不到回應,因為回應封包找不到回程路徑。NAS 會回覆至 10.8.0.2。這個位址不在 NAS 自己的子網路內,因此會將封包交給預設閘道,也就是位於 192.168.20.1 的家用路由器。該路由器從未知道 10.8.0.0/24 的路由,因此會將回應轉送至自己的預設閘道,也就是你的網際網路連線,封包便會在那裡遭到丟棄。請求封包成功抵達,但回應封包被丟棄。
選項 A:在 WireGuard server 上設定 masquerade。 server 會將每個轉送封包的來源位址改寫為 192.168.20.5,也就是 server 自己的 LAN 位址。NAS 會將請求視為來自同一子網路中的鄰近主機,直接回覆給 server;server 再還原位址並將封包透過 tunnel 傳回。LAN 上的其他設定都不必變更。
將設定放在 server 的 [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 位址時,再改用靜態路由。
找出四項規則中哪一項錯誤
從用戶端開始向外檢查。每個步驟都能確認封包是否已到達該位置。
封包是否進入通道? 在用戶端執行:
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 使用的協定。若握手正常但此處沒有任何輸出,表示問題在規則 2:伺服器針對該 peer 的 AllowedIPs 未包含 10.8.0.2,因此封包在解密前就被丟棄,尚未到達 wg0。
封包是否離開伺服器並前往 LAN? 在伺服器上監看 LAN 端:
sudo tcpdump -ni enp1s0 icmp如果在 wg0 看得到請求,但此處沒有任何輸出,表示問題在規則 3:轉送功能未啟用,或 FORWARD 規則丟棄了封包。如果此處看得到來源為 10.8.0.2 的請求,但沒有回應,表示問題在規則 4:回應封包沒有返回路由。如果此處看得到來源為 192.168.20.5 的請求,但沒有回應,表示 masquerade 規則正常運作,目標主機本身拒絕了請求,請檢查 NAS 上的防火牆。將 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 knownTunnel 沒有問題。名稱解析是獨立的路徑,而筆電仍在查詢從本機 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在需要 openresolv 或等效設定的 Linux 用戶端上,否則 wg-quick 會以 resolvconf: command not found 停止。設定前,請先了解它在 systemd-resolved 系統上的行為:wg-quick 會以獨佔方式註冊這些伺服器,因此 tunnel 啟用期間,筆電上的所有查詢都會送往家用 resolver,而不只是家用名稱。使用 resolvectl status wg0 檢查結果。若要讓家用名稱在家中解析,其餘名稱則由本機解析,這就是 split DNS;修正 WireGuard tunnel 上的 DNS 會完整說明設定方式。
家中沒有公開 IP?在中間放置 VPS
如果路由器的狀態頁面顯示 WAN 位址位於 100.64.0.0/10 內,或是顯示私有的 192.168.x.x 位址,表示您位於 CGNAT(carrier grade network address translation)之後,網際網路上的握手封包無法抵達家中。對外發起的握手仍可正常運作,因此解法是加入一個具有公開位址的第三個節點。由小型 VPS 執行中樞,家中的主機主動連線到該 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,因為現在所有流量都會先前往中樞。 - 在家中主機上,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 涵蓋金鑰產生、防火牆與 systemd unit。若要了解如何連線到無法接受傳入連線的機器,請參閱 從 CGNAT 後方開啟反向 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 拒絕時失效。完整通道仍會建立,但 wg-quick 會安裝政策路由,讓主要路由表中較具體的路由失效。因此,區域網路會優先使用,遠端 LAN 仍然無法連線。請將家用 LAN 重新編號為較少見的網段,例如 192.168.20.0/24。用戶端無法解決這個問題。
我能透過 IP 連到 NAS,卻無法使用名稱連線。缺少什麼設定?
名稱解析不會自動透過通道傳送。將 DNS = 192.168.20.1(家用解析器)加入用戶端的 [Interface] 區塊,並確認該位址位於對等端的 AllowedIPs 內,否則查詢不會進入通道。接著確認解析器會回應來自自身子網路外的查詢,因為 dnsmasq 搭配 local-service,以及 Pi-hole 的僅本機監聽模式,都會拒絕這類查詢。WireGuard 伺服器上的 masquerade 規則可以改寫查詢的來源位址,藉此避開這個問題。
我的家用連線沒有公開 IP,仍然可以連到 LAN 嗎?
可以,但需要第三個節點。在 CGNAT 後方時,路由器的 WAN 位址是私有位址,因此網際網路上的對等端無法主動與它建立握手;但由內部發起的握手通常不受影響。在具有公開位址的 VPS 上執行 WireGuard,讓家用主機透過 PersistentKeepalive = 25 主動連線到 VPS,並在 VPS 上為家用主機設定對等端項目的 AllowedIPs,其中包含其通道位址與 192.168.20.0/24。接著在 VPS 上啟用轉送,並加入允許 wg0 轉送至 wg0 的規則。