Tailscale DERP 中继节点搭建
通常,我会将自己nas上搭建的一些服务通过内网穿透的方式暴露到公网中,以供自己在外使用。此前我所使用内网穿透服务一直都是frp,但是frp需要为每个服务暴露一个公网端口,这一定程度上增加了被扫描和攻击的风险。为了降低潜在的风险,保证服务的稳定性,我逐渐产生了组建大内网的想法,通过虚拟组网从云服务器上直连本地nas,通过nginx+域名提供对外服务的想法。在这里我采用了tailscale作为组网工具,通过自建derp节点保证服务可用性,整体搭建流程如下:
1. 在服务器上安装Tailscale
1.1 安装
首先使用官方提供的安装脚本,一键安装Tailscale,同时自动注册service进行开机自启:
sudo curl -fsSL https://tailscale.com/install.sh | sh
这一步的目的是为了后续限制Derp中继节点的访问权限。Tailscale Derp可以限制是否只允许本地Tailscale登录的账户下的机器访问,否则任意账户和机器都可以使用你搭建的Derp节点作为中继。
1.2 启动
安装完成后,通过tailscale up
命令登录自己的tailscale账号,接入虚拟内网。
需要注意的是,如果使用的是阿里云的云服务器,或者其它使用100.64.0.0/10内网网段对内提供DNS等服务的机器,需要添加命令禁止tailscale修改iptables规则。否则会因为两者使用的内网网段冲突,tailscale会添加规则把所有对外的100.64.0.0/10请求都Drop掉,只允许走自己搭建的虚拟内网,导致一些服务出现故障。修改后的命令如下:
sudo tailscale up --netfilter-mode=off --accept-dns=false
2. 安装Golang
Tailscale Derp服务基于golang编写,官网推荐始终使用最新版本的golang允许Derp中继服务。如果你的服务器上有其他软件依赖旧版本的 go,将其升级为新版本可能会发生不可预料的后果,请务必仔细评估与衡量。
1.下载最新版本的安装包,请自行修改下述命令中的版本号:
wget https://go.dev/dl/go<version>.linux-amd64.tar.gz
2.将安装包解压到/usr/local
下:
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.2.linux-amd64.tar.gz
3.添加相关环境变量:
export GOROOT=/usr/local/go
export GOPATH=/usr/local/gopath
export PATH=$PATH:$GOROOT/bin
export PATH=$PATH:$GOPATH/bin
4.然后输入go version
检查是否安装成功。
3. 安装derper服务
3.1 安装
sudo mkdir -p /usr/local/gopath/bin
go install tailscale.com/cmd/derper@main
3.2 建立启动和关闭脚本
cd /usr/local/gopath/bin
sudo mkdir derper-scripts && cd derper-scripts/
启动脚本sudo vim derper-run.sh
:
#!/bin/sh
cd /usr/local/gopath/bin/derper-scripts
nohup ../derper -hostname <指向服务器的域名> -c=derper.conf -a :30001 -http-port -1 -verify-clients -stun > console.log 2>&1 &
echo $! > derper.pid
systemctl restart tailscaled
关闭脚本sudo vim derper-stop.sh
:
#!/bin/sh
kill `cat derper.pid`
rm -rf derper.pid
添加可执行权限:
chmod +x derper-run.sh derper-stop.sh
3.3 创建service
sudo vim /etc/systemd/system/derper.service
derper.service内容如下:
[Unit]
Description=derper
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/gopath/bin/derper-scripts/derper-run.sh
ExecStop=/usr/local/gopath/bin/derper-scripts/derper-stop.sh
[Install]
WantedBy=multi-user.target
创建完成后,启动服务:
sudo systemctl enable derper.service
sudo systemctl start derper.service
3.4 在nginx中配置域名(略)
配置域名指向30001端口,同时在防火墙中开启3478(stun-udp)端口。配置完成后访问域名,如果出现This is a Tailscale DERP server.
字样就表示搭建成功了。
4. 在Tailscale中设置中继节点
进入Tailscale网页控制台,打开Access Controls,在ssh配置前添加derpMap配置信息:
// Define private derp
"derpMap": {
"OmitDefaultRegions": true, // 关闭官方Derp中继节点
"Regions": {
"900": { // 900-999为预留区域
"RegionID": 900,
"RegionCode": "HK",
"RegionName": "Hong Kong",
"Nodes": [ // 每个区域中可以有多个中继节点
{
"Name": "1",
"RegionID": 900,
"HostName": "域名",
},
],
},
},
},
配置完成后,客户端开启命令行执行tailscale netcheck
即可看到自定义的derp中继节点,及对应区域的延迟了。
如果可以看到自定义的derp中继节点和延迟,但是实际访问不通,建议重启服务器和客户端tailscale,同时更新至最新版本,然后重试。
搭建完成后,就可以关闭原有的frp内网穿透服务及端口,将原来指向内网穿透端口的域名改为指向虚拟内网中的nas设备。所有请求先通过cdn到达云服务器,再走虚拟内网转发到本地nas,就可以最大限度地避免泄露和攻击了。
5.注意事项
1.一个域名可以同时绑定一个ipv4地址(A)和一个ipv6地址(AAAA)。如果你的域名只绑定了ipv4地址,那么就会出现ipv6 only的机器无法连接上DERP中继节点的情况,如下所示:
这种情况下,这台ipv6 only的机器在虚拟内网中是无法访问的。因此,如果你用来搭建DERP中继节点的机器有独立ipv6地址,那么最好一起绑定到域名上。
2.搭建完成后,你可以通过下述命令来简单查看从当前机器到任意节点的路由情况:
tailscale ping <虚拟内网ip>
此命令有两种可能的响应结果:
- 如果两端之一有公网ip,那么理想的状态是打洞直连,这种情况的响应如下:
ping from <目标节点> via <公网ip> in XXms
- 如果两端均没有公网ip,且不在同一个内网环境下,那么就会通过DERP中继节点转发请求,结果如下:
ping from <目标节点> via DERP<中继节点> in XXms
但是,如果你是在阿里云等通过私有网卡映射,实现公网访问的服务器上安装Tailscale。你就会发现你本地节点(无公网)和服务器节点(有公网)之间的访问是走DERP中继节点的,而且有些情况下会绕路选择较远的DERP中继节点,导致连接速率和稳定性都不尽如人意。这是因为服务器端Tailscale打洞所需的端口没有被开放,尝试打洞时,外网请求无法通过网卡映射到内网ip端口上,导致端对端直连失败。
Tailscale打洞所需的udp端口在/etc/default/tailscaled
中配置,默认为41641端口(udp),记得在服务台的防火墙中开放一下。这种情况适用于所有内网设备,如果你拥有公网ip,记得将内网中Tailscale设备的41641端口(udp)通过端口映射等方式放行出去,这样能够极大增加打洞成功的概率,提高连接的速率和稳定性。