WireGuard 系列文章(四):WireGuard 快速上手

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

系列文章前情提要:

  1. WireGuard 系列文章(一):什么是 VPN
  2. WireGuard 系列文章(二):WireGuard 简介 - 快速、现代、安全的 VPN 隧道
  3. WireGuard 系列文章(三):WireGuard 安装

快速入门

您首先要确保对 概念概述 有很好的掌握,然后 安装 WireGuard。之后,请继续阅读此处。

配置实战

单个配置如下所示:

wg(8) 演练

命令行界面

可以通过 添加新接口,该接口应自动处理模块加载:ip-link(8)

1
# ip link add dev wg0 type wireguard

IP 地址和 peer 可以通过 ifconfig(8)ip-address(8)分配

1
# ip address add dev wg0 192.168.2.1/24

或者,如果总共只有两个 peer ,则可以:

1
# ip address add dev wg0 192.168.2.1 peer 192.168.2.2

该接口可以使用随附的 wg(8) 实用程序配置密钥和对等(peer)端点(endpoint):

1
# wg setconf wg0 myconfig.conf

1
# wg set wg0 listen-port 51820 private-key /path/to/private-key peer ABCDEF... allowed-ips 192.168.88.0/24 endpoint 209.202.254.14:8172

最后,可以使用 ifconfig(8)ip-link(8) 激活接口:

1
# ip link set up dev wg0

还有 wg showwg showconf 命令,用于查看当前配置。不带参数的 wg 调用 wg show 默认为对所有 WireGuard 接口进行调用。

wg(8) 工具

wg(8)ip(8)的许多例行的启动和拆卸动作都可以通过随附的 wg-quick(8)工具 实现自动化:

wg- 快速(8)工具

密钥生成

WireGuard 需要 base64 编码的公钥和私钥。这些可以使用 wg(8) 实用程序生成:

1
2
umask 077
wg genkey > privatekey

这将在 stdout 上创建包含新私钥 privatekey的内容。

然后,您可以从私钥派生公钥:

1
wg pubkey < privatekey > publickey

这将从 stdin 读取 privatekey 并将相应的公钥 publickey 写入 stdout。

当然,您可以一次完成所有操作:

1
wg genkey | tee privatekey | wg pubkey > publickey

NAT 和防火墙遍历持久性

默认情况下,WireGuard 在不使用时会尝试尽可能保持静默;这不是一个健谈的协议。在大多数情况下,它仅在对等方希望发送数据包时传输数据。当系统未要求它发送数据包时,它会停止发送数据包,直到再次被请求。在大多数配置中,这运行良好。但是,当对等方位于 NAT 或防火墙后面时,它可能希望能够接收传入的数据包,即使它不发送任何数据包。由于 NAT 和有状态防火墙会跟踪 " 连接 ",因此,如果 NAT 或防火墙后面的对等方希望接收传入的数据包,他必须通过定期发送保持活动数据包来保持 NAT/ 防火墙映射有效。这称为 持久保持活动( persistent keepalives)。启用此选项后 *,每隔几秒钟 * 就会向服务器终结点(endpoint)发送一次保持活动状态的数据包。适用于各种防火墙的合理间隔为 25 秒。将其设置为 0 会关闭该功能,这是默认设置,因为大多数用户不需要此功能,并且会使 WireGuard 稍微更健谈。可以通过将字段 PersistentKeepalive = 添加到配置文件中的对等项或在命令行中进行设置 persistent-keepalive来指定此功能。如果不需要此功能,请不要启用它。但是,如果您位于 NAT 或防火墙后面,并且希望在网络流量静默很久之后接收传入连接,则此选项将使 " 连接 " 在 NAT 眼中保持打开状态。

配置详解

WireGuard 使用 INI 语法作为其配置文件格式。配置文件可以放在任何路径下,但必须通过绝对路径引用。默认路径是 /etc/wireguard/wg0.conf

配置文件的命名形式必须为 ${WireGuard 接口的名称 }.conf。通常情况下 WireGuard 接口名称以 wg 为前缀,并从 0 开始编号,但你也可以使用其他名称,只要符合正则表达式 ^[a-zA-Z0-9_=+.-]{1,15}$ 就行。

你可以选择使用 wg 命令来手动配置 VPN,但一般建议使用 wg-quick,它提供了更强大和用户友好的配置体验,可以通过配置文件来管理配置。

下面是一个配置文件示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Interface]
# Name = peer1.private
Address = 10.4.1.3/32
ListenPort = 51820
PrivateKey = thisPeerPrivateKeyXXXXXXXX=
DNS = 223.5.5.5
MTU = 1500
PreUp = echo WireGuard PreUp
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PreDown = echo WireGuard PreDown
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# Name = peer2.private
AllowedIPs = 10.4.1.1/24
Endpoint = peer1.private:51820
PublicKey = remotePeerPublicKeyXXXX=
PersistentKeepalive = 25

✔️ 实用配置

PostUp 和 PostDown 可以按照上文进行配置:

PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

当然,目前的体验最好的实践是傻瓜式地通过 Netmaker 完成所有配置。

[Interface]

这一节定义本地 VPN 配置。例如:

  • 如果本地节点是客户端,只需路由自身的流量,那就只暴露一个 IP(作为客户端嘛,就不需指定端口)。

    1
    2
    3
    4
    [Interface]
    # Name = phone.private
    Address = 10.4.1.5/32
    PrivateKey = <private key for phone.private>
  • 如果本地节点是中继服务器,它需要将流量转发到其他对等节点(peer),并公开整个 VPN 子网的路由。

    1
    2
    3
    4
    5
    6
    [Interface]
    # Name = public-server1.example-vpn.tld
    Address = 10.4.1.1/24
    ListenPort = 51820
    PrivateKey = <private key for public-server1.example-vpn.tld>
    DNS = 223.5.5.5

Address

定义本地节点应该对哪个地址范围进行路由。

  1. 如果是常规的客户端,则将其设置为节点本身的单个 IP(使用 CIDR 指定,例如 10.4.1.3/32);
  2. 如果是中继服务器,则将其设置为可路由的子网范围。

例如:

  • 常规客户端,只路由自身的流量:Address = 10.4.1.3/32
  • 中继服务器,可以将流量转发到其他对等节点(peer):Address = 10.4.1.1/24 (注意是 /24, 即整个子网)
  • 也可以指定多个子网或 IPv6 子网:Address = 10.4.1.1/24,2001:DB8::/64

ListenPort

当本地节点是中继服务器时,需要通过该参数指定端口来监听传入 VPN 连接,默认端口号是 51820。常规客户端(只作为客户端,不作为服务器端)则不需要此选项。

PrivateKey

本地节点的私钥,所有节点(包括中继服务器)都必须设置。不可与其他服务器共用。

DNS

通过 DHCP 向客户端宣告 DNS 服务器。客户端将会使用这里指定的 DNS 服务器来处理 VPN 子网中的 DNS 请求,但也可以在系统中覆盖此选项。例如:

  • 如果不配置则使用系统默认 DNS
  • 可以指定单个 DNS:DNS = 223.5.5.5
  • 也可以指定多个 DNS:DNS = 223.5.5.5,223.6.6.6

MTU

定义连接到对等节点(peer)的 MTU(Maximum Transmission Unit,最大传输单元),默认不需要设置,一般由系统自动确定。

PreUp

启动 VPN 接口之前运行的命令。这个选项可以指定多次,按顺序执行。

例如:

  • 添加路由:PreUp = ip rule add ipproto tcp dport 22 table 1234

PostUp

启动 VPN 接口之后运行的命令。这个选项可以指定多次,按顺序执行。

例如:

  • 从文件或某个命令的输出中读取配置值:

    1
    PostUp = wg set %i private-key /etc/wireguard/wg0.key <(some command here)
  • 添加一行日志到文件中:

    1
    PostUp = echo "$(date +%s) WireGuard Started" >> /var/log/wireguard.log
  • 调用 WebHook:

    1
    PostUp = curl https://events.example.dev/wireguard/started/?key=abcdefg
  • 添加路由:

    1
    PostUp = ip rule add ipproto tcp dport 22 table 1234
  • 添加 iptables 规则,启用数据包转发:

    1
    PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
  • 强制 WireGuard 重新解析对端域名的 IP 地址:

    1
    PostUp = resolvectl domain %i "~."; resolvectl dns %i 10.4.1.1; resolvectl dnssec %i yes

PreDown

停止 VPN 接口之前运行的命令。这个选项可以指定多次,按顺序执行。

例如:

  • 添加一行日志到文件中:

    1
    PreDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log
  • 调用 WebHook:

    1
    PreDown = curl https://events.example.dev/wireguard/stopping/?key=abcdefg

PostDown

停止 VPN 接口之后运行的命令。这个选项可以指定多次,按顺序执行。

例如:

  • 添加一行日志到文件中:

    1
    PostDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log
  • 调用 WebHook:

    1
    PostDown = curl https://events.example.dev/wireguard/stopping/?key=abcdefg
  • 删除 iptables 规则,关闭数据包转发:

    1
    PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]

定义能够为一个或多个地址路由流量的对等节点(peer)的 VPN 设置。对等节点(peer)可以是将流量转发到其他对等节点(peer)的中继服务器,也可以是通过公网或内网直连的客户端。

中继服务器必须将所有的客户端定义为对等节点(peer),除了中继服务器之外,其他客户端都不能将位于 NAT 后面的节点定义为对等节点(peer),因为路由不可达。对于那些只为自己路由流量的客户端,只需将中继服务器作为对等节点(peer),以及其他需要直接访问的节点。

举个例子,在下面的配置中,public-server1 作为中继服务器,其他的客户端有的是直连,有的位于 NAT 后面:

  • public-server1(中继服务器)

    [peer] : public-server2, home-server, laptop, phone

  • public-server2(直连客户端)

    [peer] : public-server1

  • home-server(客户端位于 NAT 后面)

    [peer] : public-server1, public-server2

  • laptop(客户端位于 NAT 后面)

    [peer] : public-server1, public-server2

  • phone(客户端位于 NAT 后面)

    [peer] : public-server1, public-server2

配置示例:

  • 对等节点(peer)是路由可达的客户端,只为自己路由流量

    1
    2
    3
    4
    5
    [Peer]
    # Name = public-server2.private
    Endpoint = public-server2.private:51820
    PublicKey = <public key for public-server2.private>
    AllowedIPs = 10.4.1.2/32
  • 对等节点(peer)是位于 NAT 后面的客户端,只为自己路由流量

    1
    2
    3
    4
    5
    [Peer]
    # Name = home-server.private
    Endpoint = home-server.private:51820
    PublicKey = <public key for home-server.private>
    AllowedIPs = 10.4.1.3/32
  • 对等节点(peer)是中继服务器,用来将流量转发到其他对等节点(peer)

    1
    2
    3
    4
    5
    6
    7
    [Peer]
    # Name = public-server1.example-vpn.tld
    Endpoint = public-server1.example-vpn.tld:51820
    PublicKey = <public key for public-server1.example-vpn.tld>
    # 路由整个 VPN 子网的流量
    AllowedIPs = 10.4.1.1/24
    PersistentKeepalive = 25

Endpoint

指定远端对等节点(peer)的公网地址。如果对等节点(peer)位于 NAT 后面或者没有稳定的公网访问地址,就忽略这个字段。通常只需要指定 中继服务器 Endpoint,当然有稳定公网 IP 的节点也可以指定。例如:

  • 通过 IP 指定:

    1
    Endpoint = 123.123.123.123:51820
  • 通过域名指定:

    1
    Endpoint = public-server1.private:51820

AllowedIPs

允许该对等节点(peer)发送过来的 VPN 流量中的源地址范围。同时这个字段也会作为本机路由表中 wg0 绑定的 IP 地址范围。如果对等节点(peer)是常规的客户端,则将其设置为节点本身的单个 IP;如果对等节点(peer)是中继服务器,则将其设置为可路由的子网范围。可以使用 , 来指定多个 IP 或子网范围。该字段也可以指定多次。

当决定如何对一个数据包进行路由时,系统首先会选择最具体的路由,如果不匹配再选择更宽泛的路由。例如,对于一个发往 10.4.1.3 的数据包,系统首先会寻找地址为 10.4.1.3/32 的对等节点(peer),如果没有再寻找地址为 10.4.1.1/24 的对等节点(peer),以此类推。

例如:

  • 对等节点(peer)是常规客户端,只路由自身的流量:

    1
    AllowedIPs = 10.4.1.3/32
  • 对等节点(peer)是中继服务器,可以将流量转发到其他对等节点(peer):

    1
    AllowedIPs = 10.4.1.1/24
  • 对等节点(peer)是中继服务器,可以转发所有的流量,包括外网流量和 VPN 流量:

    1
    AllowedIPs = 0.0.0.0/0,::/0
  • 对等节点(peer)是中继服务器,可以路由其自身和其他对等节点(peer)的流量:

    1
    AllowedIPs = 10.4.1.3/32,10.4.1.4/32
  • 对等节点(peer)是中继服务器,可以路由其自身的流量和它所在的内网的流量:

    1
    AllowedIPs = 10.4.1.3/32,192.168.1.1/24

PublicKey

对等节点(peer)的公钥,所有节点(包括中继服务器)都必须设置。可与其他对等节点(peer)共用同一个公钥。

公钥可通过命令 wg pubkey < example.key > example.key.pub 来生成,其中 example.key 是上面生成的私钥。

例如:PublicKey = somePublicKeyAbcdAbcdAbcdAbcd=

PersistentKeepalive

如果连接是从一个位于 NAT 后面的对等节点(peer)到一个公网可达的对等节点(peer),那么 NAT 后面的对等节点(peer)必须定期发送一个出站 ping 包来检查连通性,如果 IP 有变化,就会自动更新Endpoint

例如:

  • 本地节点与对等节点(peer)可直连:该字段不需要指定,因为不需要连接检查。
  • 对等节点(peer)位于 NAT 后面:该字段不需要指定,因为维持连接是客户端(连接的发起方)的责任。
  • 本地节点位于 NAT 后面,对等节点(peer)公网可达:需要指定该字段 PersistentKeepalive = 25,表示每隔 25 秒发送一次 ping 来检查连接。

参考资料