无需域名,自建 Tailscale Derp 节点

发布时间:

信息

如果你家有公网 IPv6 网络环境,推荐参考 公网 IPv6 自建 Tailscale Derp 服务 一文。 使用一台低功耗设备,比如 x86 路由器、树莓派等,可以在家里搭建一个 Derp 服务,结合域名 DDNS 使用。 该文章使用的 Lucky 方案支持自动续签 SSL/TLS 证书,无需担心证书过期问题。

云服务器性价比太低,用不到 1000 元在家里搞了个服务器,采用的是 X99 寨板 + E5 2666v3 至强 CPU(功耗相对来说确实大,但可以接受)。 装了个 PVE 虚拟机,用来跑旁路由、黑群晖、容器应用和做一些奇奇怪怪的事情。 考虑到大部分时候都不在家,为了方便无公网环境下的服务器远程运维,所以需要用到虚拟专用网络(VPN)服务。 我选择了 Tailscale,因为它的配置简单,支持多平台,而且是基于 WireGuard 协议的。

实际上我还用了 Cloudflare Zero Trust 作为双保险,这里就不展开了。

Tailscale 是什么

说的简单点就是:一个虚拟局域网用的工具,无需像 frp 那样配置复杂的端口映射,非常适合个人使用。 另外还提供了开源版本的 Headscale,可以自建 Tailscale 服务。

使用情境:SSH 远程开发、家庭 NAS 远程访问、远程桌面、远程监控等。

设置群晖的 Tailscle 套件

我家的网络拓扑很简单,主从光猫只负责提供 Wi-Fi 和 IPTV 信号,其它网络统一由路由器 AX82U 负责。 这样不论我怎么折腾路由器下的子网,都不会影响家人的网络使用。 我原本在 AX82U 路由器上安装了 Tailscale, 负责宣告家中子网的路由,但 AX82U 经常因此宕机出现 Tailscale 进程挂掉的情况(估计硬件性能不够)。 现在宣告子路由任务由黑群晖的 Tailscale 社区套件完成,只需要在群晖的 /etc/sysctl.conf 配置文件添加如下两行:

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Root 权限执下面的命令,开启 Tailscale 宣告子路由和作为出口节点的功能:

shell
sysctl -p /etc/sysctl.conf 
/var/packages/Tailscale/target/bin/tailscale configure-host
synosystemctl restart pkgctl-Tailscale.service
/var/packages/Tailscale/target/bin/tailscale up --advertise-routes=192.168.50.0/24 --advertise-exit-node --reset

上面的 192.168.50.0/24 即是家中的子网网段,你可以根据自己的网络环境进行修改。

在满足一定条件(比如双方都拥有公网 IPV6 地址)的情况下, Tailscale 负责打洞实现 P2P 直连,否则将走官方提供的中转节点(称为 Derp 服务器)。 但是大部分情况下,官方的 Derp 服务延迟很高(最近的是 HongKong 和 Tokyo 节点), 实际体验下来,除最简单的 SSH 基本处于不可用状态,此时就需要一个自建 Derp 中转服务。

自建 Derp 节点服务

通常自建 Derp 服务需要一个公网服务器,并配置好域名和 SSL 证书。 这里我们对 Derp 官方的 GO 源码稍做改动,实现无需域名,直接通过公网服务器的 IP 地址来访问 Derp 服务。

安装 GO 环境(步骤略),然后获取 Derper 源码:

shell
git clone https://github.com/tailscale/tailscale.git --depth=1

绕过 ServerName 域名检查

进入到刚刚获取的 tailscale/cmd/derper 目录。

Hack 源码,修改 cert.go 文件中的 getCertificate 函数:

go
func (m *manualCertManager) getCertificate(hi *tls.ClientHelloInfo) (*tls.Certificate, error) {
	if hi.ServerName != m.hostname {  
		return nil, fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)  
	}  

	// Return a shallow copy of the cert so the caller can append to its
	// Certificate field.
	certCopy := new(tls.Certificate)
	*certCopy = *m.cert
	certCopy.Certificate = certCopy.Certificate[:len(certCopy.Certificate):len(certCopy.Certificate)]
	return certCopy, nil
}

编译这个改动过的 Derper, 并将二进制文件放到 /usr/local/bin/derper

shell
go build -o /usr/local/bin/derper .

配置 SSL 证书

使用 OpenSSL 配置 SSL 证书:

shell
mkdir /usr/local/.ssl
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
  -keyout /usr/local/.ssl/derp.test.com.key \
  -out /usr/local/.ssl/derp.test.com.crt \
  -subj "/CN=derp.test.com" \
  -addext "subjectAltName=DNS:derp.test.com"

其中的 derp.test.com 是你的 Derp 服务域名,可以自行替换成任意域名。

配置 Derp 服务

编辑 /etc/systemd/system/derp.service 文件:

shell
[Unit]
Description=TS Derper
After=network.target
Wants=network.target

[Service]
User=root
Restart=always
ExecStart=/usr/local/bin/derper -hostname derp.test.com -a :12345 -certmode manual -certdir /usr/local/.ssl
RestartPreventExitStatus=1

[Install]
WantedBy=multi-user.target

注意检查自建 Derp 服务器上的防火墙设置,放行相关端口(在上面的例子中是 12345)。

启用并启动 Derp 服务:

shell
systemctl enable derp
systemctl start derp
systemctl status derp

访问 IP:12345 这个地址看是否生效,会看到一个 Derp 相关的网页。

配置 Tailscale

在 Tailscale 控制台管理界面 Access Control 中配置 Derp 服务的地址:

json
"derpMap": {
    //"OmitDefaultRegions": true,
    "Regions": {
        "912": {
            "RegionID": 912,
            "RegionCode": "AliYun",
            "RegionName": "AliYun Derper",
            "Nodes": [
                {
                    "Name": "AliYun",
                    "RegionID": 912,
                    "DERPPort": 12345,   // 更换为自己的端口号
                    "IPv4": "127.0.0.1", // 更换为自己的 IP 地址
                    "InsecureForTests": true,  // 允许非 SSL 连接
                },
            ],
        },
        "1": null,
        "2": null,
        "3": null,
        "4": null,
        "5": null,
        "6": null,
        "7": null,
        "8": null,
        "9": null,
        "10": null,
        "11": null,
        "12": null,
        "13": null,
        "14": null,
        "15": null,
        "16": null,
        "17": null,
        "18": null,
        "19": null,
        //"20": null,
        "21": null,
        "22": null,
        "23": null,
        "24": null,
        "25": null,
    },
},

其中强制覆盖了 Tailscale 官方的 25 个 默认 Derp 节点 (除了 20 号代表的香港节点,这样自建 Derp 服务不可用的时候可以救急)。

使用 Tailscale 客户端连接,命令行输入 tailscale netcheck 检查 Derp 服务是否生效:

shell
Report:
        * UDP: true
        * IPv4: yes, 
        * IPv6: yes, 
        * MappingVariesByDestIP: false
        * HairPinning: false
        * PortMapping: UPnP, NAT-PMP, PCP
        * CaptivePortal: false
        * Nearest DERP: AliYun Derper
        * DERP latency:
                - AliYun: 13.5ms  (AliYun Derper)
                - hkg: 151.9ms (Hong Kong)