问题

ZeroTier能P2P穿透,但由于其官方根服务器位于海外,延迟高,P2P成功率较低。

思路

个人理解,如果将ZeroTier组成的内网系统比作太阳系,那么ZeroTier官方的根服务器就是太阳,客户端就是除太阳以外的其他星体。正常情况下,客户端之间的通信都需要官方根服务器这个“太阳”,这就是所谓的公有服务器。

如果不作任何配置直接安装客户端,那么客户端之间的通信走的都是“太阳”这个公有服务器。虽然ZeroTier官方根服务器有多个,但都在海外,于是就会出现延迟高、P2P握手成功率低的问题。

不过,除了根服务器外,ZeroTier官方还提供了私有服务器:moon和planet。顾名思义,moon就是月亮,而月亮属于卫星,planet就是行星。实际上,ZeroTier官方服务器本质上也属于planet,是一种特殊的planet。planet的私有服务器稳定性比moon更高,因此,可通过在国内的云服务器上搭建一个私有的planet服务器,实现低延迟和高握手成功率。

优点

部署在云服务器上的ZeroTier服务器在P2P内网穿透时,只起到握手的作用,客户端之间的流量并不经过服务器,因此对云服务器的带宽要求不高,可以选择2-3M带宽的服务器。比如本人使用的是京东云55元/年的3M轻量云主机。

方法

服务端环境

ubuntu 22.04 server

在国内云服务器上安装ZeroTier服务端

ZeroTier提供了官方安装方法(见<https://zerotier.com/download/>):

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

更改默认根服务器

使用下列命令可以查看根服务器:

zerotier-cli peers

![Zerotier根服务器](https://pic1.zhimg.com/80/v2-cff7ac995e27ed9d28c57045b2116dc5_720w.png?source=ccfced1a)

官方提供了4个根服务器,延迟都比较高:

![](http://su4ed2sjr.hd-bkt.clouddn.com/%E6%A0%B9%E6%9C%8D%E5%8A%A1%E5%99%A8ping.png)

根服务器的信息存储在/var/lib/zerotier-one目录下的planet文件中。我在查找相关资料时发现,有的人直接将这个目录下planet文件拷贝替换客户端的planet,认为这样就可以通过私有云服务器实现P2P握手。

然而,这么做是大错特错的!因为这个planet指向的仍然是官方根服务器,如果不对这个planet做修改而替换客户端的planet,那么客户端连接的仍然是官方根服务器,而非你自己的云服务器!

接下来是修改planet的具体方法:

首先,下载zerotier源码:
$ git clone https://github.com/zerotier/ZeroTierOne

Cloning into 'ZeroTierOne'...

fatal: unable to access 'https://github.com/zerotier/ZeroTierOne/': GnuTLS recv error (-110): The TLS connection was non-properly terminated.

源码200M左右,国内的云服务器连接github很慢,所以我在自己电脑上挂代理下载了zip包,然后通过scp上传到云服务器(如果本地安装了linux环境,也可以在本地进行下列步骤),传输速度还不错:

$ scp d:\ZeroTierOne.zip root@xx.xx.xx.xx:~

root@xx.xx.xx.xx's password:

ZeroTierOne.zip 100% 172MB 1.3MB/s 02:10

切换到云服务器上,进入~目录,解压zip文件:

$ cd ~

# root @ lavm-et9uciusyk in ~ [11:12:56]

$ ls

ZeroTierOne.zip

$ unzip ZeroTierOne.zip

如果没有安装unzip,先安装:

sudo apt install unzip

ZeroTier的官方根服务器信息保存在ZeroTierOne/attic/world下的mkworld.cpp文件:

$ cd ~/ZeroTierOne/attic/world

$ ls

build.sh mkworld.cpp README.md world.bin world.c

$ vi mkworld.cpp
其次,修改mkworld.cpp文件

打开mkworld.cpp文件,里面有根服务器的信息:

// =========================================================================

// EDIT BELOW HERE

std::vector<World::Root> roots;

const uint64_t id = ZT_WORLD_ID_EARTH;

const uint64_t ts = 1567191349589ULL; // August 30th, 2019

// Los Angeles

roots.push_back(World::Root());

roots.back().identity = Identity("3a46f1bf30:0:76e66fab33e28549a62ee2064d1843273c2c300ba45c3f20bef02dbad225723bb59a9bb4b13535730961aeecf5a163ace477cceb0727025b99ac14a5166a09a3");

roots.back().stableEndpoints.push_back(InetAddress("185.180.13.82/9993"));

roots.back().stableEndpoints.push_back(InetAddress("2a02:6ea0:c815::/9993"));

// Miami

roots.push_back(World::Root());

roots.back().identity = Identity("de8950a8b2:0:1b3ada8251b91b6b6fa6535b8c7e2460918f4f729abdec97d3c7f3796868fb02f0de0b0ee554b2d59fc3524743eebfcf5315e790ed6d92db5bd10c28c09b40ef");

roots.back().stableEndpoints.push_back(InetAddress("207.246.73.245/443"));

roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:9002:5cb:ec4:7aff:fe8f:69d9/443"));

// Tokyo

roots.push_back(World::Root());

roots.back().identity = Identity("34e0a5e174:0:93efb50934788f856d5cfb9ca5be88e85b40965586b75befac900df77352c145a1ba7007569d37c77bfe52c0999f3bdc67a47a4a6000b720a883ce47aa2fb7f8");

roots.back().stableEndpoints.push_back(InetAddress("147.75.92.2/443"));

roots.back().stableEndpoints.push_back(InetAddress("2604:1380:3000:7100::1/443"));

// Amsterdam

roots.push_back(World::Root());

roots.back().identity = Identity("992fcf1db7:0:206ed59350b31916f749a1f85dffb3a8787dcbf83b8c6e9448d4e3ea0e3369301be716c3609344a9d1533850fb4460c50af43322bcfc8e13d3301a1f1003ceb6");

roots.back().stableEndpoints.push_back(InetAddress("195.181.173.159/443"));

roots.back().stableEndpoints.push_back(InetAddress("2a02:6ea0:c024::/443"));

```

mkworld.cpp文件中有4个官方根服务器:洛杉矶、迈阿密、东京和阿姆斯特丹,可以直接删掉或注释掉。

在安装zerotier时,mkworld.cpp文件中的根服务器信息将写入planet,所以要修改planet文件,就要从mkworld.cpp文件入手。

打开/var/lib/zerotier-one/identity.public文件

$ vi /var/lib/zerotier-one/identity.public

b556e06c57:0:ba1e04bc5ecb946193315678d0372889c62abeaaaf397b0e929a09926b07d846ffdf1f4650593d85b43cff6962332621f3be609569eea8d527bd0cbad2fe60ce

将identity.public文件中的字符串替换mkworld.cpp文件Identity("")双引号中的内容,例如:

Identity("b556e06c57:0:ba1e04bc5ecb946193315678d0372889c62abeaaaf397b0e929a09926b07d846ffdf1f4650593d85b43cff6962332621f3be609569eea8d527bd0cbad2fe60ce")

IP地址改成云服务器的地址,例如:

roots.back().stableEndpoints.push_back(InetAddress("17.211.14.7/9993"));

服务器默认端口是9993,也可以根据实际情况修改。

再次,编译生成新的配置文件planet

修改好mkworld.cpp并保存后,执行下列命令:

$ ./build.sh

如果出现“permission denied: ./build.sh”,则修改build.sh的权限:

$ chmod +x build.sh

再次运行:

$ ./build.sh

出现如下错误:

zsh: ./build.sh: bad interpreter: /bin/bash^M: no such file or directory

这是由于build.sh文件格式为dos格式导致,解决方法如下:

$ vi build.sh

$ :set ff

$ :set fileformat=unix

$ :wq

重新运行安装命令:

$ ./build.sh

$ ./mkworld

命令运行后,将在目录中生成world.bin文件,将这个文件重命名为planet,这就是包含私有服务器地址信息的planet:

$ mv world.bin planet

##### 最后,将编译生成的planet复制到/var/lib/zerotier-one/目录中

$ cp planet /var/lib/zerotier-one/

<u>**这个planet需要保存好,之后用来替换客户端的planet**</u>

##### 重启服务

$ systemctl restart zerotier-one.service

查看服务状态

$ systemctl status zerotier-one.service

● zerotier-one.service - ZeroTier One

Loaded: loaded (/lib/systemd/system/zerotier-one.service; enabled; vendor preset: enabled)

Active: active (running) since Thu 2025-04-03 13:44:09 CST; 10s ago

Main PID: 151332 (zerotier-one)

Tasks: 23 (limit: 2234)

Memory: 4.1M

CPU: 56ms

CGroup: /system.slice/zerotier-one.service

└─151332 /usr/sbin/zerotier-one

Apr 03 13:44:09 lavm-et9uciusyk systemd[1]: Started ZeroTier One.

Apr 03 13:44:09 lavm-et9uciusyk zerotier-one[151332]: Starting Control Plane...

Apr 03 13:44:09 lavm-et9uciusyk zerotier-one[151332]: Starting V6 Control Plane...

安装控制台服务

参考官方文档(<https://key-networks.com/ztncui/#installation>):

$ curl -O https://s3-us-west-1.amazonaws.com/key-networks/deb/ztncui/1/x86_64/ztncui_0.8.14_amd64.deb

$ sudo apt install ./ztncui_0.8.14_amd64.deb

$ sudo sh -c "echo ZT_TOKENsudo cat /var/lib/zerotier-one/authtoken.secret > /opt/key-networks/ztncui/.env"

$ sudo sh -c "echo HTTPS_PORT=53443 >> /opt/key-networks/ztncui/.env"

(注:3443是控制台的端口,可以根据实际情况修改)

$ sudo sh -c "echo NODE_ENV=production >> /opt/key-networks/ztncui/.env"

$ sudo chmod 400 /opt/key-networks/ztncui/.env

$ sudo chown ztncui.ztncui /opt/key-networks/ztncui/.env

$ sudo systemctl restart ztncui

开放服务端口

在云服务器的防火墙中,放开9993的tcp和udp端口,以及3443的tcp端口。

在浏览器中输入:

https://云服务器IP:3443

注意:必须用“https",否则打不开!

进入控制台,登陆,用户名默认admin,密码默认password。

由于ip地址比较难记,为了方便,可以修改本机hosts文件,用一个容易记住的域名影射到云服务器ip上,例如:

myname.com xx.xx.xx.xx

之后,就可以通过https://myname.com:3443来访问控制台了。

本地客户端配置

windows

1.安装完成后,进入C:\ProgramData\ZeroTier\One,用之前编译生成的planet替换目录中的planet,然后运行“services.msc”进入服务,重启zerotier服务。

2.客户端join new network,输入控制台中的16位字符串,设置开机启动。

ubuntu

1.安装完成后,用之前编译生成的planet替换/var/lib/zerotier-one目录下的planet,然后运行下列命令:

sudo zerotier-cli join <控制台中的16位字符串,没有尖括号>

2.重启服务

sudo systemtcl restart zerotier-one.service

最后,通过ping命令试验一下是否握手成功

我设置的局域网如下图:

![](http://su4ed2sjr.hd-bkt.clouddn.com/%E5%B1%80%E5%9F%9F%E7%BD%91.png)

在本机(10.10.10.100)ping:

![](http://su4ed2sjr.hd-bkt.clouddn.com/ping%E6%9C%AC%E6%9C%BA.PNG)

![](http://su4ed2sjr.hd-bkt.clouddn.com/ping101.PNG)

101是一台windows server,基本可以判定握手成功。

![](http://su4ed2sjr.hd-bkt.clouddn.com/ping102.PNG)

102是工作笔记本,从116ms骤降到6ms,可以判定握手成功。

![](http://su4ed2sjr.hd-bkt.clouddn.com/ping103.PNG)

![](http://su4ed2sjr.hd-bkt.clouddn.com/ping103-1.png)

103是一台ubuntu server,不知道是什么原因导致的问题。

在云服务器上,运行zerotier-cli peers命令:

![](http://su4ed2sjr.hd-bkt.clouddn.com/peers3.png)

可以看到,服务器本机不显示,四个客户端的角色均为leaf。

在本地ubuntu servers运行zerotier-cli peers:

![](http://su4ed2sjr.hd-bkt.clouddn.com/peers4.PNG)

可以看到云服务器角色是planet,除本机外的另外三个客户端角色均为leaf。

从客户端的端口来看,只有服务器和一台客户端的端口是9993,因为是在同一个路由下,所以我猜测是否跟这个有关系?但同一局域网的客户端难道不能都是同一端口?会有冲突?这个真不了解。

等去单位再试试,看看端口会不会有变化。