Featured image of post 基于Wireguard安全访问家庭内网

基于Wireguard安全访问家庭内网

目标

经常在外或公司想访问一下家里内部的一些服务,如查看照片或文件。甚至拿起手机输入像192.168.1.1就可以访问到家里路由网页。直接暴露服务对外是不太安全的。在此之前我已经有一套相对安全的OAuth方案了,不过也心心念想试一下传统的如OpenVPN等方案,刚好Wireguard号称更艺术的VPN解决方案,于是一下能否完美解决这个场景。

准备

安装wireguard和依赖,我以ubuntu为例:

1
apt install resolvconf wireguard

配置

虽然wireguard是强调的对等网络,本身没有server/client之分,而我们这个应用场景,为了描述方便,我会区分server/client。

生成key

不需要特别对等,把家里的一台机器作为接入服务器,所有流量先到它,然后通过它转给内网。其它外网的腾讯云或公司笔记本等都主要是Client角色。

生成服务端key

用于配置家庭内网的一台机器wireguard。

1
2
3
mkdir wireguard
wg genkey > server.key
wg pubkey < server.key > server.key.pub

现在还不能直接配置它,我们还需要client的key,才能构建起一条隧道。

生成客户端key

我暂时以腾讯云上的一台CVM机器作为客户端。

1
2
3
mkdir wireguard
wg genkey > client.key
wg pubkey < client.key > client.key.pub

配置wireguard

服务端配置

如上述,服务端我指定10.10.10.10这个IP作为虚拟局域网IP,创建一个配置文件,随便取个名字如:wg-server.conf,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[Interface]
# 用上述server.key内容代替
PrivateKey = `cat server.key` 
Address = 10.10.10.10/32

# 让服务端监听在51820
ListenPort = 51820  

# 定义一些转发规则
PostUp   = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostUp = sysctl -w net.ipv4.ip_forward=1
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
# 用上述client.key.pub内容代替
PublicKey = `cat client.key.pub`
AllowedIPs = 10.10.10.0/24
PersistentKeepalive = 25

简单解释一下,在wireguard中,通过双方定义的密钥等加解密,并且对于来源的ip等都有一些限制。这篇文章有部分讲解wireguard原理

WireGuard 的核心部分是加密密钥路由(Cryptokey Routing),它的工作原理是将公钥和 IP 地址列表(AllowedIPs)关联起来。每一个网络接口都有一个私钥和一个 Peer 列表,每一个 Peer 都有一个公钥和 IP 地址列表。发送数据时,可以把 IP 地址列表看成路由表;接收数据时,可以把 IP 地址列表看成访问控制列表。

上述我们使用ListenPort来指定监听端口,一般这种情况是让wireguard具备中继能力,固定了端口,而如果不定义会是随机端口(可通过wg show all查看到)。

接着定义了PostUp/PostDown等用于设置一些东西。这是很关键的一点是,为了让这个服务能将消息转给我们内网,使用iptables的nat能力转给eth0(服务器的内网IP所在)设备,这样即达到消息转给内网。

然后将wireguard启用,可以直接用wg命令配置,不过这里推荐使用wg-quick简化操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 将上述创建的配置文件拷贝到默认目录 
kevin@ingress:~/wireguard$  sudo cp wg-server.conf /etc/wireguard/wg0.conf

# wg-quick,也可以传wg0.conf路径,这里用默认
kevin@ingress:~/wireguard$ wg-quick up wg0 
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.10.10.10/32 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.10.10.0/24 dev wg0
[#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
[#] sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

配置客户端

客户端的配置就是指定某些IP段消息转给我们内网服务器,你可以直接编辑: /etc/wireguard/wg0.conf,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[Interface]
# 上面生成的客户端私有key
PrivateKey = `cat client.key`
Address = 10.10.10.20/32  # 随便给客户端一个vip
# 如果你内网有DNS服务器提供内部域名解析,这里可以指定它的IP
DNS = 192.168.10.53  

[Peer]
# 使用上述在服务端生成的公共key
PublicKey = `cat server.key.pub`
# 这里要注意,除了wireguard所定义的10.10.10.0/24网段外,我们也加上内网IP段。
AllowedIPs = 10.10.10.0/24,192.168.10.0/24
PersistentKeepalive = 25
# 这里要注意,这个是我们内网服务器的IP(可用域名)和端口
# 视你内网服务器所在机器,有可能需要在路由上做一次端口转发
Endpoint = `domain-of-your-home-or-ip`:51820

以上配置按注释所描述修改后,我们再使用wg-quick起来。

1
2
3
4
5
6
7
ubuntu@tcloud:~/wireguard$ wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.10.10.20/32 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 192.168.10.0/24 dev wg0
[#] ip -4 route add 10.10.10.0/24 dev wg0

可以看到wg-quick帮我们做的事情,添加了几条ip route规则,将我们定义的网段通过wg0发出。

测试

基础联通性

现在我们先查看client那台的ip address信息,使用ip a即可。也测试ping内网服务器IP:10.10.10.10,发现是联通的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
ubuntu@tcloud:~/wireguard$ ip a
...
11: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 10.10.10.20/32 scope global wg0
       valid_lft forever preferred_lft forever
ubuntu@tcloud:~/wireguard$ ping 10.10.10.10
PING 10.10.10.10 (10.10.10.10) 56(84) bytes of data.
64 bytes from 10.10.10.10: icmp_seq=1 ttl=64 time=9.71 ms
64 bytes from 10.10.10.10: icmp_seq=2 ttl=64 time=9.78 ms
64 bytes from 10.10.10.10: icmp_seq=3 ttl=64 time=9.45 ms
^C
--- 10.10.10.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 9.449/9.648/9.783/0.143 ms

内网转发测试

继续ping一个内网的其它机器:

1
2
3
4
5
6
7
8
9
ubuntu@tcloud:~/wireguard$ ping 192.168.10.1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=63 time=12.3 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=63 time=11.7 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=63 time=11.7 ms
^C
--- 192.168.10.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 11.680/11.897/12.300/0.285 ms

甚至curl一个web服务,也是正常返回的。

1
2
3
ubuntu@tcloud:~/wireguard$ curl -kI https://192.168.10.57
HTTP/2 200
...

如果上面你配置了内网DNS,也可以用内部域名访问了。

1
2
3
ubuntu@mercedes:~/wireguard$ curl -Ik https://nas.home
HTTP/2 200
...

现在我们的这台外网的client已经和家庭内网几乎一样的使用体验了。

移动端配置

我们可以下载Wireguard的移动客户端,Android或iOS都有安装包,然后像上面一样定义一个client给这个客户端分配上。记得在内网服务器上添加上这个client为peer。

然后如果你不想在手机输入那么多客户端的接入信息,那么可以借助qrencode来生成一个配置的二维码。

1
apt install -y qrencode

如果你为你的手机创建了一个client相关的wg配置,比如名称为wg8.conf,内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Interface]
PrivateKey = ANzJpCQbtitEQfc4D7U28sfAso9SZmeUgMQQgiAeUUE=
Address = 10.10.10.30/32
DNS = 192.168.10.53 # 可选,没DNS则不写

[Peer]
PublicKey = `cat server.key.pub`
AllowedIPs = 10.10.10.0/24,192.168.10.0/24
PersistentKeepalive = 25
Endpoint = `domain-of-your-home-or-ip`:51820

以上仅示例,有一些东西替换为你自己的内容。

然后使用:

1
qrencode -t ansiutf8 -l L < wg8.conf

即可看到终端生成了大大的二维码,赶快扫码连通你家内网吧~~

结语

至此基本的场景是满足了,已经可以随时开心切换到连接家庭内网模式。不过略有点不爽的是:

  1. 真Peer-to-Peer了。内网NAT下,还利用了端口映射,希望走中继模式下,免除这一步。
  2. 配置还是比较复杂,后续添加新的节点也有几步,进一步简化。

后面再研究如何优化一下,暂时就整到这吧~

参考资料