WireGuard 系列文章(二):WireGuard 简介 - 快速、现代、安全的 VPN 隧道

本文最后更新于:2022年1月22日 晚上

系列文章前情提要:

  1. WireGuard 系列文章(一):什么是 VPN

WireGuard® 是一个非常简单,快速和现代的 VPN,它利用了 最先进的 密码学 。它旨在比 IPsec 更快 更简单,更精简,更有用,同时避免巨大的头痛。它比 OpenVPN 的性能要高得多。WireGuard 被设计为一个通用 VPN,用于在嵌入式接口和超级计算机上运行,适用于许多不同的情况。它最初是为 Linux 内核发布的,现在是跨平台(Windows,macOS,BSD,iOS,Android)并且可以广泛部署。它目前正在进行大量开发,但它已经被认为是业内最安全,最易于使用和最简单的 VPN 解决方案。

从 2020 年 1 月开始,它已经并入了 Linux 内核的 5.6 版本,这意味着大多数 Linux 发行版的用户将拥有一个开箱即用的 WireGuard。

WireGuard 优点

简单易用

WireGuard 旨在像 SSH 一样易于配置和部署。VPN 连接只需通过交换非常简单的公钥即可实现 - 就像交换 SSH 密钥一样 - 其余所有内容都由 WireGuard 透明地处理。它甚至能够在 IP 地址之间漫游,就像 Mosh 一样。无需管理连接、关注状态、管理守护程序或担心后台的内容。WireGuard 提供了一个非常基本但功能强大的界面。

加密健全

WireGuard 使用最先进的密码学,如 噪声协议框架Curve25519ChaCha20Poly1305BLAKE2SipHash24HKDF 和安全可信结构。 它做出了保守合理的选择,并经过密码学家的审查。

最小攻击面

WireGuard 在设计时考虑了易于实现和简单性。它意味着在很少的代码行中轻松实现,并且易于审计安全漏洞。与 *Swan/IPsec 或 OpenVPN/OpenSSL 等庞然大物相比,即使对于大型安全专家团队来说,审计庞大的代码库也是一项艰巨的任务,WireGuard 意味着可以由单个人进行全面审查。

高性能

极高速的加密原语和 WireGuard 存在于 Linux 内核中的事实相结合,意味着安全网络可以非常高速。它适用于智能手机等小型嵌入式设备和满载骨干路由器。

WireGuard 与其他 VPN 协议的性能测试对比:

ireGuard 与其他 VPN 协议的性能测试对比

定义明确,经过深思熟虑

WireGuard 是漫长而彻底考虑的学术过程的结果,产生了 技术白皮书,一篇学术研究论文,明确定义了协议和每个决策的激烈考虑因素。

WireGuard 通过 UDP 安全地封装 IP 数据包。您可以添加一个 WireGuard 接口,使用您的私钥和对等方的公钥对其进行配置,然后通过它发送数据包。密钥分发和推送配置的所有问题都不在 WireGuard 的范围之内;这些问题最好留给其他层,以免最终导致类似 IKE 或 OpenVPN 的膨胀。相比之下,它更多地模仿了 SSH 和 Mosh 的模型;双方都拥有对方的公钥,然后他们就可以开始通过接换数据包。

简单的网络接口(interface)

WireGuard 通过添加一个(或多个)网络接口(interface)来工作,比如 eth0wlan0 ,称为 wg0 (或wg1wg2wg3 等)。然后可以使用 ifconfig(8)ip-address(8) 对该网络接口(interface)进行正常配置,并使用 route(8)ip-route(8) 添加或删除该网络接口(interface)的路由,以此类推,使用所有普通网络实用工具。该接口的特定 WireGuard 方面是使用 wg(8) 工具配置的。该接口相当于 tunnel 接口。

WireGuard 将隧道 IP 地址与公钥和远程端点相关联。当接口向对等方发送数据包时,它会执行以下操作:

  1. 此数据包适用于 192.168.30.8。那是哪个 peer ?让我看看… 找到了,这是为 peer ABCDEFGH准备的。(或者,如果不是针对任何已配置的 peer ,丢弃数据包。)
  2. 使用 peer ABCDEFGH 的公钥加密整个 IP 数据包。
  3. peer ABCDEFGH 的远程端点是什么?让我看看… 找到了,端点是主机 216.58.211.110 上的 UDP 端口 53133。
  4. 使用 UDP 通过 Internet 将步骤 2 中的加密字节发送到 216.58.211.110:53133。

当接口收到数据包时,会发生这种情况:

  1. 我刚刚从主机 98.139.183.24 上的 UDP 端口 7361 获得了一个数据包。让我们解密它!
  2. Peer LMNOPQRS 进行了正确的解密和验证。好的,让我们记住,peer LMNOPQRS 的最新互联网端点是 98.139.183.24:7361,使用 UDP。
  3. 解密后,明文数据包来自 192.168.43.89。是否允许 peer LMNOPQRS 以 192.168.43.89 的身份向我们发送数据包?
  4. 如果是,请在接口上接受该报文。如果没有,就放弃吧。

在幕后,使用最先进的加密技术,可以提供适当的隐私性、真实性和完美的前向保密。

加密密钥路由

WireGuard 的核心是一个名为 Cryptokey Routing 的概念,它的工作原理是将公钥与隧道中允许的隧道 IP 地址列表相关联。每个网络接口(interface)都有一个私钥和一个 peer list。每个 peer 都有一个公钥。公钥既短又简单,被 peer 用来互相验证。它们可以通过任何带外方法在配置文件中传递,类似于将其 SSH 公钥发送给朋友以访问 shell 服务器。

例如,服务器计算机可能具有以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Interface]
PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=
ListenPort = 51820

[Peer]
PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=
AllowedIPs = 10.192.122.3/32, 10.192.124.1/24

[Peer]
PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=
AllowedIPs = 10.192.122.4/32, 192.168.0.0/16

[Peer]
PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=
AllowedIPs = 10.10.10.230/32

客户端计算机可能具有以下更简单的配置:

1
2
3
4
5
6
7
8
[Interface]
PrivateKey = gI6EdUSYvn8ugXOt8QQD6Yc+JyiZxIhp3GInSWRfWGE=
ListenPort = 21841

[Peer]
PublicKey = HIgo9xNzJMWLKASShiTqIybxZ0U3wGLiUeJ1PKf8ykw=
Endpoint = 192.95.5.69:51820
AllowedIPs = 0.0.0.0/0

在服务器配置中,每个 peer (客户端) 将能够发送数据包到网络接口(interface),其源 IP 与相应的允许 IP 列表相匹配。例如,当服务器从 peer gN65BkIK... 接收到一个报文,经过解密和认证后,如果其源 IP 为 10.10.10.230,则允许其进入接口;否则它就被丢弃。

在服务器配置中,当网络接口(interface)想要向 peer (客户端)发送数据包时,它会查看数据包的目的 IP,并将其与每个 peer 的允许 IP 列表进行比较,以确定将它发送到哪个 peer。例如,如果要求该网络接口(interface)发送一个目的 IP 为 10.10.10.230 的数据包,它将使用 peer gN65BkIK...的公钥对其进行加密,然后将其发送到该节点最近的 Internet 端点。

在客户端配置中,它的单个 peer (服务器)将能够发送数据包到 任何源 IP的网络接口(因为 0.0.0.0/0 是一个通配符)。例如,当一个数据包从 peer HIgo9xNz...接收到,如果它对任何源 IP 进行了正确的解密和认证,那么它就可以进入接口;否则它就被丢弃。

在客户端配置中,当网络接口(interface)希望将一个包发送到它的单个 peer (服务器)时,它将使用 任意目标 IP 地址 加密单个 peer 的包(因为 0.0.0.0/0 是一个通配符)。例如,如果要求网络接口(interface)发送带有任意目的地 IP 的数据包,它将使用单个 peer HIgo9xNz... 的公钥对其进行加密,然后将其发送到离 peer 最近的 Internet 端点。

换句话说,在发送数据包时,允许的 IP 列表表现为一种路由表,而在接收数据包时,允许的 IP 列表的行为为一种访问控制列表。

这就是我们所说的 加密密钥路由表(Cryptokey Routing Table):公钥和允许的 IP 的简单关联。

对于任何字段,可以使用 IPv4 和 IPv6 的任何组合。如有必要,Wireguard 完全能够在一个内部封装另一个。

由于在 WireGuard 接口上发送的所有数据包都经过加密和身份验证,并且由于 peer 的身份与 peer 允许的 IP 地址之间存在如此紧密的耦合,因此系统管理员不需要复杂的防火墙扩展(例如在 IPsec 的情况下),而是可以简单地匹配 「它是否来自此 IP?在此接口(interface)上?」,并确保它是一个安全且真实的数据包。这极大地简化了网络管理和访问控制,并提供了更多的保证,即您的 iptables 规则实际上正在执行您希望它们执行的操作。

内置漫游(Roaming)

客户端配置包含其单个 peer(服务器)的 初始 端点(endpoint),以便它在收到加密数据之前知道将加密数据发送到何处。服务器配置没有任何其 peer(客户端)的初始端点(endpoint)。这是因为服务器通过检查正确身份验证的数据的来源来发现其 peer 的端点(endpoint)。如果服务器本身更改了自己的端点(endpoint),并将数据发送到客户端,则客户端将发现新的服务器端点(endpoint)并更新相同的配置。客户端和服务器都将加密数据发送到它们对其真实解密数据的最新 IP 端点(endpoint)。因此,两端都有完整的 IP 漫游。

容器就绪

WireGuard 使用最初创建 WireGuard 接口的网络命名空间发送和接收加密数据包 。这意味着您可以在主网络命名空间中创建 WireGuard 接口(该接口可以访问 Internet),然后将其移动到属于 Docker 容器的网络命名空间中,作为该容器的 唯一 接口。这确保了容器能够访问网络的唯一可能方式是通过安全的加密 WireGuard 隧道。

WireGuard 优点总结

  • 简单易用
  • 加密健全
  • 最小攻击面
  • 高性能:比目前主流的 VPN 协议,连接速度要更快,延迟更低
  • 简单的网络接口(interface):就像普通的以太网接口一样,以 Linux 内核模块的形式运行,资源占用小。
  • 加密密钥路由:使用了更先进的加密技术,具有前向加密和抗降级攻击的能力。
  • 内置漫游
  • 容器就绪:可以运行在主机中为容器之间提供通信,也可以运行在容器中为主机之间提供通信。
  • 能够将部分流量或所有流量通过 VPN 传送到局域网内的任意主机。
  • 能够在网络故障恢复之后自动重连,这是相比其他 VPN 优势的一点
  • 支持任何类型的二层网络通信,例如 ARPDHCPICMP,而不仅仅是 TCP/HTTP。比如 VPN Peer 之间可以互 ping

WireGuard 词汇表

Peer/Node/Device

Peer:对等节点。

连接到 VPN 并为自己注册一个 VPN 子网地址(如 10.4.1.3)的主机。还可以通过使用逗号分隔的 CIDR 指定子网范围,为其自身地址以外的 IP 地址选择路由。

中继服务器(Bounce Server)

本质上还是一个 peer,只是该 peer 公网可达 ,可以将流量中继到 NAT 后面(如:家里的电脑、NAS)的其他对等节点。Bounce Server 并不是特殊的节点,它和其他对等节点一样,唯一的区别是它有公网 IP,并且 开启了内核级别的 IP 转发,可以将 VPN 的流量转发到其他客户端。

子网(Subnet)

一组私有 IP,例如 10.4.1.1-255192.168.1.1/24,一般在 NAT 后面,例如办公室局域网或家庭网络。

NAT

子网的私有 IP 地址由路由器提供,通过公网无法直接访问私有子网设备,需要通过 NAT 做网络地址转换。路由器会跟踪发出的连接,并将响应转发到正确的内部 IP。

公开端点(Public Endpoint)

节点的公网 IP 地址: 端口,例如 123.124.125.126:1234,或者直接使用域名 some.domain.tld:1234。如果对等节点不在同一子网中,那么节点的公开端点必须使用公网 IP 地址。

私钥(Private key)

单个节点的 WireGuard 私钥,生成方法是:wg genkey > example.key

公钥(Public key)

单个节点的 WireGuard 公钥,生成方式为:wg pubkey < example.key > example.key.pub

DNS

域名服务器,用于将域名解析为 VPN 客户端的 IP,不让 DNS 请求泄漏到 VPN 之外。

WireGuard 工作原理

中继服务器工作原理

中继服务器(Bounce Server)和普通的对等节点一样,它能够在 NAT 后面的 VPN 客户端之间充当中继服务器,可以将收到的任何 VPN 子网流量转发到正确的对等节点。事实上 WireGuard 并不关心流量是如何转发的,这个由系统内核和 iptables 规则处理。

如果所有的对等节点都是公网可达的,则不需要考虑中继服务器,只有当有对等节点位于 NAT 后面时才需要考虑。

在 WireGuard 里,客户端和服务端基本是平等的,差别只是谁主动连接谁而已。双方都会监听一个 UDP 端口,谁主动连接,谁就是客户端。主动连接的客户端需要指定对端的公网地址和端口,被动连接的服务端不需要指定其他对等节点的地址和端口。如果客户端和服务端都位于 NAT 后面,需要加一个中继服务器,客户端和服务端都指定中继服务器作为对等节点,它们的通信流量会先进入中继服务器,然后再转发到对端。

WireGuard 是支持漫游的,也就是说,双方不管谁的地址变动了,WireGuard 在看到对方从新地址说话的时候,就会记住它的新地址(跟 mosh 一样,不过是双向的)。所以双方要是一直保持在线,并且通信足够频繁的话(比如配置 persistent-keepalive),两边的 IP 都不固定也不影响的。

Wireguard 如何路由流量

利用 WireGuard 可以组建非常复杂的 网络拓扑,这里主要介绍几个典型的拓扑:

点对点(Point-to-point)

这是最简单的拓扑,所有的节点要么在同一个局域网,要么直接通过公网访问,这样 WireGuard 可以直接连接到对端,不需要中继跳转。

中心辐射型(Hub-and-spoke)

一端位于 NAT 后面,另一端直接通过公网暴露

这种情况下,最简单的方案是:通过公网暴露的一端作为服务端,另一端指定服务端的公网地址和端口,然后通过 persistent-keepalive 选项维持长连接,让 NAT 记得对应的映射关系。

两端都位于 NAT 后面,通过中继服务器连接

大多数情况下,当通信双方都在 NAT 后面的时候,NAT 会做源端口随机化处理,直接连接可能比较困难。可以加一个中继服务器,通信双方都将中继服务器作为对端,然后维持长连接,流量就会通过中继服务器进行转发。

小结

在 WireGuard 的世界里没有 Server 和 Client 之分,所有的节点都是 Peer。大家使用 WireGuard 的常规做法是找一个节点作为中转节点,也就是 VPN 网关,然后所有的节点都和这个网关进行连接,所有节点之间都通过这个网关来进行通信。这种架构中,为了方便理解,我们可以把网关看成 Server,其他的节点看成 Client,但实际上是不区分 Server 和 Client 的。架构示例如下图所示:

WireGuard Hub-and-spoke

这种架构的缺点相当明显:

  • 当 Peer 越来越多时,VPN 网关就会变成垂直扩展的瓶颈。
  • 通过 VPN 网关转发流量的成本很高,毕竟云服务器的流量很贵。
  • 通过 VPN 网关转发流量会带来很高的延迟。

全网状网络(Full mesh)

✔️ 重点

本次 Wireguard 后续实战的重点就是实现 Full mesh 网络。
无论是 VPN 网络还是 K8S 网络。

书接上文,全互联模式是什么样的架构呢?假设有五个节点,每个节点都和其他节点建立 WireGuard 隧道,架构如图:

WireGuard Full Mesh

这种架构带来的直接优势就是快!任意一个 Peer 和其他所有 Peer 都是直连,无需中转流量。那么在 WireGuard 的场景下如何实现全互联模式呢?其实这个问题不难,难点在于配置的繁琐程度,但是目前已经有很多开源工具帮忙自动实现 WireGuard Full Mesh 的配置,后面会详细介绍我通过 NetMaker 这样一款工具实现 WireGuard 的 Full Mesh 网络。

为什么需要全网状网络(Full mesh)?

以我为例,我趁双十一买(薅)了阿里、腾讯、百度、华为、天翼云的 1C2G 均价 80 元 / 年 的云服务器,那么:

  1. 一方面,可以通过 WireGuard Full mesh 将这些服务器组成一个全互联的内网;
  2. 另一方面,可用通过 K8S 网络插件(如:Flannel、Cilium 等)配合 WireGuard 组建一个跨云的 K8S 集群;
  3. 本地家庭网络和移动设备及电脑可以和云上网络全互联互通;
  4. 更进一步,可以用家里的 NAS 和电脑和云服务器的节点组建 WireGuard 隧道,然后本地的 AllowedIPs 中加入 Pod 网段和 Service 网段,这就相当于是 K8S 本地和开发环境双向打通的方案了,可以大大提升开发效率;
  5. 再进一步,甚至家里的设备也可以加入云上 K8S 集群,作为其中的一个节点。

还是很诱人的~😍😍😍

关键原理和实现总结

  • 在发送数据包时,允许的 IP 列表表现为一种 路由表 ,而在接收数据包时,允许的 IP 列表的行为为一种 访问控制列表(ACL)
  • WireGuard 只有 peer(对等节点)的概念,原则上没有 server 和 client 的区别
  • 在 WireGuard 里,客户端和服务端基本是平等的(都是 peer),差别只是谁主动连接谁而已
  • 定义本地节点应该对哪个地址范围进行路由。如果是常规的客户端,则将其设置为节点本身的单个 IP(使用 CIDR 指定,例如 10.88.60.2/32);如果是中继服务器,则将其设置为可路由的子网范围。
  • 中继服务器必须将所有的客户端定义为对等节点(peer),除了中继服务器之外,其他客户端都不能将位于 NAT 后面的节点定义为对等节点(peer),因为路由不可达。对于那些只为自己路由流量的客户端,只需将中继服务器作为对等节点(peer),以及其他需要直接访问的节点。
  • 如果对等节点(peer)位于 NAT 后面或者没有稳定的公网访问地址,就忽略 Endpoint。

参考资料