Theme NexT works best with JavaScript enabled

学习飞翔的企鹅

What's the point in living if I have to hide ?

0%

使用 Zerotier 进行 p2p 连接

​ 在一些场景下需要在两台不同内网中的主机间建立连接。例如在家里放了一台常开的电脑,电脑处于家中内网中,平时我们在外面无法直接访问。此时有两种解决方案。第一种是利用Sakura Frp等内网端口映射的工具,但是如果通信流量较大(比如在家里电脑上开网站、代理等),就会被内网映射服务的限速、限额影响。第二种方案就是进行p2p连接,公网服务器只用于协助建立两台主机的连接,建立完连接后两台主机之间直接通信,而不用再通过公网服务器。Zerotier就提供这样的服务,并且提供一台免费的公网服务器。p2p连接是基于NAT穿透实现的,而NAT穿透的原理搜索即可,有很多优质博客。本文只记录使用方法。

一、安装

首先按照Zerotier官网的指导安装工具,Windows和MacOs都是带GUI的APP,Linux为命令行工具zerotier-cli

1
curl -s https://install.zerotier.com | sudo bash

安装完成后zerotier会自动生成一个随机token,在 /var/lib/zerotier-one/authtoken.secret 中,通常需要sudo权限才能查看。同时zerotier会自动启动 zerotier-one 服务,在 localhost:9993 启动 API 后端,与后端的交互需要token进行权限验证。服务状态可以用systemctl status zerotier-one 查看,如果没有启动可以用 systemctl start zerotier-one 手动启动服务。

二、建立网络

我们使用self-hosting的方式构建网络,即某一个客户端同时也是网络的controller(可以用来管理网络准入、ip分配等,但并不是p2p连接中所需要的中间公网服务器,中间公网服务器仍由zerotier提供)。官方的指导在这里。我们用主机A来建立controller。

由于每次使用API都需要token验证,因此为了方便,首先将token放入环境变量:

1
TOKEN=$(sudo cat /var/lib/zerotier-one/authtoken.secret)

一台主机在网络(Network)中是一个节点(Node),并会被zerotier分配一个10位的节点id。主机加入虚拟网络时会通过节点id来寻找该网络的controller。可以通过两种方式查询当前主机的节点id:

1
2
3
4
# 通过zerotier-cli,返回 200 info 节点id 版本号 online
sudo zerotier-cli info
# 通过API,节点id在"address"字段
curl "http://localhost:9993/status" -H "X-ZT1-AUTH: ${TOKEN}"

为了方便,将节点id加入环境变量

1
NODEID=your-node-id

接下来我们用当前的节点作为controller创建一个虚拟网络。

1
curl -X POST "http://localhost:9993/controller/network/${NODEID}______" -H "X-ZT1-AUTH: ${TOKEN}" -d {}

通过post请求建立网络,会生成一个随机的16位网络id。为了方便,将网络id加入环境变量:

1
NWID=your-network-id

可以通过以下指令查看当前节点管理的所有网络的id:

1
curl "http://localhost:9993/controller/network/" -H "X-ZT1-AUTH: ${TOKEN}"

通过以下指令可以查看指定网络的信息配置信息:

1
curl "http://localhost:9993/controller/network/${NWID}/" -H "X-ZT1-AUTH: ${TOKEN}"

通过以下指令可以查看加入当前网络的所有节点,在没有节点加入该网络时,返回的是空:

1
curl "http://localhost:9993/controller/network/${NWID}/member" -H "X-ZT1-AUTH: ${TOKEN}"

接下来设置网络的ip分配,并设置为private网络,这样别的节点加入网络就需要controller的授权:

1
2
3
# ipRangeStart和ipRangeEnd指定了子网ip分配的范围
curl -X POST "http://localhost:9993/controller/network/${NWID}/" -H "X-ZT1-AUTH: ${TOKEN}" \
-d '{"ipAssignmentPools": [{"ipRangeStart": "192.168.192.1", "ipRangeEnd": "192.168.192.254"}], "routes": [{"target": "192.168.192.0/24", "via": null}], "v4AssignMode": "zt", "private": true }'

三、加入网络

在另一台主机B上安装zerotier,并通过以下指令加入刚创建的网络:

1
2
# network-id是16位的网络id
sudo zerotier-cli join network-id

此时再在主机A上查看网络所含节点,应当包含主机B的节点id,并且是字典的key,value为1:

1
curl "http://localhost:9993/controller/network/${NWID}/member" -H "X-ZT1-AUTH: ${TOKEN}"

需要在controller(主机A)上进行授权:

1
2
# MEMID是主机B的节点id
curl -X POST "http://localhost:9993/controller/network/${NWID}/member/${MEMID}" -H "X-ZT1-AUTH: ${TOKEN}" -d '{"authorized": true}'

此时再查看节点成员列表,主机B的value会变成3。注意主机A是controller只代表它是类似于路由器的存在,因此需要用同样的方法将主机A自己也加入网络才能使AB互相访问。

完成之后在主机AB上分别使用ip addr查看自己的网络接口,会看到里面多了zt开头的网络接口以及主机在虚拟网络中的ip地址,使用虚拟网络中的ip地址即可互相访问。例如主机A的地址是192.168.192.5,主机B的地址是192.168.192.6,在主机A上可以通过ssh user@192.168.192.6使用ssh连接主机B。如果主机A在0.0.0.0:8080开了一个网站,在主机B上也可以使用192.168.192.5:8080进行访问。主机的ip地址是根据public key算出来的,算法在identity.cpp里,因此同一主机的ip是固定的。

四、其他

如果要取消对某个节点的授权,可以在controller上使用以下指令:

1
curl -X POST "http://localhost:9993/controller/network/${NWID}/member/${MEMID}" -H "X-ZT1-AUTH: ${TOKEN}" -d '{"authorized": false}'

如果要从网络中删除某个成员,可以使用:

1
curl -X DELETE "http://localhost:9993/controller/network/${NWID}/member/${MEMID}" -H "X-ZT1-AUTH: ${TOKEN}"

如果想要清理掉这些网络,可以使用:

1
2
3
4
5
6
7
# 先停止zerotier服务
systemctl stop zerotier-one
# 删除现在的服务配置
cd /var/lib/zerotier-one/
rm -rf ./controller.d/
# 重新启动zerotier服务
systemctl start zerotier-one