WireGuard
引用自 WireGuard 項目主頁:
- WireGuard 是一種極其簡單但快速且現代的 VPN,它利用了最先進的加密技術。它的目標是比 IPsec 更快、更簡單、更精簡和更有用,同時避免令人頭疼的問題。其旨在提供比 OpenVPN 更高的性能。WireGuard 被設計為在嵌入式接口和超級計算機等上運行的通用 VPN,適用於許多不同的環境。雖然最初僅支持 Linux,但現在可以跨平台(Windows、macOS、BSD、iOS、Android)廣泛部署。
本文中的主要概念在 WireGuard 項目主頁上有粗略的介紹。2019 年底以後,Linux 內核中包含 WireGuard。
安裝[編輯 | 編輯原始碼]
安裝 wireguard-tools包,其提供了用戶空間實用程序。
另外,如果 Peer 公鑰可用,也可以藉助其他網絡管理器的 WireGuard 支持。詳見#持久化配置。
圖形客戶端[編輯 | 編輯原始碼]
- Qomui — OpenVPN GUI,具有高級功能,且支持多個後端。
命令行工具[編輯 | 編輯原始碼]
- wg_tool — 用於管理服務器和用戶 wireguard 配置的工具。
- wg2nd — 將 WireGuard 配置從 wg-quick(8) 格式轉換為 systemd-networkd 兼容的配置的工具。
使用[編輯 | 編輯原始碼]
以下命令展示了如何在兩個對等節點之間建立一條使用以下設置的基本的隧道:
外部(公共)地址 | 內部 IP 地址 | 端口 | ||||
---|---|---|---|---|---|---|
域名 | IPv4 地址 | IPv6 地址 | IPv4 地址 | IPv6 地址 | ||
對等節點 A | 198.51.100.101 | 2001:db8:a85b:70a:ffd4:ec1b:4650:a001 | 10.0.0.1/24 | fdc9:281f:04d7:9ee9::1/64 | UDP/51871 | |
對等節點 B | peer-b.example | 203.0.113.102 | 2001:db8:40f0:147a:80ad:3e88:f8e9:b002 | 10.0.0.2/24 | fdc9:281f:04d7:9ee9::2/64 | UDP/51902 |
對等節點 C | 動態(不固定) | 動態(不固定) | 10.0.0.3/24 | fdc9:281f:04d7:9ee9::3/64 | UDP/51993 |
外部地址應該已經存在。例如,如果 ICMP 回顯請求未被阻止,則對等節點 A 應該能夠通過 ping 通對等點 B 的公共 IP 地址,反之亦然。
內部地址將是使用 ip(8) 實用程序手動創建或通過網絡管理軟件創建的新地址,這些地址將在新的 WireGuard 網絡內部使用。以下示例使用 10.0.0.0/24 和 fdc9:281f:04d7:9ee9::/64 作為內部網絡。 IP 地址中的 /24
和 /64
是 CIDR。
生成密鑰[編輯 | 編輯原始碼]
為每個對等節點創建公鑰和私鑰。如果要連接很多節點,可以考慮 vanity keypair 個性化 Base64 編碼的公鑰字符串,參見#Vanity keys。
生成私鑰:
$ (umask 0077; wg genkey > peer_A.key)
生成公鑰:
$ wg pubkey < peer_A.key > peer_A.pub
或者,一次性生成公鑰和私鑰:
$ wg genkey | (umask 0077 && tee peer_A.key) | wg pubkey > peer_A.pub
還可以生成預共享密鑰,以添加一層額外的對稱密鑰加密技術,將其混合到現有的公鑰加密技術中,以實現後量子抵抗。應為每個對等節點對生成預共享密鑰,且不應重複使用。例如,三個互連的對等節點 A、B 和 C 將需要三個單獨的預共享密鑰,每個對等節點對一個。
使用以下命令為每個對等節點對生成預共享密鑰(確保也使用 umask 0077
):
$ wg genpsk > peer_A-peer_B.psk $ wg genpsk > peer_A-peer_C.psk $ wg genpsk > peer_B-peer_C.psk
Vanity keys[編輯 | 編輯原始碼]
Currently, WireGuard does not support comments or attaching human-memorable names to keys. This makes identifying the key's owner difficult particularly when multiple keys are in use. One solution is to generate a public key that contains some familiar characters (perhaps the first few letters of the owner's name or of the hostname etc.), wireguard-vanity-addressAUR does this.
For example:
$ wireguard-vanity-address --in 8 leslie
searching for 'leslie' in pubkey[0..10], one of every 214748364 keys should match one core runs at 2.69e6 keys/s, CPU cores available: 16 est yield: 5.0 seconds per key, 200.10e-3 keys/s hit Ctrl-C to stop private wEoVMj92P+E3fQXVf9IixWJqpCqcnP/4OfvrB1g3zmY= public LEsliEny+aMcWcRbh8Qf414XsQHSBOAFk3TaEk/aSD0= private EAOwlGGqpHVbZ9ehaCspdBJt+lkMcCfkwiA5T5a4JFs= public VlesLiEB5BFd//OD2ILKXviolfz+hodG6uZ+XjoalC8= private UDWG4VWI+RzAGzNSnlC+0X4d3nk9goWPs/NRC5tX524= public 9lESlieIFOlJFV6dG7Omao2WS+amWgshDdBYn8ahRjo=
在 peer 上生成公鑰和私鑰:
$ wg genkey | tee privatekey | wg pubkey > publickey
下面的指令會演示如何以表中的配置建立一條兩個 peer 之間的隧道
Peer A | Peer B | |
---|---|---|
公網地址 | 10.10.10.1/24 | 10.10.10.2/24 |
內網地址 | 10.0.0.1/24 | 10.0.0.2/24 |
wireguard 監聽端口 | UDP/4857 | UDP/3981 |
Peer 應當已經擁有公網地址。例如,peer A 應當能夠通過 ping 10.10.10.2
ping 通 peer B,反之亦然。內部地址是由下文 ip
命令創建的新地址,並且將在 WireGuard 網絡中共享。IP地址中 /24
的含義詳見 CIDR
Peer A 配置[編輯 | 編輯原始碼]
這個 peer 將監聽 UDP 端口 48574,通過將 peer B 的公鑰與其內部和外部 IP 地址關聯,接受來自 peer B 的連接。
# ip link add dev wg0 type wireguard # ip link set dev wg0 mtu 1420 # ip addr add 10.0.0.1/24 dev wg0 # wg set wg0 listen-port 4857 private-key ./privatekey # wg set wg0 peer [Peer B public key] persistent-keepalive 25 allowed-ips 10.0.0.2/32 endpoint 10.10.10.2:3981 # ip link set wg0 up
[Peer B public key]
的格式應當如同 EsnHH9m6RthHSs+sd9uM6eCHe/mMVFaRh93GYadDDnM=
。allowed-ips
是 peer A 能夠向之發送流量的地址列表。allowed-ips 0.0.0.0/0
將允許向任意地址發送流量。
Peer B 配置[編輯 | 編輯原始碼]
如同 Peer A,只不過 wireguard 守護監聽 UDP 端口 39814 並且只接受 peer A 的連接
# ip link add dev wg0 type wireguard # ip link set dev wg0 mtu 1420 # ip addr add 10.0.0.2/24 dev wg0 # wg set wg0 listen-port 3981 private-key ./privatekey # wg set wg0 peer [Peer A public key] persistent-keepalive 25 allowed-ips 10.0.0.1/32 endpoint 10.10.10.1:4857 # ip link set wg0 up
基本檢查[編輯 | 編輯原始碼]
不帶任何參數使用 wg 命令可以快速查看當前的配置。
例如,當 peer A 配置好之後,我們可以看見它的身份和與之關聯的 peers。
peer-a$ wg interface: wg0 public key: UguPyBThx/+xMXeTbRYkKlP0Wh/QZT3vTLPOVaaXTD8= private key: (hidden) listening port: 4857 peer: 9jalV3EEBnVXahro0pRMQ+cHlmjE33Slo9tddzCVtCw= endpoint: 10.10.10.2:3981 allowed ips: 10.0.0.2/32
此時我們可以 ping 通隧道的另一端:
peer-a$ ping 10.0.0.2
配置持久化[編輯 | 編輯原始碼]
配置可以通過 showconf
來保存
# wg showconf wg0 > /etc/wireguard/wg0.conf # wg setconf wg0 /etc/wireguard/wg0.conf
示例 peer 配置[編輯 | 編輯原始碼]
/etc/wireguard/wg0.conf
[Interface] PrivateKey = [CLIENT PRIVATE KEY] MTU = 1420 [Peer] PublicKey = [SERVER PUBLICKEY] AllowedIPs = 10.0.0.0/24, 10.123.45.0/24, 1234:4567:89ab::/48 Endpoint = [SERVER ENDPOINT]:5182 PersistentKeepalive = 25
配置一個 VPN 服務器[編輯 | 編輯原始碼]
WireGuard 自帶一個快速創建和銷毀 VPN 服務器的工具,wg-quick
。注意這裡使用的配置文件不是一個能被 wg setconf
有效的配置文件,並且你可能至少要把 eth0
改成你實際使用的。
服務器[編輯 | 編輯原始碼]
/etc/wireguard/wg0server.conf
[Interface] Address = 10.0.0.1/24 # This is the virtual IP address, with the subnet mask we will use for the VPN PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE ListenPort = 5182 PrivateKey = [SERVER PRIVATE KEY] MTU = 1420 [Peer] PublicKey = [CLIENT PUBLIC KEY] AllowedIPs = 10.0.0.2/32 # 這表示客戶端只有一個 IP。
要使 iptables 規則生效,啟用 IPv4 轉發:
# sysctl net.ipv4.ip_forward=1
永久保留這項改變,向 /etc/sysctl.d/99-sysctl.conf
添加 net.ipv4.ip_forward = 1
。
使用 wg-quick up wg0server
啟用 Interface,wg-quick down wg0server
用以關閉
客戶端 (轉發所有流量)[編輯 | 編輯原始碼]
/etc/wireguard/wg0.conf
[Interface] Address = 10.0.0.2/24 # The client IP from wg0server.conf with the same subnet mask PrivateKey = [CLIENT PRIVATE KEY] DNS = 10.0.0.1 MTU = 1420 [Peer] PublicKey = [SERVER PUBLICKEY] AllowedIPs = 0.0.0.0/0, ::0/0 Endpoint = [SERVER ENDPOINT]:5182 PersistentKeepalive = 25
使用 wg-quick up wg0
來啟用 Interface, 使用 wg-quick down wg0
來關閉。
使用 systemctl enable wg-quick@wg0
來自動啟動。
如果你使用 NetworkManager, 可能有必要啟用 NetworkManager-wait-online.service systemctl enable NetworkManager-wait-online.service
或者你使用的是 systemd-networkd, 啟用 systemd-networkd-wait-online.service systemctl enable systemd-networkd-wait-online.service
等待所有設備就緒再嘗試 WireGuard 連接
使用技巧[編輯 | 編輯原始碼]
以加密的形式存儲私鑰[編輯 | 編輯原始碼]
以加密的形式存儲私鑰可能是可以實現的,例如通過使用 pass包。只需將配置文件中[Interface]下的 PrivateKey 一行替換為。
PostUp = wg set %i private-key <(su user -c "export PASSWORD_STORE_DIR=/path/to/your/store/; pass WireGuard/private-keys/%i")
where user is your username. See the `wg-quick(8)` man page for more details.
將其中的user替換成你的用戶名。更多細節請參見 `wg-quick(8)` man 頁面。
疑難解答[編輯 | 編輯原始碼]
路由定期被重置[編輯 | 編輯原始碼]
NetworkManager 用戶應確保其未管理 WireGuard 接口。例如,創建以下配置文件:
/etc/NetworkManager/conf.d/unmanaged.conf
[keyfile] unmanaged-devices=type:wireguard
DNS 無法解析[編輯 | 編輯原始碼]
When tunneling all traffic through a WireGuard interface, the connection can become seemingly lost after a while or upon new connection. This could be caused by a network manager or DHCP client overwriting /etc/resolv.conf
.
By default wg-quick uses resolvconf to register new DNS entries (from the DNS
keyword in the configuration file). This will cause issues with network managers and DHCP clients that do not use resolvconf, as they will overwrite /etc/resolv.conf
thus removing the DNS servers added by wg-quick.
The solution is to use networking software that supports resolvconf.
Users of NetworkManager should know that it does not use resolvconf by default. It is recommended to use systemd-resolved. If this is undesirable, install openresolv包 and configure NetworkManager to use it: NetworkManager#Use openresolv.
MTU 過低[編輯 | 編輯原始碼]
Due to too low MTU (lower than 1280), wg-quick may have failed to create the WireGuard interface. This can be solved by setting the MTU value in WireGuard configuration in Interface section on client.
foo.config
[Interface] Address = 10.200.200.2/24 MTU = 1420 PrivateKey = PEER_FOO_PRIVATE_KEY DNS = 10.200.200.1
密鑰長度或格式錯誤[編輯 | 編輯原始碼]
To avoid the following error, put the key value in the configuration file and not the path to the key file.
# wg-quick up wg0
[#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 Key is not the correct length or format: `/path/example.key' Configuration parsing error [#] ip link delete dev wg0
無法在 NAT 或防火牆後建立持久連接[編輯 | 編輯原始碼]
By default, WireGuard peers remain silent while they do not need to communicate, so peers located behind a NAT and/or firewall may be unreachable from other peers until they reach out to other peers themselves (or the connection may time out). Adding PersistentKeepalive = 25
to the [Peer]
settings of a peer located behind a NAT and/or firewall can ensure that the connection remains open.
To temporarily set the persistent-keepalive setting via command line, run the following command:
# wg set wg0 peer public_key persistent-keepalive 25
循環路由[編輯 | 編輯原始碼]
Adding the endpoint IP to the allowed IPs list, the kernel will attempt to send handshakes to said device binding, rather than using the original route. This results in failed handshake attempts.
As a workaround, the correct route to the endpoint needs to be manually added using
# ip route add endpoint_ip via gateway dev network_interface
E.g. for peer B from above in a standard LAN setup:
# ip route add 203.0.113.102 via 192.168.0.1 dev eth0
To make this route persistent, the command can be added as PostUp = ip route ...
to the [Interface]
section of wg0.conf
. However, on certain setups (e.g. using wg-quick@.service
in combination with NetworkManager) this might fail on resume. Furthermore, this only works for a static network setup and fails if gateways or devices change (e.g. using Ethernet or Wi-Fi on a laptop).
Using NetworkManager, a more flexible solution is to start WireGuard using a dispatcher script. As root, create
/etc/NetworkManager/dispatcher.d/50-wg0.sh
#!/bin/sh case $2 in up) wg-quick up wg0 ip route add <endpoint ip> via $IP4_GATEWAY dev $DEVICE_IP_IFACE ;; pre-down) wg-quick down wg0 ;; esac
If not already running, start and enable NetworkManager-dispatcher.service
. Also make sure that NetworkManager is not managing routes for wg0
, see #Routes are periodically reset.
使用 systemd-networkd 時睡眠導致連接丟失[編輯 | 編輯原始碼]
systemd version 253 introduced a change in how network interfaces are reconfigured when resuming from a suspended state[1]. In doing so, network connections managed by systemd-networkd will lose connection to the wireguard interface. Unless a kill switch is configured, this risks exposing the public IP address after resuming from suspend. To fix this, uncomment and change the value to no
for ManageForeignRoutingPolicyRules
in /etc/systemd/networkd.conf
. [2]