使用 WireGuard 搭建 VPN 访问家庭内网

不知道从什么时候开始,B 站博主和什么值得买的值友都开始纷纷带货 NAS 了。看了很多人推荐,于是自己也买了一台群晖 NAS 来玩玩。由于 NAS 部署在家中,出门的时候要访问文件就非常不方便。传统的做法是使用群晖的 QuickConnect 或者 DDNS。然而,前者(据说)速度很慢,后者将设备暴露在公网上,安全性又存在一些疑虑。因此我决定试一试组网工具 WireGuard。

使用 WireGuard 搭建 VPN 访问家庭内网
Photo by Steve Johnson / Unsplash

不知道从什么时候开始,B 站博主和什么值得买的值友都开始纷纷带货 NAS 了。看了很多人推荐,于是自己也买了一台群晖 NAS 来玩玩。由于 NAS 部署在家中,出门的时候要访问文件就非常不方便。传统的做法是使用群晖的 QuickConnect 或者 DDNS。然而,前者(据说)速度很慢,后者将设备暴露在公网上,安全性又存在一些疑虑,况且我家宽带没有公网 IP。因此,我决定试一试组网工具 WireGuard。

由于家里网络没有公网 IP,因此需要一台具有公网 IP 的服务器作为 WireGuard 网络的“server”。我这里使用的是一台 CentOS  8 服务器。同时,家中也需要有一台设备作为 WireGuard 网络中的节点,以将流量转发给 NAS。当然这台设备也可以是 NAS 本身,但为了简单起见,我使用的是 Mac。最后,我们将使用手机,在 4G 网络下检查 VPN 是否搭建成功。

IP 段的选择

WireGuard 组网需要使用一个不与你的任何设备的网络相冲突的 IP 地址段,你可以在这里查询到所有的保留地址段。像 192.0.2.0/24、198.51.100.0/24 这些分配为用于文档和示例中的“TEST-NET”,似乎是很好的选择,因为这些地址段通常不会被你需要连接的其他网络所使用。

在下面的配置中,我会分别将 192.0.2.1、192.0.2.2、192.0.2.3 分配给公网服务器、家中的 Mac 和 iPhone。

在服务器上配置 WireGuard

要使用 WireGuard,首先需要确保 Linux 内核支持。可使用 modinfo wireguard 命令检查是否内置了 WireGuard。也可用过 uname -r 检查内核版本是否为 5.6 以上。我这里版本还是 4.x,因此需要升级内核:

$ rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org

$ yum -y install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm

$ yum --disablerepo="*" --enablerepo="elrepo-kernel" list available

$ yum -y install --enablerepo=elrepo-kernel kernel-ml kernel-ml-devel kernel-ml-headers

# 查看安装之后的内核
$ rpm -qa | grep kernel

# 查看默认内核
$ grubby --default-kernel

# 如果默认内核不是最新的,可以使用如下命令更改
$ grubby --set-default /boot/vmlinuz-5.18.4-1.el8.elrepo.x86_64

# 重启生效
$ reboot

现在使用 modinfo wireguard 命令可以看到正常输出了:

$ modinfo wireguard
filename:       /lib/modules/5.18.4-1.el8.elrepo.x86_64/kernel/drivers/net/wireguard/wireguard.ko.xz
alias:          net-pf-16-proto-16-family-wireguard
alias:          rtnl-link-wireguard
version:        1.0.0
author:         Jason A. Donenfeld <Jason@zx2c4.com>
description:    WireGuard secure network tunnel
license:        GPL v2
srcversion:     746FF227790A0865A542034
depends:        udp_tunnel,curve25519-x86_64,libchacha20poly1305,ip6_udp_tunnel,libcurve25519-generic
retpoline:      Y
intree:         Y
name:           wireguard
vermagic:       5.18.4-1.el8.elrepo.x86_64 SMP preempt mod_unload modversions

然后安装 wireguard-tools:

$ yum install elrepo-release epel-release

$ yum install wireguard-tools

完成服务器端的配置

在服务器端配置文件中,我们需要设置监听的端口,以及所有需要连接到的 Peer 列表。在 WireGuard 中,每一个 Peer (即网络中的每一个设备)都需要一个公钥和私钥。在正确安装 wireguard-tools 后,你可以通过如下命令快速创建一组公钥和私钥。重复多次,以给你的所有设备完成创建:

$ wg genkey | tee peer_A.key | wg pubkey > peer_A.pub && cat peer_A.key && cat peer_A.pub

然后完成配置文件的创建,并保存到 /etc/wireguard/wg0.conf(默认配置文件位置):

[Interface]
PrivateKey = (your server private key here)
Address = 192.0.2.1/24
ListenPort = 51820

[Peer]
# Mac at home
PublicKey = (Mac public key here)
AllowedIPs = 192.0.2.2/32, 192.168.1.0/24

[Peer]
# iPhone
PublicKey = (iPhone public key here)
AllowedIPs = 192.0.2.3/32

在 WireGuard 中,你需要手动给各个设备分配 IP,并确保每个设备都有唯一的 IP。Interface 包含了当前设备的设置,对于“服务端”来说,ListenPort 是必须的。下面的每一个 Peer 段代表了能连接到本设备的一个其他设备。

AllowedIPs 是一个神奇的字段,我至今没有完全理解它的意思。根据网上的说法,这个字段有两个作用:

  • 它会创建一个到指定网络的路由,比如在上述的例子中,目标位置为 192.0.2.2/32 或 192.168.1.0/24 的包会通过 WireGuard 的虚拟网卡发送给第一个 Peer
  • 它会允许来自这个 Peer、且源地址为 192.0.2.2/32 或 192.168.1.0/24 的包被 WireGuard 的虚拟网卡路由。换句话说,来自这个 Peer、且源地址不为 192.0.2.2/32 或 192.168.1.0/24 的包将被丢弃

在完成配置文件的保存后,我们可以使用 wg-quick up wg0 来启用配置文件。wg-quick 会自动配置路由表,无需我们手动设置。

在完成上述所有操作后,还需进入云服务厂商的控制台,开放云主机的防火墙的  51820 端口。注意你的云主机可能也有软件防火墙,记得在这里也要开放相应的端口。

完成 Mac 端的配置

我们仿照服务器端的配置文件,完成 Mac 端的配置文件。由于我的 iPhone 是通过访问公网服务器来访问家庭内网的,并不直接连接家中的 Mac,因此这里的节点列表中并不一定需要添加 iPhone。

[Interface]
PrivateKey = (private key of Mac)
Address = 192.0.2.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = (public key of server)
AllowedIPs = 192.0.2.0/24
Endpoint = (server ip address):51820
PersistentKeepalive = 10

在这里,我们将公网服务器作为唯一的 Peer,通过设置 PersistentKeepalive 来进行连接的保活。这里 AllowedIPs 的作用是确保来自于我们 WireGuard 子网网段来的流量能被本机的 WireGuard 虚拟网卡进行处理,而并不是用于限制 Peer 可以访问本机所在的哪些网段,因此添加 192.168.1.0/24 是绝对错误的。WireGuard 似乎并不能限制 Peer 可以通过本机访问哪些网段,不过我承认我并没有细看它的安全模型,有机会再补补课吧。

在 Mac 上,我们可以下载官方的 GUI 客户端。然后将配置文件导入进来,并一键点击运行。通过这种方式我们可以免去设置防火墙等配置。

另外值得一提的是,在 Mac 上,WireGuard 是可以和 Surge 的网关模式兼容的。因此使用 Mac 作为网关的同学可不必担心兼容性问题。

完成 iPhone 端的配置

为了实现最佳网络体验,在 iPhone 上我也使用了 Surge,并通过 iCloud 实现配置的自动同步。Surge 目前已经支持了 WireGuard 作为代理策略,事实上这正是我决定试一试 WireGuard 的原因。毕竟如果连上家里内网的时候就连不上真正的互联网,那就太糟糕了。

我们在 Surge 的配置文件中添加如下内容:

[Proxy]
wireguard-iphone = wireguard, section-name = iPhone

[WireGuard iPhone]
private-key = (iPhone private key here)
self-ip = 192.0.2.3
dns-server = 223.5.5.5
mtu = 1280
peer = (public-key = (server public key here), allowed-ips = 0.0.0.0/0, endpoint = (server public ip here):51820)

另外我们希望在内网下不使用 WireGuard 连接 NAS,而在公网下使用 WireGuard 连接 NAS,因此新增如下 Surge Module,并按需开启:

#!name=WireGuard 内网访问
#!desc=开启后,可在非家中网络环境访问家庭内网

[Rule]
AND,((OR,((SUBNET,TYPE:CELLULAR), (NOT, ((OR, ((SUBNET,SSID:My 5G WiFi), (SUBNET,SSID:My 2.4G WiFi))))))), (IP-CIDR,192.168.1.0/24)),wireguard-iphone
IP-CIDR,192.0.2.0/24,wireguard-iphone

上述规则中括号非常多,看着很头疼,实际效果如下:

  • 当使用蜂窝网络,并访问 192.168.1.0/24 地址段时,使用名为 wireguard-iphone 的 Proxy
  • 当使用的 WiFi SSID 名称不是 My 5G WiFi 或 My 2.4G WiFi,并访问 192.168.1.0/24 地址段时,使用名为 wireguard-iphone 的 Proxy

通过上述配置,我成功在手机的 4G 网络环境中访问到了家中的 NAS。对 NAS 上部署的测速服务器进行访问,实际带宽大概为 6Mbps 左右,已经跑满了我的云服务器的带宽。这个速率基本上很难满足 1080p 串流需求,只能用于临时的小文件访问。毕竟国内的带宽还是太贵了。不过好消息是,经过上述的折腾,我们不仅可以访问家中的 NAS,也可以访问家中的任意网络服务,而无需额外配置。之后给家里安排上公网 IP 应该会舒服很多,到时候应该至少可以流畅串流 1080p 视频了。

更新:

通过找电信师傅,成功获取了公网 IP。现在 iPhone 通过 DDNS  直连家中 Mac 的 WireGuard 端口,测速可稳定跑满家中宽带的上传带宽 30Mbps 左右。这个速度可以流畅串流 1080p 视频,但对于 4K 视频来说比较困难。一部 20GB、2 小时的 4K  视频的码率为 2.84MB/s,转换成带宽为 22.7Mbps。而这个码率在 4K 电影中已经算非常低的了。如果考虑到协议损耗、上传带宽波动,能够从头开始播放不卡顿已经是万幸,想要实现随时跳转进度体验会不太好。