公网 IPv6 自建 Tailscale DERP 服务

发布时间:

警告

这不是一篇面向纯小白用户的文章,省略了一些细节,需要对网络运维知识有一定了解。

一年前写了篇 无需域名,自建 Tailscale DERP 节点, 缺点是需要一台公网服务器,且众所周知云服务器的带宽给的都很小,不适合做中转服务。 现在我家的网络环境有了公网 IPv6 地址,所以可以考虑在家里自建一个 DERP 服务(当然这个节点只推荐自己使用)。

  • 网络环境:G7615v2 光猫 IPv6 入站开启, 下接路由器 AX82U 开启 IPv6 Pass-Through,家中网络设备都能获取到公网 IPv6 地址。 注意光猫没有改桥接,其自带的 Wi-Fi 和 IPTV 功能给家里人正常使用,我只折腾路由器下的有线子网(连接各个卧室)。
  • 硬件设备:存算分离,NAS、PVE 机和 J1900 工控机都通过 AX82U 的 LAN 口连接到我卧室的交换机上。 这台 J1900 工控机是本文的主角, 刷了 DHDAXCW 的 x86 ImmortalWRT, 作为家中的旁路由器,负责跑一些 24 小时开机的 Docker 服务。
  • 域名解析:阿里云买的 cn 域名,使用 Cloudflare 的 DNS 服务。

推荐工具:Lucky 大吉

家宽通常给动态公网 IPv6 地址,平均几天就会变一次,所以需要一个动态 DNS 服务, 将 IPv6 地址映射到指定的域名上,这里推荐使用 Lucky 大吉 对于本文要提到的 DERP 节点,我还会使用 Lucky 自带的 SSL/TLS 证书功能为相应的域名生成证书。 在骷髅头 DHDAXCW 最新固件中已经预装了 Lucky,且可以通过 iStore 更新到最新版本。 基本上安装了 Lucky 大吉版本后,常见的网络服务都可以通过图形界面配置,非常适合小白用户使用。

  1. 动态域名:域名列表两行,分别是 openwrt.example.cn*.openwrt.example.cn.

  2. SSL/TLS 证书:注意 Cloudflare 官方提供的证书仅限回源,无法在公网访问时使用, 所以我们需要手动申请免费的可信证书,我选择的证书颁发机构是 FreeSSL, 参考配置如下:

    • 证书备注:_.openwrt.example.cn
    • 添加方式:ACME
    • 证书颁发机构:FreeSSL(使用和 Cloudflare 账户一样的邮箱账号)
    • EAB 认证:启用(在 FreeSSL 自动化的 管理 页面获取参数)
    • 验证方式:Cloudflare(使用与动态域名配置一致的 Token 和域名列表)
    • 证书映射:启用,路径设置成 /opt/cert 目录(可根据你的需求修改), 且添加证书更新后的触发脚本 python3 /opt/cert/update.py, 需添加的代码如下:
    python
    import os
    import shutil
    
    def update_certificates(source_dir, target_dir, old_prefix, new_prefix):
        """
        更新证书文件后 Hook 调用,将其复制到目标目录,同时重命名文件。
    
        :param source_dir: 源目录,包含原始证书文件
        :param target_dir: 目标目录,用于存放更新后的证书文件
        :param old_prefix: 旧前缀,用于匹配需要更新的文件
        :param new_prefix: 新前缀,用于替换旧前缀
        """
        if not os.path.exists(target_dir):
            os.makedirs(target_dir)
    
        for filename in os.listdir(source_dir):
            if old_prefix in filename:
                new_filename = filename.replace(old_prefix, new_prefix)
                source_file = os.path.join(source_dir, filename)
                target_file = os.path.join(target_dir, new_filename)
                shutil.copy(source_file, target_file)
                print(f"Copied and renamed {source_file} to {target_file}")
    
    def main():
        SOURCE_DIR = "/opt/cert"
        
        domains = [
            {"old_prefix": "_", "new_prefix": "derper"},
            # {"old_prefix": "_", "new_prefix": "another_domain"},
        ]
    
        for domain in domains:
            old_prefix = domain["old_prefix"]
            new_prefix = domain["new_prefix"]
            target_dir = os.path.join(SOURCE_DIR, new_prefix)
            update_certificates(SOURCE_DIR, target_dir, old_prefix, new_prefix)
    
    if __name__ == "__main__":
        main()

    这个 update.py 脚本后续可进一步维护,毕竟内网可能不止有 DERP 一个服务需要用到本地证书路径。 我们完全借助 Lucky 来帮我们完成证书续签,这样就不用担心证书过期了。 你可以根据你的需要添加 WebHook 通知。

  3. Web 服务:这一步仅仅是借助 Lucky Web UI 来验证上面配置的 SSL 证书是否能正常使用

    • 添加 Web 服务器规则:监听 tcp6 和 2333 端口,开启 TLS 和防火墙自动放行
    • 添加子规则:名称为 lucky, 类型选择 “反向代理”, 前端地址 lucky.openwrt.example.cn, 后端地址 http://127.0.0.1:16601, 其他不用动
    • 使用任意浏览器访问 https://lucky.openwrt.example.cn:2333 进行测试
    • 同理你还可以添加其他子规则来反向代理内网其它 WEB 服务。

到这里 Lucky 部分的配置就完成了,接下来我们开始搭建 DERP 服务。

自建 DERP 服务

我的 OpenWrt 固件支持 Docker, 我选择使用 fredliang44/derper-docker 来简化部署:

shell
docker run \
  -e DERP_DOMAIN=derper.openwrt.example.cn \
  -e DERP_CERT_MODE=manual \
  -v /opt/cert/derper:/app/certs \
  -p 30080:80 \
  -p 30443:443 \
  -p 3478:3478/udp \
  fredliang/derper

其中 80 是 HTTP 端口,443 是 HTTPS 端口(也是 DERP 端口),3478 是 STUN 端口。 开放 STUN 端口可以支持在运行 tailscale netcheck 命令时检测延迟, 也借此来确定 Tailscale 客户端的公共 IP 地址及其 NAT 类型,尝试进行 NAT 穿透直连。

部署后访问 https://derper.openwrt.example.cn:30443 看到 Web 界面即表示部署成功。

添加 DERP 节点

在 Tailscale 的 Access Controls 界面中,如下添加:

yaml
// Example/default ACLs for unrestricted connections.
{
    // ...
	"derpMap": {
		"OmitDefaultRegions": true,  // 禁用默认 DERP 节点,只使用自建 DERP 节点
		"Regions": {
			"900": {
				"RegionID":   900,
				"RegionCode": "HOME",
				"RegionName": "Homehub Derper",
				"Nodes": [
					{
						"Name": "OpenWrt",
						"RegionID": 900,
						"HostName": "derper.openwrt.example.cn",
						"DERPPort": 30443,
						"STUNPort": 3478,
					},
				],
			},
        },
	},
    // ...
}

Tailscale 客户端重新连接后,命令行输入 tailscale netcheck 检查 DERP 服务是否生效。

添加客户端验证(可选)

以上的配置是不验证客户端的,适合好朋友们共用你的 DERP 节点。如果需要对客户端进行验证防止滥用, 则在需要在运行 DERP 容器时额外添加环境变量 DERP_CLIENT_AUTH=true

shell
docker run \
  -e DERP_DOMAIN=derper.openwrt.example.cn \
  -e DERP_CERT_MODE=manual \
  -e DERP_CLIENT_AUTH=true \
  -v /opt/cert/derper:/app/certs \
  -v /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock \
  -p 30080:80 \
  -p 30443:443 \
  -p 3478:3478/udp \
  fredliang/derper

这就要求你在安装了 DERP 的节点上,另外安装并登录 tailscaled 实例进行验证。 我在自己的 OpenWrt 机器上没有开启这个功能,因为担心 Tailscale 会把 iptables 改得很乱。

根据 官网文档 的说明:

The Personal plan allows for 3 free users in a single Tailscale network, known as a tailnet. You can also share devices with other users with node sharing. For more information on what is included in the Personal plan, see the Pricing page.

开启验证后,免费套餐最多可以使用 3 个用户来共享你的 DERP 服务。

参考材料/推荐阅读