如何在 VPS 上架設 WireGuard VPN:完整教學
本指南詳細介紹在 Linux VPS 上架設 WireGuard 的步驟,包含金鑰產生、wg0.conf 設定、IP forwarding 與 NAT 規則。針對 AllowedIPs 設定錯誤導致的流量遺失、DNS 設定以及 KVM 與 OpenVZ 環境下的模組差異提供解決方案。
建立目標
在自有伺服器上建立 WireGuard VPN 僅需約 40 行設定:包含一組金鑰對、一個介面檔案、一個 sysctl 設定、一條 NAT 規則與一個防火牆規則。由於安裝過程非常簡單,本指南將重點說明常見錯誤,包含金鑰權限、AllowedIPs、轉發 (forwarding) 與 DNS。
WireGuard 是核心層級的 Layer 3 隧道,自 Linux 5.6 起已納入主線版本,因此 Ubuntu 24.04 與 Debian 13 無需安裝外部模組即可使用。其不包含加密協商、憑證授權單位 (CA) 或帳號密碼驗證:Peer 的定義即為該金鑰對應的公鑰與可用 IP 位址。若封包未通過 MAC 檢查,系統會直接丟棄且不回傳任何訊息,因此該連接埠不會回應掃描。其缺點在於不存在驗證伺服器,因此若要取消存取權限,必須直接從主機刪除該 Peer。
首先檢查虛擬化技術
WireGuard 需要可載入模組的 kernel;在 KVM VPS 上可直接使用。若使用共用 host kernel 的 container virtualisation(如 OpenVZ、LXC),第一個指令會因 RTNETLINK answers: Operation not supported 而失敗,此時會改用 wireguard-go userspace 實作版本。請先使用 sudo modprobe wireguard && echo ok 進行檢查。
在不洩漏金鑰的情況下產生金鑰
若 /etc/wireguard/server.key 可被所有使用者讀取,等同於完全沒有使用 VPN。常見的 umask 077 && wg genkey | sudo tee ... 做法並不可靠,因為 sudo 會對 tee 建立的檔案套用其自身的 umask。請明確設定權限模式。
sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.key以相同方式產生 client 配對。wg genpsk 可加入選用的 pre-shared key,每個 config 檔案中各佔一行。
伺服器介面:/etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>
[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32chmod 600 it; 若出現檔案可被所有人存取的啟動警告,代表您跳過了該步驟。Address 是隧道內的伺服器位址,包含整個 VPN 子網路的遮罩。請選擇一個不會與外部網路衝突的範圍 —— 192.168.1.0/24 會與半數用戶端所在的家用路由器衝突,導致隧道路由被本地路由靜默覆蓋。
Peer 在 server 端設定的 AllowedIPs 是 /32,即該用戶端擁有的唯一隧道位址。若為兩個 Peer 設定相同的 allowed IP,系統會將其移至最後設定的項目,且第一個 Peer 將無法接收流量,且不會顯示任何錯誤訊息。請保持 SaveConfig 不設定,否則 wg-quick down 會將目前的執行狀態重寫回此檔案。
將主機轉換為路由器
Linux 伺服器預設會丟棄非目標地址的封包。預設情況下,Forwarding 與 source NAT 皆未啟用。
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
| sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward若僅進行 bare sysctl -w,設定會在下次重新開機後失效。NAT 需要指定 egress 介面,即連接至 Internet 的 NIC,而非 wg0。請勿假設介面名稱為 eth0;請從 ip route show default 取得正確名稱,因為目前的映像檔使用如 enp1s0 或 ens3 之名稱。
Firewall:連接埠與轉發路徑
單一 nftables 檔案同時包含 filter 與 NAT。執行 /etc/nftables.conf 會清除現有的規則集,若系統已由 ufw 或 Docker 管理,請跳過此步驟。
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
tcp dport 22 accept
udp dport 51820 accept
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "wg0" oifname "enp1s0" accept
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
}
}使用 sudo systemctl enable --now nftables 套用設定,並保持另一個 SSH 連線開啟:若 policy drop 中的 SSH 規則出現拼寫錯誤,將導致無法連入伺服器。請注意 forward chain 不允許的操作 —— wg0 至 wg0。Peer 僅能連向網際網路,無法互相連線;若需建立 peer-to-peer VPN,請加入 iifname "wg0" oifname "wg0" accept。相同的 chain 也決定了 peer 對伺服器本身的存取權限,當該機器同時作為 執行 tmux 中 Claude Code 的遠端開發機 使用時,這點至關重要,因為您不希望將該端公開。
在 ufw 環境下:請執行 ufw allow 51820/udp,於 /etc/default/ufw 中設定 DEFAULT_FORWARD_POLICY="ACCEPT",並在 /etc/ufw/before.rules 的最上方加入 *nat POSTROUTING MASQUERADE 規則。
使用 systemd 啟動
sudo systemctl enable --now wg-quick@wg0
sudo wg showwg-quick 會建立介面、新增位址,並根據 AllowedIPs 安裝路由。enable --now 是關鍵部分:手動執行的 wg-quick up wg0 在下次重新啟動後會消失,且核心升級也需要重新啟動。
客戶端配置,以及大家常搞錯的設定
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25AllowedIPs 同時承擔兩個不同的功能,將兩者混淆是導致 WireGuard 使用困惑的主要原因。
對外(Outbound)它是路由表。 若封包的目的地符合 peer 的 AllowedIPs,該封包會被加密並傳送至該 peer。0.0.0.0/0, ::/0 會將所有流量導向隧道——即全隧道(full tunnel),將伺服器設為預設路由。分割隧道(split tunnel)則是較精簡的清單:AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 僅承載 VPN 流量與伺服器後端的單一私有網路,其餘流量則維持原有的本地路由。透過這份精簡清單,您可以將服務完全排除在公網之外——例如綁定於隧道位址的 VPS 上的私有 Nextcloud 實例,或是執行於同一台機器上的 嵌套虛擬化實驗室 VM,都能讓 peer 可存取,且對其他人隱身。
對內(Inbound)它是存取控制清單(ACL)。 若來自 peer 的解密封包,其來源位址不在該 peer 的 AllowedIPs 清單中,該封包會被捨棄。這就是為什麼伺服器端要為筆記型電腦列出 10.8.0.2/32:若在此處填入 0.0.0.0/0,該用戶端將能偽造隧道內的任何位址。
PersistentKeepalive 用於位於 NAT 後方的用戶端,因為路由器僅在有封包傳輸時才會保持 UDP 映射開啟。一旦逾時,伺服器將無法主動連線至用戶端。PersistentKeepalive = 25 可保持映射開啟——請在用戶端進行設定,而非具備公用 IP 的伺服器。
DNS,以及沒人察覺的洩漏
若使用 AllowedIPs = 0.0.0.0/0 且未設定 DNS =,用戶端會沿用從區域網路取得的解析器——即位於 192.168.1.1 的咖啡廳路由器。由於該路由比預設路由更具體,DNS 查詢會透過區域鏈路以明文形式傳送,而其他所有流量皆經過隧道傳輸。流量本身是私密的,但域名清單卻不是。
有兩種正確的解決方案。將 DNS 指向公共解析器 (DNS = 9.9.9.9),查詢將經由隧道傳送並從您的伺服器送出,但該解析器仍會看到這些查詢。或者,將 unbound 或 dnsmasq 綁定至 10.8.0.1,設定 DNS = 10.8.0.1,並在 input chain 中加入 udp dport 53 iifname "wg0" accept —— 設定此行後即可忽略解析器,且不會發生任何解析問題。
在 Linux 用戶端,wg-quick 會透過 resolvconf 套用 DNS;若缺少此設定,則會得到 resolvconf: command not found。請安裝 openresolv,或在 systemd-resolved 用戶端上設定 PostUp = resolvectl dns %i 10.8.0.1。
在不中斷 tunnel 的情況下新增或移除 peers
重啟 interface 會導致所有已連線的使用者斷線。請將 [Peer] 區塊附加至 wg0.conf,接著直接重新載入 peer set。
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip 會印出不含 wg-quick-only 鍵值 (Address, DNS, PostUp) 的設定,而 syncconf 則能在維持現有連線的情況下套用差異。此操作僅更新 peers:若修改了 Address,仍須執行完整的 down/up。使用 sudo wg set wg0 peer <public key> remove 進行撤銷,接著從檔案中刪除該區塊,否則下次重新載入時該設定會再次出現。
Failure modes, with the strings you will see
Handshake never completes. wg show lists the peer with no latest handshake, and the client logs:
Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)Nothing is arriving, or nothing is accepted. In order: is UDP 51820 open on the VPS firewall and on your provider's network firewall, a separate control on most panels; is the Endpoint address and port right; are the keys crossed. The key in the client's [Peer] block must be the server's public key, and vice versa — pasting a private key, or a client's own public key, gives exactly this symptom. sudo tcpdump -ni any udp port 51820 on the server shows whether packets arrive at all. The kernel module logs nothing by default; WireGuard messages appear in dmesg only after you enable dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control), and with that on, a key mismatch shows up as an invalid-MAC drop.
Handshake works, no internet. ping 10.8.0.1 succeeds but ping 1.1.1.1 times out: forwarding or NAT is missing. Check sysctl net.ipv4.ip_forward reads 1, then watch the counters while the client pings, with sudo nft list ruleset or sudo iptables -t nat -L POSTROUTING -n -v. Zero packets on the masquerade rule means its egress interface name is wrong; a rising counter with no replies points at the forward chain policy.
Internet works, names do not. ping 1.1.1.1 succeeds and curl https://example.com returns Could not resolve host. The DNS line is missing, or it names a resolver unreachable from inside the tunnel.
Some HTTPS sites hang. SSH and ping are fine; large pages stall. That is path MTU: the tunnel adds overhead, and some link in the middle drops the oversized packets without an ICMP message getting back. Lower MTU on the client [Interface] — try 1420, then 1380, then 1280.
Interface refuses to start. Address already in use means another process holds UDP 51820. Cannot find device wg0 after a failed up usually means the config was rejected; read journalctl -u wg-quick@wg0 -n 50.
從 Streisand 或 OpenVPN 遷移
Streisand 已停止維護且其 repository 已封存。在已廢棄的自動化工具上執行 VPN 會導致長期的安全性問題。此過程無法進行就地升級 (in-place upgrade),且 OpenVPN 的 PKI 無法轉換:WireGuard 不使用憑證、CA 或過期機制,因此每個 client 都會取得全新的 key pair。
請採用並行遷移方案 —— 在同一台機器上同時執行 UDP 51820 的 WireGuard 與 1194 的 OpenVPN。部署 wg0,逐一遷移 client,最後再停止舊有的 service。OpenVPN 的 username/password 與撤銷 (revocation) 模型無法直接轉換;若需要帳號管理或稽核軌跡 (audit trail),請在 WireGuard 之上建立對應層級。
Backups, upgrades, and what strains at scale
/etc/wireguard 是該伺服器。請備份伺服器(使用 sudo tar czf wg-backup.tgz -C /etc wireguard,權限設為 600,並儲存於外部裝置),如此便能在幾分鐘內於新的 VPS 上重建。若遺失伺服器的 private key,由於客戶端會鎖定 (pin) 伺服器的 public key,因此必須重新核發所有客戶端的 config。升級作業僅需執行一般的 apt upgrade,若有 kernel 更新則需重新啟動;若已啟用 wg-quick@wg0,則系統會自動恢復。
由於每個 peer 的狀態量極小,且加密運算是在 kernel 中執行,因此效能瓶頸在於 VPS 的 CPU 與頻寬限制,而非此 config 中的設定。請使用 iperf3 測試 tunnel 的實際效能,而非依賴官方公布的數據。在大規模運作時,真正的挑戰在於維運。每個 peer 都需要唯一的 tunnel IP,若手動編輯 60 個 [Peer] 區塊,容易導致重複的 AllowedIPs 產生:請使用 script 來產生 config。單一伺服器即是單一 UDP endpoint 與單一故障點 (point of failure),且 WireGuard 不支援叢集化 (clustering):若要實現冗餘 (redundancy),必須部署另一台擁有獨立 keys 的伺服器。Key rotation 仍需手動執行,因此請記錄誰持有哪些 key,以及如何撤銷 (revoke) 特定 key。
執行上述所有作業需要一台您可完全控制的 Linux 主機:必須具備 public IP、可載入 module 的 kernel,以及您擁有完全控制權的 firewall。
FAQ
為什麼 WireGuard 握手(handshake)永遠無法完成?
wg show 列出的 peer 若沒有 latest handshake,代表封包未送達或未被接受。請檢查 VPS 防火牆與供應商網路防火牆的 UDP 51820 埠,確認 Endpoint 主機與連接埠正確,並檢查金鑰是否配對錯誤 —— 用戶端端的 [Peer] 區塊必須包含伺服端的 public key。sudo tcpdump -ni any udp port 51820 可顯示封包是否成功送達;dmesg 僅在啟用動態偵錯 (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control) 後才會回報 WireGuard 握手失敗,且金鑰不匹配會顯示為 invalid-MAC drop。
隧道已連線但無法連上網路。缺少了什麼?
ping 10.8.0.1 正常運作但 ping 1.1.1.1 超時,代表問題出在轉發(forwarding)或 NAT。請確認 sysctl net.ipv4.ip_forward 讀取到的內容為 1,且已在 /etc/sysctl.d/ 中設定,而非僅透過會在重啟後失效的 sysctl -w 設定。接著請檢查 masquerade 規則,確認其指定的出口介面(egress interface)符合 ip route show default 中的 enp1s0、ens3 或極少數情況下的 eth0。
用戶端設定中需要 DNS = 這一行嗎?
若使用 full tunnel 模式且未設定 DNS =,用戶端會沿用從區域網路取得的解析器(resolver),導致 DNS 查詢以明文經由區域網路傳送,而其他流量則透過隧道傳送。請將 DNS 指向公用解析器,或執行綁定至 10.8.0.1 的 unbound/dnsmasq,並在 input chain 中開啟 udp dport 53 iifname "wg0"。
AllowedIPs 實際控制什麼?
它負責兩項任務。對外(Outbound)它是路由表:符合 peer 的 AllowedIPs 之流量會被加密並傳送至該 peer。對內(Inbound)它是存取控制清單(ACL):若解密後的封包來源不在該 peer 的 AllowedIPs 範圍內,則會被丟棄。這就是為什麼伺服端會為每個用戶端列出一個 /32,而用戶端端可能只列出 0.0.0.0/0。
WireGuard 是否能在任何 VPS 上執行?
在 KVM VPS 上,可直接使用內核模組(in-kernel module)執行,無需額外設定。在與主機共用內核的容器虛擬化技術(如 OpenVZ 或 LXC)中,modprobe wireguard 會因 Operation not supported 而失敗,此時必須改用 wireguard-go 使用者空間(userspace)實作。請在執行其他操作前先執行 sudo modprobe wireguard && echo ok。