自建 Tailscale 服务并实现多设备组网互联

1. 简介

Tailscale 是基于 wireguard 的跨平台虚拟组网工具,可以非常方便地实现多个设备的 mesh 虚拟组网。组网后,设备间可以直接使用内网 IP / 域名进行通信,可以随意切换网络,不会引起地址变动。这在远程访问、自建服务等场景下是极为有用的。

举例而言,接入统一的 Tailscale 网络后,你可以在外随时随地访问家中的 NAS 设备而无需公网 IP 或 DDNS 服务,同时享受到 NAT 穿透带来的几乎满带宽的网速体验;你可以随处使用手机 / 平板通过 moonlight 串流家中的电脑进行 “云游戏 “,将强大的计算与渲染性能随身携带;对于基友小规模自建服务器联机游戏的场景,Tailscale 更是适合不过。

另外,Tailscale 还提供了不少非常实用的功能,如文件隔空投送 Taildrop、接入同网段内其他设备的 Subnet Router(可以远程共享 NAS、打印机)、内网 MagicDNS 模块、出口路由功能、内网暴露服务和分享目录、子网内部安全 SSH、与现有组网系统集成等等……Tailscale 通过 SDN 的设计,自动在各节点之间下发配置文件并协调密钥加密,解决了 wireguard 网络增删设备时需要手动配置所有节点,灵活性不够好的问题,并且还提供强大的端到端加密通信保护和基于零信任思想的 ACL 控制等安全机制。

唯一的缺点可能是,Tailscale 是国外服务,借用国外现成的 oauth 系统(比如谷歌账号等)进行账户管理,这在国内使用起来相对不是很方便。而且在国内,并没有现成的 Tailscale Derper 中继服务器,直接使用 Tailscale 默认的中继服务会出现延迟爆炸的问题。

我们可以通过在国内的服务器上自行搭建开源的控制服务端 Headscale,配合自建官方开源的 Derper 中继服务器,来彻底解决这些问题,将 Tailscale 的各种强大功能充分发挥出来。

2. 大致通信原理

处于同一个 Telnet 内的设备之间将要进行通信时,Tailscale 会自动尝试 NAT 打洞进行 P2P 直连,穿透后设备等同于直连,设备间通信延迟极低。Tailscale 团队对 NAT 穿透有较为深入的研究,其穿透技术对各种类型的 NAT 的都有相当不错的效果。

如果打洞无法成功,Tailscale 将通过 Derp 中继协议进行端到端加密的 TCP 中继,自动根据延迟等因素选用最优的中继服务器进行连接。TCP 中继有效解决了 wireguard UDP 公网大流量被 QOS 的问题。

3. 服务端部署

自建 Tailscale 需要搭建两个服务端:控制平面 Headscale 和中继服务器 Derper。

3.1. Headscale 控制平面部署

Headscale 是控制平面部分,负责作为中心组件协调各节点之间的连接、下发各种配置信息等。

这里使用 docker 部署,caddy 反代并自动申请证书,另外还部署了开源项目 Headscale Admin 面板,用来进行简单的服务端控制。

docker-compose 文件如下,这里使用了 Traefik 作为反代服务器,其配置非常适应 docker 环境下的部署:

services:
  headscale:
    container_name: headscale
    image: headscale/headscale:latest
    restart: unless-stopped
    volumes:
      - ./headscale-config:/etc/headscale
      - ./headscale-data:/var/lib/headscale
      - ./headscale-run:/var/run/headscale
    entrypoint: headscale serve
    networks:
      - traefik
    labels:
      traefik.enable: true
      traefik.http.routers.headscale.tls: true
      traefik.http.routers.headscale.tls.certresolver: letsencrypt
      traefik.http.routers.headscale.entrypoints: https
      traefik.http.routers.headscale.rule: Host(`<Headscale服务器域名>`)
      traefik.http.services.headscale.loadbalancer.server.port: 8080

  # admin panel
  headscale-admin:
    image: goodieshq/headscale-admin:latest
    container_name: headscale-admin
    restart: unless-stopped
    user: "1003"
    depends_on:
      - headscale
    networks:
      - traefik
    labels:
      traefik.enable: true
      traefik.http.routers.headscale-admin.tls: true
      traefik.http.routers.headscale-admin.tls.certresolver: letsencrypt
      traefik.http.routers.headscale-admin.entrypoints: https
      traefik.http.routers.headscale-admin.rule: Host(`<Headscale服务器域名>`) && PathPrefix(`/admin`)
      traefik.http.services.headscale-admin.loadbalancer.server.port: 80

除了 docker-compose 文件之外,还需要将 headscale 项目的 config-example.yaml 复制到./headscale-config 目录下,改名 config.yaml。对下面这些项做修改:

...
server_url: https://<Headscale服务器域名>:<工作端口>
...
prefixes:
  v4: 100.64.0.0/10
  # v6: fd7a:115c:a1e0::/48		# 把IPv6注释掉,按需
...
derp:
  server:
    enabled: false				# 禁用内置derper服务器(直接用官方的会更好)
...

全部配置完成之后,启动容器,Headscale 即可在 https://<Headscale服务器域名>:<工作端口> 访问到。记得要在 Traefik 中配置泛域名 HTTPS 证书申请

3.1.1. 配置管理面板

前面的 docker-compose 中已经部署了 goodieshq/headscale-admin 管理面板的镜像,可以通过 https://<Headscale服务器域名>:<工作端口>/admin 地址访问到。通过管理面板可以快速添加、管理与删除节点、修改配置,不用再每次上服务器跑命令行。

管理面板需要 Headscale 的 API Key 以访问其接口。在 docker 容器部署完毕开始运行后,在服务器执行如下命令:

docker exec headscale headscale apikey create

这样会创建一个新的 Headscale API Key。将此 Key 填入管理面板的 Settings 页面并保存,即可开始使用管理面板。

3.2. Derper 中继部署

Derper 中继服务器的部署仍然使用 docker-compose,并通过客户端防止自己的 Derper 中继服务器被别人白嫖。

具体详见文章:《自建 Tailscale Derper 中继服务》

3.3. 服务器防火墙放行

服务器的防火墙 / 安全组需要放行如下这些端口,以允许 Headscale 和中继 Derper 对外提供服务:

  • Headscale 服务端工作端口(TCP)
  • Derper 中继工作端口(TCP)
  • Derper STUN 服务工作端口(UDP)

4. 客户端接入

4.1. Windows & Mac & Linux

安装官方客户端,按通过命令行登录自定义 headscale 服务器

tailscale up --login-server=https://<Headscale服务器域名>:<工作端口> --unattended

4.2. 安卓 & iOS

直接从应用商店下载官方客户端即可,UI 美观且好用。

在国内的网络环境下,需要对梯子进行兼容,详见下面” 梯子兼容问题 “

5. 其他细节问题

5.1. 梯子兼容问题

国内普遍网络环境问题,导致平时经常需要使用梯子。tailscale 与梯子应用之间存在一些协调性问题,比如安卓平台只能同时运行一个 VPN 应用,导致 tailscale 应用和梯子 APP 无法共存,只能同时开启一个;另外,Windows 平台上梯子启用 TUN 模式时,若不对路由等进行额外处理,tailscale 也无法工作。

5.1.1. 桌面平台:修改梯子配置

要使 Tailscale 和梯子互不冲突,可以通过设定梯子的规则和 TUN 绕过 Tailscale 网段工作,以避免错误地路由其流量。以 Clash.Meta 为例,如下设置配置文件:

tun:
  ...
  route-exclude-address:	# 绕过tailscale网段
    - "100.64.0.0/10"
...
hosts:
  # 可以在这里加Tailscale内网DNS解析条目,以代替MagicDNS的功能,类似
  # pc.YOUR-DOMAIN.com: 100.64.111.111
  # phone.YOUR-DOMAIN.com: 100.64.111.112
...
dns:
  ...
  use-hosts: true
  fake-ip-filter:       # fake ip 白名单列表
    - "<Headscale服务器域名>"
    ...
...
rules:
  # 不路由Tailscale流量
  - IP-CIDR,100.64.0.0/10,DIRECT,no-resolve
  - DOMAIN-SUFFIX,<Headscale服务器域名>,DIRECT,no-resolve
  ...

5.1.2. 安卓平台

安卓平台的 Tailscale 与梯子共存,已经有较为完善的方案。详见:《安卓平台实现 Tailscale 与代理 APP 共存》

5.1.3. iOS 平台

不使用苹果设备,因此缺乏这方面经验,欢迎评论区补充可用方案~

5.2. MagicDNS 相关问题

Tailscale 官方提供了 MagicDNS 功能,在 admin 面板自定义各个主机名后,就可以通过 MagicDNS 用自定义的主机域名来访问到各主机。

不过事实上,考虑到和梯子的协同问题,为了避免出现 BUG,目前各设备上推荐暂不启用 MagicDNS。有两种方法实现类似 MagicDNS 的效果:

  1. 在梯子配置文件的 hosts 段中,针对各主机手动设置 hosts 条目
  2. 在 headscale 域名所在服务商处(如 Cloudflare)手动添加对应主机的 DNS 条目

这些都可以达到类似的效果。

6. 补:zerotier 和 tailscale 的区别

粗略来看,两者都是虚拟组网方案,在性能上也区别不大。不过在技术实现细节上有一定的差异:

6.1. 网络底层实现

  • zerotier 是二层(数据链路层)虚拟组网方案,更贴近理解上的 “虚拟局域网” 的概念,对于大部分协议都有比较好的支持
  • tailscale 是基于 wireguard 的三层(网络层)点对点虚拟组网方案,其主要支持三层协议的点对点通信。某些需要二层支持的协议 Tailscale 无法使用。比如,截至目前(2024.10)并不支持 UDP 广播、mDNS、Bonjour 等,导致一些设备互联 / 游戏等场景下的的局域网设备发现无法正常工作。

6.2. 通信协议实现

  • zerotier 通过 TCP 协议直连通信。自建 moon 服务器进行中继时默认使用 UDP 通信,不过可以在客户端手动切换到 TCP 中继模式
  • tailscale 基于 wireguard,直连时使用 UDP,高峰期或者大流量下可能受到运营商 QoS。自建 derp 服务器中继则是使用 TCP 进行中继通信,相对较为稳定。

6.3. 附属生态与功能

  • tailscale 有非常丰富的附属功能,如文件投送、子网路由、出口路由、自定义 DNS 等等。
  • zerotier 专注于实现虚拟组网,附属功能较少。

另外,似乎有评价说相较于 Zerotier,Tailscale 的易用性、UI 美观度等各方面都要更强一些。这个仁者见仁智者见智吧,有需求的话可以亲自测试,感受一下两者的区别。


自建 Tailscale 服务并实现多设备组网互联
https://blog.openyq.top/posts/64921/
作者
yqs112358
许可协议