系列文章前情提要:
1.WireGuard系列文章(一):什么是V**[1]
WireGuard® 是一个非常简单,快速和现代的 V**,它利用了最先进的 密码学[2]。它旨在比 IPsec更快[3],更简单[4],更精简,更有用,同时避免巨大的头痛。它比 OpenV** 的性能要高得多。WireGuard 被设计为一个通用 V**,用于在嵌入式接口和超级计算机上运行,适用于许多不同的情况。它最初是为 Linux 内核发布的,现在是跨平台(Windows,macOS,BSD,iOS,Android)并且可以广泛部署。它目前正在进行大量开发,但它已经被认为是业内最安全,最易于使用和最简单的 V** 解决方案。
从 2020 年 1 月开始,它已经并入了 Linux 内核的 5.6
版本,这意味着大多数 Linux 发行版的用户将拥有一个开箱即用的 WireGuard。
WireGuard 优点
简单易用
WireGuard 旨在像 SSH 一样易于配置和部署。V** 连接只需通过交换非常简单的公钥即可实现 - 就像交换 SSH 密钥一样 - 其余所有内容都由 WireGuard 透明地处理。它甚至能够在 IP 地址之间漫游,就像 Mosh[5] 一样。无需管理连接、关注状态、管理守护程序或担心后台的内容。WireGuard 提供了一个非常基本但功能强大的界面。
加密健全
WireGuard 使用最先进的密码学,如 噪声协议框架[6],Curve25519[7],ChaCha20[8],Poly1305[9],BLAKE2[10],SipHash24[11],HKDF[12] 和安全可信结构。它做出了保守合理的选择,并经过密码学家的审查。
最小攻击面
WireGuard 在设计时考虑了易于实现和简单性。它意味着在很少的代码行中轻松实现,并且易于审计安全漏洞。与*Swan/IPsec 或 OpenV**/OpenSSL 等庞然大物相比,即使对于大型安全专家团队来说,审计庞大的代码库也是一项艰巨的任务,WireGuard 意味着可以由单个人进行全面审查。
高性能
极高速的加密原语和 WireGuard 存在于 Linux 内核中的事实相结合,意味着安全网络可以非常高速。它适用于智能手机等小型嵌入式设备和满载骨干路由器。
WireGuard 与其他 V** 协议的性能测试对比:
ireGuard 与其他 V** 协议的性能测试对比
定义明确,经过深思熟虑
WireGuard 是漫长而彻底考虑的学术过程的结果,产生了 技术白皮书[13],一篇学术研究论文,明确定义了协议和每个决策的激烈考虑因素。
WireGuard 通过 UDP 安全地封装 IP 数据包。您可以添加一个 WireGuard 接口,使用您的私钥和对等方的公钥对其进行配置,然后通过它发送数据包。密钥分发和推送配置的所有问题都不在 WireGuard 的范围之内;这些问题最好留给其他层,以免最终导致类似 IKE 或 OpenV** 的膨胀。相比之下,它更多地模仿了 SSH 和 Mosh 的模型;双方都拥有对方的公钥,然后他们就可以开始通过接换数据包。
简单的网络接口(interface)
WireGuard 通过添加一个(或多个)网络接口(interface)来工作,比如 eth0
或 wlan0
,称为 wg0
(或wg1
、wg2
、wg3
等)。然后可以使用 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 服务器。
例如,服务器计算机可能具有以下配置:
代码语言:javascript复制[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
客户端计算机可能具有以下更简单的配置:
代码语言:javascript复制[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 接口的网络命名空间发送和接收加密数据包[14]。这意味着您可以在主网络命名空间中创建 WireGuard 接口(该接口可以访问 Internet),然后将其移动到属于 Docker 容器的网络命名空间中,作为该容器的唯一接口。这确保了容器能够访问网络的唯一可能方式是通过安全的加密 WireGuard 隧道。
WireGuard 优点总结
•简单易用
•加密健全
•最小攻击面
•高性能:比目前主流的 V** 协议,连接速度要更快,延迟更低
•简单的网络接口(interface):就像普通的以太网接口一样,以 Linux 内核模块的形式运行,资源占用小。
•加密密钥路由:使用了更先进的加密技术,具有前向加密和抗降级攻击的能力。
•内置漫游
•容器就绪:可以运行在主机中为容器之间提供通信,也可以运行在容器中为主机之间提供通信。
•能够将部分流量或所有流量通过 V** 传送到局域网内的任意主机。
•能够在网络故障恢复之后自动重连,这是相比其他 V** 优势的一点
•支持任何类型的二层网络通信,例如 ARP
、DHCP
和 ICMP
,而不仅仅是 TCP/HTTP。比如 V** Peer 之间可以互 ping
WireGuard 词汇表
Peer/Node/Device
Peer:对等节点。
连接到 V** 并为自己注册一个 V** 子网地址(如 10.4.1.3)的主机。还可以通过使用逗号分隔的 CIDR 指定子网范围,为其自身地址以外的 IP 地址选择路由。
中继服务器(Bounce Server)
本质上还是一个 peer,只是该 peer 公网可达,可以将流量中继到 NAT
后面(如:家里的电脑、NAS)的其他对等节点。Bounce Server
并不是特殊的节点,它和其他对等节点一样,唯一的区别是它有公网 IP,并且开启了内核级别的 IP 转发,可以将 V** 的流量转发到其他客户端。
子网(Subnet)
一组私有 IP,例如 10.4.1.1-255
或 192.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
域名服务器,用于将域名解析为 V** 客户端的 IP,不让 DNS请求泄漏到 V** 之外。
WireGuard 工作原理
中继服务器工作原理
中继服务器(Bounce Server)和普通的对等节点一样,它能够在 NAT
后面的 V** 客户端之间充当中继服务器,可以将收到的任何 V** 子网流量转发到正确的对等节点。事实上 WireGuard 并不关心流量是如何转发的,这个由系统内核和 iptables
规则处理。
如果所有的对等节点都是公网可达的,则不需要考虑中继服务器,只有当有对等节点位于 NAT 后面时才需要考虑。
在 WireGuard 里,客户端和服务端基本是平等的,差别只是谁主动连接谁而已。双方都会监听一个 UDP 端口,谁主动连接,谁就是客户端。主动连接的客户端需要指定对端的公网地址和端口,被动连接的服务端不需要指定其他对等节点的地址和端口。如果客户端和服务端都位于 NAT 后面,需要加一个中继服务器,客户端和服务端都指定中继服务器作为对等节点,它们的通信流量会先进入中继服务器,然后再转发到对端。
WireGuard 是支持漫游的,也就是说,双方不管谁的地址变动了,WireGuard 在看到对方从新地址说话的时候,就会记住它的新地址(跟 mosh 一样,不过是双向的)。所以双方要是一直保持在线,并且通信足够频繁的话(比如配置 persistent-keepalive
),两边的 IP 都不固定也不影响的。
Wireguard 如何路由流量
利用 WireGuard 可以组建非常复杂的网络拓扑[15],这里主要介绍几个典型的拓扑:
点对点(Point-to-point)
这是最简单的拓扑,所有的节点要么在同一个局域网,要么直接通过公网访问,这样 WireGuard
可以直接连接到对端,不需要中继跳转。
中心辐射型(Hub-and-spoke)
一端位于 NAT 后面,另一端直接通过公网暴露
这种情况下,最简单的方案是:通过公网暴露的一端作为服务端,另一端指定服务端的公网地址和端口,然后通过 persistent-keepalive
选项维持长连接,让 NAT 记得对应的映射关系。
两端都位于 NAT 后面,通过中继服务器连接
大多数情况下,当通信双方都在 NAT 后面的时候,NAT 会做源端口随机化处理,直接连接可能比较困难。可以加一个中继服务器,通信双方都将中继服务器作为对端,然后维持长连接,流量就会通过中继服务器进行转发。
小结
在 WireGuard 的世界里没有 Server 和 Client 之分,所有的节点都是 Peer
。大家使用 WireGuard 的常规做法是找一个节点作为中转节点,也就是 V** 网关,然后所有的节点都和这个网关进行连接,所有节点之间都通过这个网关来进行通信。这种架构中,为了方便理解,我们可以把网关看成 Server,其他的节点看成 Client,但实际上是不区分 Server 和 Client 的。架构示例如下图所示:
WireGuard Hub-and-spoke
这种架构的缺点相当明显:
•当 Peer 越来越多时,V** 网关就会变成垂直扩展的瓶颈。
•通过 V** 网关转发流量的成本很高,毕竟云服务器的流量很贵。
•通过 V** 网关转发流量会带来很高的延迟。
全网状网络(Full mesh)
✔️ 重点: 本次 Wireguard 后续实战的重点就是实现 Full mesh 网络。无论是 V** 网络还是 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 集群,作为其中的一个节点。
6....
还是很诱人的~