引言
自购入nas来已过了大半年,期间我简单拿了个博士学位(并不简单),做出了满意的赛博猫娘,欠下了好几篇没写的文章————其中一篇就是讲赛博猫娘的————随后过上了玫瑰色的社畜生活。
当上社畜之后,我持巨资两万元搭建了新的nas。当然,其中有一万六七都是硬盘的钱。和憋博士论文时不同,现在我要充分考虑打通家庭和工作环境的网络。与此同时,过年回爹妈家时的网络环境也给我狠狠上了一课,靠tailscale官方提供的小水管根本不足以支撑任何可以称之为稳定的内网穿透操作。于是我痛定思痛,决定自建墙内derp节点,打通大内网。
中间遇到的坑就不提了,直接说说最终的解决方案吧。本文的代码有80%是参考大内网战略(6):自建 Tailscale DERP 中继服务器 保姆级教程写成的,15%是跟G老师学的,剩下5%来自我自己的曲折探索。
这还是第一次在这个网站上写技术文章,有点激动。
事前准备:域名、服务器和备案
首先要买一个服务器。最近阿里云在做活动,99就能买一年CES,还能再同价续一年,所以我决定先爽两年再说。域名建议也在阿里云买了算了,因为可以使用阿里云的备案助手,不用亲自和政府网站打交道。怎么买这些东西实在太基础,暂且略过不提,唯一的建议是域名到手之后把他的解析服务器改成Cloudflare,因为阿里云的域名服务实在是又抠又难用屁事儿又多,不如CF一根。阿里云的CES同样是又抠又难用屁事儿又多,不如vultr一根,但是不用不行。
买了域名之后,就可以先去备案了。备案要花几天时间,一切正常的话一两周足矣。未备案的域名的流量解析到阿里云服务器(可能)会被截断,对debug十分不利,所以最好还是不要尝试。
在备案期间,可以先去把宝塔面板装了,具体的步骤登陆官网即可查阅,整个过程十分傻瓜,不在此赘述。需要注意的只有一条:宝塔面板现在需要手机号注册才能使用,如果真的非常在意的话就自行搜索宝塔面板降级免登陆之类的关键词吧。
装好宝塔面板后先点击左侧的“网站”,自动把环境都装了。这个过程会花上几十分钟而且会占用可怜的服务器几乎全部的资源,所以趁着等备案的时候先做了算了。同样的道理,可以趁这时在Cloudflare上把域名(注意给个前缀)解析到服务器,但不要开cdn代理。把含前缀的完整域名记为derp.example.top,之后只会用到这个域名。
之后就没有什么要做的了,安心等备案通过吧喵。
上传证书文件
恭喜!备案通过了,现在你是一个有(合法)域名的人了。有域名的人就像互联网上的领主老爷,注定是比没有域名的贱民高贵一些的,而这每年只需要几块钱。
登陆宝塔面板,点击左侧的网站,然后添加一个站点。域名填入完整的域名,ftp和数据库都不需要。
点击站点,点击弹出窗口左侧的SSL,点击Lets encrypt选项卡,点击申请证书,文件验证即可。现在你会得到两个字符串:一个key和一个pem。将它们复制到本地的两个txt文件中,重命名为:
derp.example.top.crt derp.example.top.key
然后登录服务器后台,新建文件夹:
sudo mkdir /usr/local/cert
然后把刚才的两个证书文件上传到这个目录下,这就是之后derp服务要读取证书的目录。需要注意的是,derp强制要求证书重命名为这个格式,但目录其实可以自己指定。
部署tailscale
derp实际上可以脱离tailscale而运行,这里安装tailscale是为了让derp借助ts客户端进行验证,防止有人蹭服务器。不然的话,只要知道了域名和端口,谁都可以用你的服务器。
curl -fsSL https://tailscale.com/install.sh | sh
安装完成后,可以先不tailscale up,防止ts的iptables和阿里云冲突。ts和阿里云选择了同一个网段当内网,他们都以为别人不会选,但结果撞上了,导致会出各种各样的奇怪问题。
安装Golang
旧版本golang会大概率导致derp安装失败,所以运行命令检查一下:
go version
如果显示的版本和这个地方的不一样,就需要卸载重新安装。如果已经是最新版本请跳过整个步骤;如果显示go 命令不存在就可以直接安装最新版本了。
卸载旧版golang:
rm -rf /usr/local/go
记住在刚才那个地方查到的最新版本号(比如1.22.1),然后直接下载:
wget https://go.dev/dl/go<最新版本号>.linux-amd64.tar.gz
当然也可以本机下载再传上去。
解压安装包:
sudo tar -C /usr/local -xzf $HOME/go<最新版本号>.linux-amd64.tar.gz
配置Go环境变量:
sudo vim /etc/profile
在vim中输入i进入编辑模式,按下esc后输入:wq执行保存退出。
在文件的最后输入以下内容:
export GOROOT=/usr/local/go export GOPATH=/usr/local/gopath export GOBIN=$GOPATH/bin export PATH=$PATH:$GOROOT/bin export PATH=$PATH:$GOPATH/bin
读取刚才编辑的初始化脚本,并检查go是否正确安装:
source /etc/profile go version
如果显示刚才安装的go版本即为成功。
部署derper
建立目录,设置go代理,安装derper:
sudo mkdir -p /usr/local/gopath/bin go env -w GOPROXY=https://goproxy.cn,direct go install tailscale.com/cmd/derper@main
建立启动脚本:
sudo vim /usr/local/gopath/bin/runderper
脚本中输入:
#!/bin/sh cd /usr/local/gopath/bin nohup ./derper -hostname <你的域名> -c=derper.conf -a :<你喜欢的端口号> -http-port -1 -certdir /usr/local/cert -certmode manual -verify-clients -stun > console.log 2>&1 & echo $! > app.pid
其中你的域名就是derp.example.top,挑一个你喜欢的端口填进去,/usr/local/cert是一开始放入证书文件的目录。参数-verify-clients用来防止其他人使用你的derper服务器。
建立停止脚本:
sudo vim /usr/local/gopath/bin/stopderper.sh
#!/bin/sh kill `cat app.pid` rm -rf app.pid
保存后输入以下代码给脚本提权:
chmod +x /usr/local/gopath/bin/runderper chmod +x /usr/local/gopath/bin/stopderper.sh
建立服务:
sudo vim /etc/systemd/system/derper.service
Description=derper服务 After=network.target [Service] Type=forking ExecStart=/usr/local/gopath/bin/runderper ExecStop=/usr/local/gopath/bin/stopderper.sh [Install] WantedBy=multi-user.target
然后在阿里云安全组放行derp端口。需要放行刚才你选择的端口(TCP)和3478(UDP),后者是STUN服务的端口。在宝塔面板中也要放行相应端口。
启动tailscale服务
tailscale up
执行后tailscale会提供一个链接,可以在链接中完成登陆认证。
在参考的文章中,tailscale运行后会影响阿里云的workbench。但在我的情况下是反过来的,阿里云的workbench会把tailscale干躺,导致这个节点无法与其他节点通信……为了保险起见,这一步最好在其他ssh终端操作。为了解决这个问题,可以阅读这几篇文章:
启动derp
systemctl start derper systemctl enable derper
执行之后访问https://derp.example.top:<你喜欢的端口>/,应该会出现DERP服务器的信息。

添加中继节点
在tailscale控制台中点进Access Controls,在ssh前添加以下代码:
""derpMap": {
// OmitDefaultRegions 用来忽略官方的中继节点,一般自建后就看不上官方小水管了
"OmitDefaultRegions": true,
"Regions": {
// 这里的 901 从 900 开始随便取数字
"901": {
// RegionID 和上面的相等
"RegionID": 901,
// RegionCode 自己取个易于自己名字
"RegionCode": "阿里云",
"Nodes": [
{
// Name 保持 1不动
"Name": "1",
// 这个也和 RegionID 一样
"RegionID": 901,
// 域名
"HostName": "derp.example.top",
// 端口号
"DERPPort": <你喜欢的端口>,
},
],
},
},
},
注意将域名和端口替换成自己的。
此时在任意tailscale终端上运行:
tailscale netcheck
都应该能且仅能看到自己的中继节点。
自动续签SSL证书
其实到这里就已经可以正常使用大内网了,但是它还不持久。阿里云提供的免费证书需要3个月手动更换,Lets encrypt则可以续签免费证书,但我们需要把它重命名并移动到需要的地方。续签这部分,我们已经使用宝塔面板自动完成了,所以不需要操心。在宝塔面板中,会把签发的证书文件放到:
/www/server/panel/vhost/letsencrypt/derp.example.top
是真的绕……打开这个目录我们会发现有一堆文件,其中的fullchain.pem和privkey.pem就是我们需要的证书文件了。
在derp服务器里挑一个目录,创建一个脚本,一般选择home目录即可。
#!/bin/bash
# 定义源文件和目标文件路径
SRC_CERT="/www/server/panel/vhost/letsencrypt/derp.example.top/fullchain.pem"
SRC_KEY="/www/server/panel/vhost/letsencrypt/derp.example.top/privkey.pem"
DEST_CERT="/usr/local/cert/derp.example.top.crt"
DEST_KEY="/usr/local/cert/derp.example.top.key"
LOG_FILE="/home/cert_update.log"
# 检查日志文件是否存在超过一年
YEAR_AGO=$(date --date="1 year ago" +%s)
if [ -f "$LOG_FILE" ]; then
LOG_MODIFIED=$(date --date="$(stat -c %y $LOG_FILE)" +%s)
if [ $LOG_MODIFIED -lt $YEAR_AGO ]; then
mv $LOG_FILE "${LOG_FILE}_$(date --date="@${LOG_MODIFIED}" +%Y-%m-%d).old"
fi
fi
# 获取当前时间戳
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
# 复制并重命名证书文件,记录操作到日志文件
{
echo "$TIMESTAMP: 开始更新证书和密钥文件。"
cp -f $SRC_CERT $DEST_CERT && echo "$TIMESTAMP: 证书文件已成功更新。" || echo "$TIMESTAMP: 更新证书文件失败。"
cp -f $SRC_KEY $DEST_KEY && echo "$TIMESTAMP: 密钥文件已成功更新。" || echo "$TIMESTAMP: 更新密钥文件失败。"
echo "-----------------------------------------"
} >> $LOG_FILE 2>&1
这个脚本可以把文件重命名并覆盖到之前的位置,并且保留一年的日志。把这个脚本保存到喜欢的位置——比如/home/cert_update.sh。然后给脚本提权:
chmod +x /home/cert_update.sh
接着通过Cron来定期执行脚本,实现自动更新。打开Cron作业列表:
crontab -e
在里面添加一行:
0 3 * * * /home/cert_update.sh
就会每天凌晨三点自动执行一次脚本。
结语
到这里,整个大内网就搭建完成了。在境内derp服务器的加持下,tailscale服务的延迟会变得非常、非常低,乃至和局域网没有明显的差别。但带宽仍然受限于节点上传速度。
如果你没有成功的话:
- 检查所有需要输入域名和端口的地方;
- 检查阿里云安全组和宝塔面板有没有放行端口;
- 检查备案结果,必要的话可以给阿里云打电话催他们更新(当管局备案通过后);
- 域名不要套cdn;
- 在vim /usr/local/gopath/bin/console.log检查derp服务日志。




