每次配 Wireguard 总是要去网上找教程,用得多了不如就自己记录一下。

前言

Wireguard 是一个新兴的开源 VPN 工具,具有速度快、性能高、配置简单、使用安全的优势,目前已经加入 Linux 内核 (>= 5.6) 代码中。

通过 Wireguard 可以将广域网上的主机连接起来,形成一个局域网。假设我们现在有一台云服务器(具有固定的公网 IP),就可以将其作为我们搭建的局域网的中心节点,让其他的主机(不论是否有公网 IP,不论是否在 NAT 内),都通过这个中心节点和彼此相连。由此我们就构建了一个中心辐射型的局域网,实现了内网穿透等功能。

本文将讲解如何使用 Wireguard 搭建中心辐射型拓扑的网络。

Wireguard 安装

Wireguard 最低支持 3.10 版本的 Linux 内核。

安装 Wireguard 需要分情况。如果系统的内核版本较高,那么可能已经内置了 Wireguard,安装过程会比较简单。但对于内核版本较低的系统,安装过程可能会比较复杂,一般有两种解决方法。

  1. 自行编译安装内核模块
  2. 使用 Wireguard-go 替代

要查看系统的内核对 Wireguard 的支持性,可以使用:

lsmod | grep wireguard

查看输出,如果有结果就是已经内置支持了。

内核已经内置支持 (>= 5.6)

妥妥的一等公民,可以直接安装 wireguard-tools。wireguard-tools 是 Wireguard 内核模块的配套工具。

具体方法直接参考官网:Installation - WireGuard

自行编译安装内核模块

我们的思路是:首先安装编译工具链,然后拉取 wireguard-linux-compat 和 wireguard-tools 的代码,先编译安装内核模块,然后再编译并安装 配套的 wireguard-tools。

其中 wireguard-linux-compat 是一个最低兼容 3.10 版本内核的项目,可以通过它将 Wireguard 安装为内核的 external module,而不必升级内核。

官网给出了详细教程,并号称它是一个 fairly simple procedure (其实并不)Compilation from Source Code - WireGuard

注意:这个教程在 Step 1 - 4 之后还有两个部分,一般情况下是不用操作的。

是的,按照这个教程操作需要有一些编译内核模块的经验,可能有点难。

使用 Wireguard-go 替代

这种方案放弃了对内核进行操作,直接使用一个 Golang 写的程序和 User Space 机制替代了内核模块完成流量转发的工作。由于不是由内核进行操作,所以性能会有下降,不过得益于 Golang 出色的性能表现,对于轻度使用足够。

官方页面:wireguard-go - Go implementation of WireGuard (zx2c4.com)

Tips:Windows 上的 Wireguard 客户端就是集成了它。

官网只提供了源码和简单的使用教程,下面我们直接获取二进制文件并把它和 wireguard-tools 结合起来,实现媲美内核模块版 Wireguard 的功能。

  1. P3TERX/wireguard-go-builder 的 Release 页面下载预编译的二进制文件。这个项目虽然不是由官方维护的,但会通过 Actions 自动同步 Wireguard-go 的源码并编译发布。不想偷懒的同学可以选择自己按照官网的教程编译自己的二进制文件。
  2. 将二进制文件解压出来,赋予执行权限,然后拷贝到 /usr/bin/ 目录下(或是任何其他在 $PATH 内的目录下)。

    tar -xzf wireguard-go-linux-amd64.tar.gz # 以 amd64 架构为例
    chmod a+x wireguard-go
    mv wireguard-go /usr/bin
  3. 安装 wireguard-tools,这里可以尝试直接使用包管理器安装。

    sudo apt install wireguard-tools # 这里以 debian 为例,其他系统请自行修改

    如果包管理器无法安装可以尝试自行编译安装 wireguard-tools,教程在“自行编译安装内核模块”部分的链接中的 Step 4。

  4. 测试安装。

    wg --version # 正常会显示 wireguard-tools 的版本信息
    wg-quick -h  # 正常会显示 wg-quick 的 Usage 提示。

其他安装方法

这里我还看到了几种其他的安装方案。

Wireguard 配置

说了这么多其实才是把软件安装好,配置网络又是另外一回事。

首先介绍基本概念。Wireguard 使用 INI 的语法配置网络,配置文件默认放在 /etc/wireguard/ 下,一般文件名为 wg0.confwg1.conf……表示该配置文件创建的 Wireguard 网卡(interface)的名称为 wg0wg1……

具体配置文件的内容会在下面的实例中讲解,由于我们只配置一个最简单的中心辐射型拓扑,涉及的配置项有限,具体请参考手册。

中心节点配置

这里的中心节点,也常常被人称为”服务端“,因为它作为整个网络的中心,其他节点的流量都要通过它,好像一个向众多用户提供服务的服务器。不过需要注意的是,在 Wireguard 中,所有节点都叫做“Peer”,意即他们是对等的,并不是传统的 C-S(Client-Server)模型,不论是“服务端”还是“客户端”,Wireguard 都是一视同仁的,他们之间只存在配置文件的不同而已。

下面开始编写配置文件:

  1. 进入存放配置文件的路径并修改权限:

    cd /etc/wireguard/ # 配置文件所在路径依照默认
    umask 077          # 赋予目录下所有文件 600 权限,保证安全
  2. 生成公钥和私钥并查看:

    wg genkey | tee wg0 | wg pubkey > wg0.pub
    cat wg0       # 查看并记下私钥,配置本节点需要
    cat wg0.pub   # 查看并记下公钥,配置其他节点需要

    简单注释一下:

    • wg genkey 可以生成私钥,wg pubkey 接收私钥,输出对应的公钥;
    • tee 从 STDIN 读取输入并输出到指定的文件中,同时在 STDOUT 上打印出来;
    • 配合管道和重定向,上面的命令就把公钥和私钥分别保存到了 wg0wg0.pub 中。
  3. 创建配置文件:

    vim /etc/wireguard/wg0.conf  # 这里以创建网卡 wg0 为例
  4. 在配置文件中输入:

    [Interface]
    Address = 192.168.50.1/32
    PrivateKey = <SERVER's PRIVATE KEY>
    ListenPort = <SERVER's LISTEN PORT>
    PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o <SOME NET INTERFACE> -j MASQUERADE
    PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o <SOME NET INTERFACE> -j MASQUERADE
    
    [Peer]
    PublicKey = <PEER 1's PUBLIC KEY>
    AllowedIPs = 192.168.50.11/32

    注意 <> 内的内容需要根据实际情况替换。

    整个配置文件分为两部分,[Interface] 部分配置本机的网络接口(网卡),而 [Peer] 部分配置可以与本机连接的节点。

    [Interface] 部分:

    • Address 是当前配置的 Wireguard 网卡在组建的局域网中的地址,遵循 CIDR 表示法。/24 表示了子网大小,具体含义请自行查询。
    • PrivateKey 是当前网卡的私钥。其他节点发往当前节点的流量都会用该私钥对应的公钥加密,本地网卡收到后使用私钥解密。
    • ListenPort 是当前网卡监听的端口。该端口收到的 UDP 流量会被 Wireguard 处理,该网卡也使用这个端口向外发送数据,所以需要确保这个端口没有被防火墙拦截或者被其他程序占用。
    • PostUp 是当前网卡启动后进行的操作,可以是任何 Linux 命令,多条指令用 ; 分隔。这里我们写了三条:

      • iptables -A FORWARD -i %i -j ACCEPT 在 iptables 的 filter 表 FORWARD 链上追加一条规则,对来自 %i(即 wg0 网卡)的数据包采用 ACCEPT 的策略。
      • iptables -A FORWARD -o %i -j ACCEPT 在 iptables 的 filter 表 FORWARD 链上追加一条规则,对去向 %i(即 wg0 网卡)的数据包采用 ACCEPT 的策略。
      • iptables -t nat -A POSTROUTING -o <SOME NET INTERFACE> -j MASQUERADE 在 iptables 的 nat 表 POSTROUTING 链上追加一条规则,对去向 某网卡 的数据包采用 MASQUERADE 的策略。MASQUERADE 是一种特殊的 SNAT(基于源地址的网络地址转换),在这个例子中的作用为,将发出的数据包的源地址改为 某网卡 的 IP 地址。

        需要注意的是,这里的 某网卡 一般要填写连接公网的网卡,例如 eth0。具体的原理解释可参考:使用iptables配置NAT - 知乎 (zhihu.com)

    • PostDown 是当前网卡关闭后进行的操作,和上面的 PostUp 概念类似。上述例子中的指令就是将 PostUp 中添加的规则删除,不再赘述。

    [Peer] 部分:

    • PublicKey 是 Peer 节点的公钥。当前节点向 Peer 节点发送数据时会使用对方的公钥进行加密,对方收到后会使用自己的私钥解密。现在我们还没有配置 Peer 节点,可以暂时留空,稍后回来补充。
    • AllowedIPs 有两个功能:

      • 首先,它会为本机添加路由规则,本机匹配 AllowedIPs 的数据包会被路由到这个 Peer。
      • 其次,来源于这个 Peer 的数据包的源地址必须在 AllowedIPs 中,否则本机的 Wireguard 会忽略这些数据包。
      • 举个例子,AllowedIPs = 192.168.50.11/32,即只允许这个 Peer 发给我源地址是 192.168.50.11/32 的数据包,同时我这边收到的数据包如果有目的地址是 192.168.50.11/32,也会被我发给这个 Peer。

        可参考:What does WireGuard AllowedIPs actually do? – TechOverflow

  5. 启用中继节点的转发:

    echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
    echo "net.ipv4.conf.all.proxy_arp = 1" >> /etc/sysctl.conf
    sysctl -p /etc/sysctl.conf

其他节点配置

其他节点可以看作传统 C-S 模型中的客户端。生成密钥对、创建配置文件的过程和中心节点完全相同,注意中心节点和其他节点的配置文件名 不必相同。下面之间开始编写配置文件:

[Interface]
Address = 192.168.50.5/32
PrivateKey = <PEER 1's PRIVATE KEY>

[Peer]
PublicKey = <SERVER's PUBLIC KEY>
AllowedIPs = 192.168.50.0/24
Endpoint = <SERVER's IP>:<SERVER's LISTEN PORT>
PersistentKeepalive = 25

需要注意的是:

  • 这次我们填的 Peer 正是上面刚刚配置的中心节点,我们的 PrivateKeyPublicKey 要根据情况替换。
  • 这次的 Peer 节点配置相对复杂。

    • ENDPOINT :该 Peer 的公网地址。想象现在有两个人试图进行通信,如果彼此都不知道地址是无法建立连接的。至少要有一方知道另一方的地址才能发起连接,由于我们的中心节点具有稳定的公网 IP/域名(比如云服务器),所以我们在这里显式指定了 Peer 的地址。对于中心节点而言,其他节点的地址都可以等到其他节点主动建立连接时自动获取,无需指定。
    • PersistentKeepalive 是保活间隔,此处例子中每隔 25s 该节点与中心节点主动建立一次连接,使得中心节点能够即时掌握该节点的地址情况,维持连接,对于处于 NAT 后的节点非常重要。
    • AllowedIPs 中我们设置了整个网段,即本机在虚拟网络中的所有通信都要走这个 Peer(中心节点)。

编写完这个节点的配置文件,不要忘记把当前节点的公钥补充到中心节点的配置文件中。后续我们再添加新的节点时,操作步骤是基本相同的,只需要在中心节点的配置文件中追加新的 [Peer] 配置即可。

启动 Wireguard

wireguard-tools 提供了 wg-quick 工具,可以快速部署 Wireguard。

启动 /etc/wireguard/wg0.conf:

wg-quick up wg0

关闭 /etc/wireguard/wg0.conf

wg-quick down wg0

如果配置文件不在默认的 /etc/wireguard/ 下,需要输入完整的路径。

启动完毕后可以使用 wg 工具查看当前的连接情况,详情请参考:wg(8) — wireguard-tools — Debian unstable — Debian Manpages

疑难杂症

  1. 各种防火墙导致的网络不通。由于 Linux 上存在除了 iptables 之外的多种防火墙,例如 firewalld、ufw 等等,规则混乱使得我们的网络不通。解决方法是,将配置中 PostUp 里的 iptables 参数从 -A 改成 -I。即把放通的规则插入到匹配的最前面,由于放通的规则靠前,就使得后面其他的防火墙规则失效了。
  2. ……

参考资料

  1. Some Unofficial WireGuard Documentation - HedgeDoc (sweeting.me)
  2. WireGuard 教程:WireGuard 的搭建使用与配置详解 – 云原生实验室 - Kubernetes|Docker|Istio|Envoy|Hugo|Golang|云原生 (fuckcloudnative.io)