在一些场景下需要在两台不同内网中的主机间建立连接。例如在家里放了一台常开的电脑,电脑处于家中内网中,平时我们在外面无法直接访问。此时有两种解决方案。第一种是利用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 | # 通过zerotier-cli,返回 200 info 节点id 版本号 online |
为了方便,将节点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 | # ipRangeStart和ipRangeEnd指定了子网ip分配的范围 |
三、加入网络
在另一台主机B上安装zerotier,并通过以下指令加入刚创建的网络:
1 | # network-id是16位的网络id |
此时再在主机A上查看网络所含节点,应当包含主机B的节点id,并且是字典的key,value为1:
1 | curl "http://localhost:9993/controller/network/${NWID}/member" -H "X-ZT1-AUTH: ${TOKEN}" |
需要在controller(主机A)上进行授权:
1 | # MEMID是主机B的节点id |
此时再查看节点成员列表,主机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 | # 先停止zerotier服务 |