WireGuard 原理与使用
# 前言
WireGuard 是由 Jason Donenfeld 等人用 C 语言编写的一个开源3层网络隧道工具,被视为下一代 VPN 协议,旨在解决许多困扰 IPSec/IKEv2、OpenVPN 或 L2TP 等其他 VPN 协议的问题。它与 Tinc 和 MeshBird 等现代 VPN 产品有一些相似之处,即加密技术先进、配置简单。从 2020 年 1 月开始,它已经并入了 Linux 内核的 5.6 版本,这意味着大多数 Linux 发行版的用户将拥有一个开箱即用的 WireGuard。
前置知识:
- 计算机网络 (IP,UDP)
- Linux 网络框架
本文仅对其原理和使用作技术上的探讨,不对用途担责。
# WireGuard 的原理
如前言所述,WireGuard (下称:WG) 完全工作在内核模式上,这是它和其它 VPN 的很大不同之处。这意味着,WG 在效率上要高于运行在用户空间中的其他 VPN 软件(少了来回切换上下文的开销),同时却也限制了其安装,即在无法获得 root / 修改内核的情况下无法正确配置。
所有的 VPN 实现上有两种途径,一是配置系统代理,二是通过虚拟网卡。WG 工作在内核模式下,最方便的就是创建一个虚拟网卡了,在 Linux 中就是一个 TAP 设备。有了虚拟网卡,就可以分配 IP ,然后修正路由表,使得特定的流量通过虚拟网卡发送/接收,从而被 WG 处理。WG 在接收到网卡数据包时,首先进行非对称加密,然后封包通过 UDP 发送。接收到对端 (Peer) 发送的 UDP 数据包则进行解密,然后放到虚拟网卡上。加解密的操作也通过内核模块的 25519 算法完成,开销很低。
不同于 SoftEther VPN 、L2TP 等二层 VPN 协议,WG 工作在三层,即 IP 层,这意味着它无法传输二层包,也就从根本上拒绝了 DHCP。不过,静态 IP 配置也基本够用。而这样做,也进一步减少了 WG 实现上的代码量,路由的工作完全无需在 WG 中实现,完全交给系统,保持了很好的系统兼容性。
# WireGuard 的连接
上述,WG 是通过 UDP 进行端点间通信的,(并且明确不支持也不会支持 TCP),这就没有连接状态了,唯一能确定对端是否联通的方法就是可选的握手。这意味着,一旦 WG 接口启动,无论对端是否连接上,在系统中都是活动的连接,这一点在配置 DDNS 等功能的时候需要尤为注意。
WG 虽然借鉴了传统的 C/S 架构,但是没有明确端点的角色,默认情况下,由被链接的一方担任 Server,对方则为 Client。默认端口为 51820,作为 Client 时,如果不显式指定,则使用随机端口连接。典型的连接如图:
UDP
Client (Local port) <-------> Server (Listen port)
2
3
与之区分,VXLAN 则是
(Send Ports) <-------------> (Listen Port)
Peer1 UDP Peer2
(Listen Port) <-------------> (Send Ports)
2
3
基于这样的类 C/S 结构,WG 的流量能够正常穿过防火墙和 NAT 设备,而不像 VXLAN 那样需要严格的对等。
而当接口上没有流量时,在不启用握手的情况下,两个端点之间不会产生任何的 UDP 通信。考虑到 NAT 和防火墙的一般情况,即端口映射/放行策略一般有时间限制(TTL),处于 NAT/防火墙后的 WG 端点建议配置握手,即使接口上没有流量也每隔一段时间发送一个握手包,保持防火墙/NAT 放行。
# WireGuard 的配置
# 安装 WG
在一般情况下,WG 不随发行版分发,需要安装。以 Ubuntu 为例,使用
apt install wireguard-tools
安装相关内核模块和工具。安装完成后,执行 wg help
,应该能够看到这样的输出:
Usage: wg <cmd> [<args>]
Available subcommands:
show: Shows the current configuration and device information
showconf: Shows the current configuration of a given WireGuard interface, for use with `setconf'
set: Change the current configuration, add peers, remove peers, or change peers
setconf: Applies a configuration file to a WireGuard interface
addconf: Appends a configuration file to a WireGuard interface
syncconf: Synchronizes a configuration file to a WireGuard interface
genkey: Generates a new private key and writes it to stdout
genpsk: Generates a new preshared key and writes it to stdout
pubkey: Reads a private key from stdin and writes a public key to stdout
You may pass `--help' to any of these subcommands to view usage.
2
3
4
5
6
7
8
9
10
11
12
13
# 生成密钥对
由于 WG 强制使用 25519 加密,所以第一步就是生成节点的接口公私钥。
执行
wg genkey | tee wg-prikey | wg pubkey > wg-pubkey
生成公私钥对,其中 wg-prikey
为私钥文件, wg-pubkey
为公钥文件。
提示
有几个端点就生成几对公私钥!
# 编写配置文件
这里给出一个示例:
[Interface]
PrivateKey = ... # 接口私钥,即 wg-prikey 的内容,实质是 base64 编码的数据
Address = 192.168.5.100/25 # 接口地址/子网掩码长度
DNS = 223.6.6.6
MTU = 1280 # MTU - 最大传输单元
ListenPort = 25575 # 监听端口,不填就是随机,只能作 Client
[Peer]
PublicKey = ... # 对端公钥
AllowedIPs = 192.168.5.0/25 # 允许 IP,指定特定的流量经过此隧道
Endpoint = ip:port # 对端信息,ip 可以是 FQDN,IPv6 地址需要使用 [] 括住,不填默认允许接受所有连接
PersistentKeepalive = 25 # 保活(握手)间隔,不填就是0,即禁用
2
3
4
5
6
7
8
9
10
11
12
将这样的配置稍作修改部署到两台机器上,如配置文件名为 wg.conf
,则使用
wg-quick up wg.conf
创建接口并修改路由表。
对于多个对端,只需重复 [Peer] 段即可。
# 测试
如果一切正常,两台/多台端点之间应该能相互 ping 通。
Ping 不通怎么办?
- 检查配置的 IP 和端口
- 检查网段信息
- 检查 AllowIPs
- 检查防火墙
- TCPDUMP,启动!