站点图标 Linux-技术共享

使用 WireGuard 无缝接入内网

NAS 放在家里,奈何囊肿羞涩,最终只能捡垃圾捡了个蜗牛星际,又因为种种原因吃了近一年的灰。最近又比较心血来潮,想起来了在角落中已经蒙尘的蜗牛星际。

在某宝上买的机子已经由卖家预装了黑群晖,用起来也是完全的傻瓜式操作,免去了自己折腾各种软件浪费时间。目前仅仅作为一个 samba 文件服务器,在局域网中共享一些视频或者其他资源。

 

OK,开始正题。现在需要支持在外面远程控制 NAS 下载一些东西。目前有这么几种方案:

考虑到后面可能会折腾好多东西(虽然目前就这一个),干脆将搞个 VPN 直接支持整个内网的访问吧,这样也不用一个端口一个端口地去映射了。但缺点也显而易见,访问内网就得连接 VPN,还是有那么一点点麻烦。

那么用哪个 VPN 方案呢?

先说 OpenVPN,这个安全性是毋庸置疑的,可以说是非常安全。但缺点是配置繁琐,而且性能可能不大好。

PPTP 和 L2TP 在大部分平台中都有内置,配置起来非常方便,性能也挺好的,但不是很安全。

WireGuard 是一个极其迅速而且简单的 VPN,它的目标是更快,更简单,更小,并且比 IPSec 更好用(来自 WireGuard 官网)。目前 WireGuard 已经合并进入了 Linux 内核。

可以看一下性能对比(来自 WireGuard 官网)

话不多说,开始安装吧。网络拓扑大致如下。

其中:

最终实现的效果是,电脑和手机通过连接 1.1.1.1 上的 VPN 服务,接入其他两个网段。

安装 WireGuard

WireGuard 不区分服务端与客户端,安装也比较简单,不出意外的话可以一行命令直接安装。

具体可参考 Installation - WireGuard,基本上涵盖了大部分 Linux 发行版,Windows 以及 MacOS,也包含了 Android, iOS 的安装包。

由于群晖不便于配置服务自启动(应该是我不会,没有 systemd 用的顺手),干脆在黑群晖中装个 Debian10 的虚拟机当作接入内网的网关(也就是上图中的 NAS)。

WireGuard 使用 wg 命令来进行控制,并且提供了一个 wg-quick 命令,可用来快速管理 VPN 连接。

生成密钥

1
2
3
4
5
6
7
8
9
# 生成私钥
$ wg genkey > priv_key
$ cat priv_key
qNzPeN726rM8L+JiSYeqm5zi6VwFlaa4OZhaCNPa4Uk=
# 生成公钥
$ wg pubkey < priv_key > pub_key # 使用私钥来生成公钥
$ cat pub_key
NtMtwyEB6tsQxOA5Z6xhuQkhwTFXeGPmbnirCA4lqgw=

WireGuard 的服务端与客户端是对等的。生成密钥的步骤适用于服务端,也适用于客户端。

服务器端配置

WireGuard 的配置文件一般情况下放在 /etc/wireguard 中,且为了安全考虑,需要将这个目录的所有者设置为 root,权限设置为 700

一般情况下,配置文件可依次命名为 wgX.conf,其中 X 为任意的编号,wgX 最终也是创建出来的网络接口的名称。

此处,在服务端与客户端中,都将命名为 wg0.conf

先来看一下最基础的配置文件 /etc/wireguard/wg0.conf

1
2
3
4
5
6
7
8
[Interface]
Address = 192.168.2.1/32
ListenPort = 28385
PrivateKey = Server private key
[Peer]
PublicKey = Client public key
AllowedIPs = IP Range

就是这么简单。分为两种配置组:

客户端配置

客户端的配置与服务端的配置基本一致。每个端都需要生成各自的密钥对。

下面的为 NAS 的配置文件 /etc/wireguard/wg0.conf

1
2
3
4
5
6
7
8
[Interface]
PrivateKey = Client private key
Address = 192.168.2.2/32
[Peer]
PublicKey = Server private key
AllowedIPs = IP Range
Endpoint = 1.1.1.1:28385

客户端与服务端不同的地方在于:

一般情况下,只有主动发起连接的端需要在 Peer 中指定 Endpoint,此处为服务器的公网 IP 和监听的端口。

基础的配置已经完成,现在可以直接使用 wg-quick up wg0 启动 VPN,其中 wg0 为配置文件的名称。

配置路由

通过上面的配置成功启动 VPN 后,即使连接成功,也不能访问 192.168.1.0/24 和 192.168.2.1/24 网段,这是因为截至目前,我们仅仅配置了 VPN 隧道,但路由还没有配置,发出去的 IP 包不知道应该被发往哪去。

按照预先设计的网络架构,在外面通往内网的流量大致流向为:

1
2
3
4
5
6
7
8
9
192.168.2.1(WireGuard 服务端) <------+ 192.168.2.X(客户端)
       +
       |
       v
  192.168.2.2(NAS)
       +
       |
       v
192.168.1.0/24(内网网段)

按照上图所示,我们需要:

上面的配置中的 AllowedIPs 可以被认为是路由规则的定义。意思是:

服务端

所以按照上面的包转发规则,可以得出各个客户端 Peer 的 AllowedIPs

配置 iptables

除了配置 AllowedIPs 外,在 Linux 中还需要配置 iptables

如果iptables 的 filter 表的 FORWARD 链的默认策略不是 ACCEPT 则还需要添加规则,使包可以被转发。

如果默认策略是 ACCEPT 则可以跳过这部分。

1
2
3
4
$ iptables -L filter -n -v
Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
...

虽然可以将 iptables 的默认策略改掉,但不推荐这样做。

关于 iptables 的教程可以参考 iptables | 朱双印博客

1
2
3
iptables -I FORWARD -s 192.168.2.0/24 -i wg0 -d 192.168.2.0/24 -j ACCEPT # 放行 VPN 网段
iptables -I FORWARD -s 192.168.2.0/24 -i wg0 -d 192.168.1.0/24 -j ACCEPT # 放行 VPN 转发到内网的流量
iptables -I FORWARD -s 192.168.1.0/24 -i wg0 -d 192.168.2.0/24 -j ACCEPT # 放行内网 转发到 VPN 的流量

在 WireGuard 的配置文件中,可以在 Interface 下指定多个 PostUp 和 PostDown,分别在 VPN 启动和停止后按照定义的顺序依次执行。

服务端配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Interface]
Address = 192.168.2.1/32
ListenPort = 28385
PrivateKey = Server private key
PostUp = iptables -I FORWARD -s 192.168.2.0/24 -i wg0 -d 192.168.2.0/24 -j ACCEPT
PostUp = iptables -I FORWARD -s 192.168.2.0/24 -i wg0 -d 192.168.1.0/24 -j ACCEPT
PostUP = iptables -I FORWARD -s 192.168.1.0/24 -i wg0 -d 192.168.2.0/24 -j ACCEPT
PostDown = iptables -D FORWARD -s 192.168.2.0/24 -i wg0 -d 192.168.2.0/24 -j ACCEPT
PostDown = iptables -D FORWARD -s 192.168.2.0/24 -i wg0 -d 192.168.1.0/24 -j ACCEPT
PostDown = iptables -D FORWARD -s 192.168.1.0/24 -i wg0 -d 192.168.2.0/24 -j ACCEPT
[Peer]
PublicKey = Client public key
AllowedIPs = IP Range

另外还需要开启 Linux 内核的 IP 包转发

1
2
$ sysctl net.ipv4.ip_forward
0

如果上面的输出为 0 则 Linux 内核默认会丢弃所有目的 IP 不为本机的包。

编辑 /etc/sysctl.conf, 将 net.ipv4.ip_forward = 0 中的 0 改为 1。然后 sysctl -p 使其生效。

NAS

NAS 中的 Peer 为服务端,则 AllowedIPs 应为 192.168.2.0/24

同时,NAS 需要负责将来自 192.168.2.0/24 网段的发往 192.168.1.0/24 的包进行 SNAT。

1
2
3
4
5
6
7
8
9
10
11
[Interface]
PrivateKey = Client private key
Address = 192.168.2.2/32
PostUp = iptables -t nat -A POSTROUTING -s 192.168.2.0/24 -j SNAT --to-source 192.168.1.2
PostDown = iptables -t nat -D POSTROUTING -s 192.168.2.0/24 -j SNAT --to-source 192.168.1.2
[Peer]
PublicKey = Server public key
AllowedIPs = 192.168.2.0/24
Endpoint = 1.1.1.1:28385

注意其中的 --to-source 192.168.1.2 的 IP 地址为 NAS 在内网中的 IP 地址。

同样,在 NAS 上,也需要确认已开启包转发。

总结

最终的配置文件如下

在各个机器上分别启动服务

1
wg-quick up wg0

通过 wg 命令可查看当前状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ wg # 服务端
interface: wg0
  public key: Public key
  private key: (hidden)
  listening port: 28385
peer: Client public key # NAS
  endpoint: x.x.x.x:41995
  allowed ips: 192.168.2.2/32, 192.168.1.0/24
  latest handshake: 36 seconds ago
  transfer: 132.48 KiB received, 49.25 KiB sent
peer: Client public key # 外网的电脑
  endpoint: x.x.x.x:60941
  allowed ips: 192.168.2.3/32
  latest handshake: 1 minute, 11 seconds ago
  transfer: 62.54 KiB received, 124.75 KiB sent
  
# ...

在外网尝试 ping 一下内网的主机

1
2
3
4
5
$ ping 192.168.1.1 # 路由器
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=63 time=113 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=63 time=90.10 ms
# ...

OK,完成,现在可以访问任意内网的机器了。

退出移动版