WireGuard 原理与使用

2/25/2024 WireGuardUDP

# 前言

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)

1
2
3

与之区分,VXLAN 则是

         (Send Ports)    <------------->   (Listen Port)
 Peer1                         UDP                       Peer2
         (Listen Port)   <------------->   (Send Ports)
1
2
3

基于这样的类 C/S 结构,WG 的流量能够正常穿过防火墙和 NAT 设备,而不像 VXLAN 那样需要严格的对等。

而当接口上没有流量时,在不启用握手的情况下,两个端点之间不会产生任何的 UDP 通信。考虑到 NAT 和防火墙的一般情况,即端口映射/放行策略一般有时间限制(TTL),处于 NAT/防火墙后的 WG 端点建议配置握手,即使接口上没有流量也每隔一段时间发送一个握手包,保持防火墙/NAT 放行。

# WireGuard 的配置

# 安装 WG

在一般情况下,WG 不随发行版分发,需要安装。以 Ubuntu 为例,使用

apt install wireguard-tools
1

安装相关内核模块和工具。安装完成后,执行 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.
1
2
3
4
5
6
7
8
9
10
11
12
13

# 生成密钥对

由于 WG 强制使用 25519 加密,所以第一步就是生成节点的接口公私钥。

执行

 wg genkey | tee wg-prikey | wg pubkey > wg-pubkey
1

生成公私钥对,其中 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,即禁用
1
2
3
4
5
6
7
8
9
10
11
12

将这样的配置稍作修改部署到两台机器上,如配置文件名为 wg.conf,则使用

wg-quick up wg.conf
1

创建接口并修改路由表。

对于多个对端,只需重复 [Peer] 段即可。

# 测试

如果一切正常,两台/多台端点之间应该能相互 ping 通。

Ping 不通怎么办?

  • 检查配置的 IP 和端口
  • 检查网段信息
  • 检查 AllowIPs
  • 检查防火墙
  • TCPDUMP,启动!

# 参考

被Linux创始人称做艺术品的组网神器——WireGuard (opens new window)

WireGuard官网 (opens new window)