Tailscale 是一种优秀的异地组网工具,支持通过 STUN 打洞实现客户端点对点直连,互联协议基于 WireGuard 实现,兼具高效与安全的特性。相关项目的开源代码库请参考 Tailscale GitHub Repository

简单而言,Tailscale 可以不受限于服务器带宽,使位于不同网络环境下的设备获得类似于同一局域网下的体验。但在国内网络环境下,Tailscale 官方的 DERP 中转服务器多部署在海外,详情可参考 High Availability Documentation。一旦设备之间点对点(P2P)直连打洞失败,流量就需要绕道海外的中转节点进行中转,导致网络连接非常不稳定。

为了保障隐私和使用稳定性,决定采用自建方案搭建国内的虚拟局域网。本文将以步骤化的方式,介绍如何使用 1Panel 与 Docker 部署自托管的 Headscale 服务器(官网:Headscale Official Website),并结合国内的 DERP 中转服务器实现稳定的低延迟异地组网。


第一步:网络组网原理与前期准备

1. 为什么需要自建 Headscale 服务器与中转服务?

Tailscale 的服务器端主要分为两部分:

  • Headscale 服务器(服务端) :负责节点的分配、通信与认证,其工作模式细节可以参考 Control and Data Planes 官方文档
  • DERP 中转服务器(中转站) :当两个节点无法通过 STUN 打洞直连时,通过中转服务器转发流量。自建国内中转节点能确保打洞失败时依然拥有低延迟。

2. NAT 网络类型对直连的影响

在国内的家庭及办公网络中,NAT 类型主要为 Full Cone(NAT1)、Port Restricted Cone(NAT3)和 Symmetric(NAT4)。

  • 如果两端 NAT 类型在 NAT3 及以上 ,通常可以顺利建立 P2P 直连隧道。
  • 如果一端为 NAT4(对称型) ,由于其端口分配机制严格,通常无法打通直连,此时流量必须完全通过 DERP 服务器进行中转。
  • IPv6 优化直连 :IPv6 具有去 NAT 化的天然优势。如果自建的 DERP 节点配置了 IPv6 地址,且客户端也同样具有 IPv6 地址,则可以通过 IPv6 直接实现 STUN 打洞直连,大幅提升网络互联质量。
    • *

第二步:使用 Docker 部署 Headscale 服务器

Headscale 是 Tailscale 协调服务端的开源实现。容器的详细部署说明可参考 Headscale Container Installation Guide

1. 编写 Docker Compose 文件

在 1Panel 应用管理或目标服务器工作目录下,建立 docker-compose.yml 配置文件:

services:
  headscale:
    image: docker.io/headscale/headscale:latest
    restart: always
    container_name: headscale
    read_only: true
    tmpfs:
      - /var/run/headscale
    ports:
      - "8080:8080"           # Headscale 服务主端口
      - "9090:9090"           # Metrics 运行报告端口
    volumes:
      - ./config:/etc/headscale:ro
      - ./lib:/var/lib/headscale
    command: serve
    healthcheck:
      test: ["CMD", "headscale", "health"]
      interval: 10s
      timeout: 5s
      retries: 3

2. 配置 Headscale 参数(config.yaml

在挂载的 ./config 目录下创建主配置文件 config.yaml。您可以基于官方给出的 Headscale Official Example Config 进行修改,核心参数如下:

server_url: https://headscale.example.com  # 客户端连接的 Headscale 服务域名

listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090

noise:
  # 用于 Noise 加密协议的私钥路径
  private_key_path: /var/lib/headscale/noise_private.key

prefixes:
  # 虚拟局域网的内网 IP 分配段
  v4: 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48
  allocation: sequential

derp:
  # 关闭内置 DERP 服务,我们使用后续独立的高性能 DERP 容器
  server:
    enabled: false
  # 从官方 URL 引入中转节点映射表
  urls:
    - https://controlplane.tailscale.com/derpmap/default
  # 从本地配置文件引入自定义的 DERP 服务器
  paths:
    - /etc/headscale/derp.yaml
  auto_update_enabled: true
  update_frequency: 3h

database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite
    write_ahead_log: true                # 开启 SQLite WAL 模式以提升数据库并发性能
    wal_autocheckpoint: 1000

dns:
  magic_dns: true                        # 开启虚拟机局域网 MagicDNS
  base_domain: internal.net              # 虚拟网根域名
  override_local_dns: true
  nameservers:
    global:
      - 223.5.5.5                        # 公共 DNS 设置
      - 119.29.29.29

unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"

注:关于内置 DERP 服务的开启细节与参数解释,可参考 Headscale Embedded DERP Configuration


第三步:部署可视化管理页面(Headscale-UI)

为了避免频繁在命令行维护节点和用户,推荐部署官方支持的可视化前端面板 Headscale-UI 项目。详情请见 Headscale Web-UI Integration Guide

在刚才的 docker-compose.yml 中追加以下服务:

  headscale-ui:
    image: ghcr.io/gurucomputing/headscale-ui:latest
    restart: always
    container_name: headscale-ui
    ports:
      - "8081:8080" # 映射外部 8081 端口以避免占用 8080

第四步:自建国内独立的 DERP 中转服务器

对于独立的自建 DERP 服务器,为了方便维护,我们选择使用第三方 Docker 镜像。构建方案及命令行参数可参考 Tailscale Custom DERP Server Reference 官方文档以及 Sparanoid DERP Docker Image

1. 编写 DERP 服务docker compose

services:
  derp:
    image: sparanoid/derp:master
    restart: always
    init: true
    ports:
      - "80:80"
      - "443:443"            # DERP/HTTPS 协议端口
      - "3478:3478/udp"      # STUN 服务的 UDP 端口
    volumes:
      - ./ssl:/app/certs     # 挂载您的域名 SSL 证书
      - /var/run/tailscale:/var/run/tailscale
    command: sh -c "
      derper \
        -hostname derp.example.com \
        -certdir /app/certs \
        -certmode manual \
        -verify-clients=true \
        -verify-client-url=https://headscale.example.com/verify \
        -verify-client-url-fail-open=true"

2. 客户端验证参数设置(防止他人薅羊毛)

为了防止自建中转服务被他人匿名蹭网,可以使用参数来指定验证服务器:

  • -verify-client-url(验证客户端 URL 参数):

将其指向您的 Headscale 服务器验证接口(例如 https://headscale.example.com/verify)。当中转服务器收到客户端连接请求时,会向 Headscale 确认该客户端是否属于您注册的节点。如果确认属于合法设备则允许接入,否则直接拒绝。

  • -verify-client-url-fail-open(故障降级开路):

用于控制当 Headscale 服务器暂时无法访问时的中转策略。设置为 true 时,若校验接口连接失败,中转服务器将默认允许客户端继续连接以维持网络可用性;设置为 false,则在无法联系 Headscale 服务时拒绝新的连接。

更多中转控制设计详见 Headscale 官方中转文档

3. 配置本地中继节点映射表

在 Headscale 配置文件夹下创建 derp.yaml,以便将您自建的独立中转站告知其他设备:

regions:
  901:
    regionid: 901
    regioncode: "custom-derp"
    regionname: "Custom Private DERP"
    nodes:
      - name: "derp-node-01"
        regionid: 901
        hostname: "derp.example.com"
        ipv4: "x.x.x.x"                # 您的 DERP 服务器公网 IPv4
        ipv6: "y:y:y::y"               # 您的 DERP 服务器公网 IPv6(可选)
        stunport: 3478
        stunonly: false
        derpport: 443

第五步:配置 Nginx 反向代理与跨域设置

由于自建的管理页面 Headscale-UI 运行在您的本地浏览器中,它通过浏览器向 Headscale API 发起连接。为了确保两者正常通讯,且避免跨域(CORS)问题,如果您在 1Panel 的 Nginx 反向代理配置中绑定了 Headscale 服务,请务必添加以下规则:

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header REMOTE-HOST $remote_addr;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_http_version 1.1;

    # 1. 允许跨域的配置(请将地址替换为 Headscale-UI 的公网访问源地址)
    add_header 'Access-Control-Allow-Origin' 'https://headscale-ui.example.com' always; 
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; 
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; 
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; 

    # 2. 拦截并处理浏览器的 OPTIONS 预检请求,直接返回 204
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' 'https://headscale-ui.example.com' always; 
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; 
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; 
        add_header 'Access-Control-Max-Age' 1728000; 
        add_header 'Content-Type' 'text/plain; charset=utf-8'; 
        add_header 'Content-Length' 0; 
        return 204; 
    }
}

第六步:在不同客户端安装并连接自建服务器

各系统所需的客户端安装包可从 Tailscale Official Download Page 进行下载。客户端的基本接入指引详见 Headscale Getting Started Guide

1. Windows / Linux / macOS 连接自建服务器

安装客户端后,打开命令行,执行登录命令引导注册:

# 作为独立设备接入
tailscale up --login-server=https://headscale.example.com --accept-dns=true --accept-routes=true

2. 广播子网路由(外网直接访问家里/公司网段内其他设备)

如果您希望该设备充当网关,将内网段(如 192.168.1.0/24)路由到自建虚拟局域网:

tailscale up --login-server=https://headscale.example.com --exit-node-allow-lan-access --advertise-routes=192.168.1.0/24 --accept-dns=true --accept-routes=true

注:发布宣告后,需登录 Headscale-UI 网页管理端,在 Routes 页面中手动 Approve(通过)该子网广播。

3. OpenWRT 路由器等边缘端设备内存调优

由于部分网关设备内存较小,而 Go 编写的客户端在复杂互联环境下容易消耗过多系统内存,可通过设置环境变量来约束 Tailscale 使用的内存上限:

# 约束 Go 进程堆内存分配
export GOMEMLIMIT=100MiB
tailscale up --login-server=https://headscale.example.com --accept-routes=true

4. 手机端接入

  • Android / iOS :下载安装客户端后,依次进入高级菜单并配置 “Alternate Server” 地址为 https://headscale.example.com,完成授权后即可无交互注册。
    • *

第七步:常用运维管理命令与连接测试速查

以下为日常管理中,在 Headscale 容器内运行的常见维护命令(可使用 docker exec):

1. 创建命名空间(用户)与密钥生成

# 1. 创建新用户
docker exec headscale headscale users create <username>

# 2. 生成多端可复用的免交互预授权 Key(设置有效期为 10 年)
docker exec headscale headscale preauthkeys create --user <username> --expiration 87600h --reusable

# 3. 创建用于 Web 界面登录的长期 API Key
docker exec headscale headscale apikeys create --expiration 87600h

2. 测试和诊断网络连接方式

要查看客户端之间是实现了低延迟点对点直连还是走自建 DERP 中转转发,可在客户端运行以下命令:

# 查看各个客户端节点之间的直连通路(显示 direct 为 P2P 打洞直连,derp 则为走中转服务器)
tailscale status --peers

# 执行网络测速与 STUN 状态检查,获取和当前各个中转节点的延迟
tailscale netcheck

通过上述命令,您可以持续监控自建私有网络的质量并对路由配置进行微调。

参考文章:

https://cloud.tencent.com/developer/article/2457464
https://blog.csdn.net/weixin_47540149/article/details/157373807

标签: none

评论已关闭