headscale:自托管 tailscale control plane(mesh VPN 不依赖第三方)

起因

Tailscale 是 WireGuard-based 的 mesh VPN,体验绝佳:所有设备通过
控制平面"发现"互通,零端口转发。但:

  • 控制平面是 Tailscale 公司的(数据不出公司是大忌行业)
  • 免费层 100 设备限制
  • 隐私敏感场景不想依赖商业服务

headscale 是开源的 Tailscale control plane 实现,自己跑一台
机器就能成为"自己的 Tailscale 公司"。客户端继续用 Tailscale 官方
app(功能完整)+ 连你的 headscale server。

架构

        ┌──────────────┐
        │ headscale    │ ← 自己跑,public 可达
        │ (control)    │
        └──┬──────┬────┘
           │      │
   ┌───────┘      └────────┐
   │                       │
[laptop] ←── mesh ──→ [server]
   │      WireGuard      │
   └─── 互联 ──────────────┘

控制平面只协调 peer discovery + key exchange,不路由数据流量
节点间 P2P 直连(NAT 穿透)或者 P2P 失败时 fallback 到 DERP relay。

装 headscale

需要一台公网可达的服务器(VPS)+ HTTPS(必须)。

# 二进制安装
HEADSCALE_VERSION=0.23.0
curl -fsSL https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64.deb \
    -o headscale.deb
sudo dpkg -i headscale.deb

或 Docker:

# docker-compose.yml
services:
  headscale:
    image: headscale/headscale:0.23.0
    command: serve
    restart: unless-stopped
    ports: ["8080:8080", "9090:9090"]
    volumes:
      - ./config:/etc/headscale
      - ./data:/var/lib/headscale

配置

/etc/headscale/config.yaml(关键字段):

server_url: https://hs.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090

ip_prefixes:
  - 100.64.0.0/10        # tailscale 默认 CGNAT 段
  - fd7a:115c:a1e0::/48

derp:
  server:
    enabled: false        # 自己跑 DERP 可选;先用官方 DERP
  urls:
    - https://controlplane.tailscale.com/derpmap/default

database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite

log:
  level: info

oidc:
  # 可选:用 GitHub / Google / Authelia 等 SSO 登录
  # issuer: https://auth.example.com
  # client_id: ...

用 systemd

sudo systemctl enable --now headscale
sudo systemctl status headscale

需要 HTTPS 反代(nginx / Caddy)。hs.example.com 指向这台机器
+ TLS 证书。

创建 user + 设备 enroll

# 创建 user (一个 user 一组设备)
sudo headscale users create alice

# 客户端那边(macOS / Linux / iOS / Android / Windows)装 Tailscale,
# 但通过 --login-server 指向你的 headscale
sudo tailscale up --login-server https://hs.example.com
# 输出一个授权 URL,但 headscale 不能 auto-auth,要手动 register key:

# 看到 URL:
# To authenticate, visit: https://hs.example.com/register/abc123...
# 复制 nodekey 到 server 上:
sudo headscale nodes register --user alice --key nodekey:abc123...

或者用 pre-auth key(推荐脚本化):

sudo headscale preauthkeys create --user alice --reusable --expiration 24h
# 输出:xxx-yyy-zzz

# 客户端
sudo tailscale up --login-server https://hs.example.com --auth-key xxx-yyy-zzz
# 自动 enroll,无需手动 register

成功后:

sudo headscale nodes list
# ID | Name      | User  | IP            | Online
# 1  | laptop    | alice | 100.64.0.1    | yes
# 2  | server    | alice | 100.64.0.2    | yes

节点间访问

ssh [email protected]    # 通过 mesh VPN 访问
ping 100.64.0.2

或用 MagicDNS:

ssh user@server         # tailnet 内 hostname 自动解析

要开 MagicDNS:

# config.yaml
dns:
  magic_dns: true
  base_domain: tailnet.example.com
  override_local_dns: true
  nameservers:
    global:
      - 1.1.1.1

每节点 tailscale.tailnet.example.com 自动可解析。

ACL(谁能访问谁)

// /etc/headscale/acl.hujson
{
    "acls": [
        // alice 的设备能互访
        {"action": "accept", "src": ["alice"], "dst": ["alice:*"]},

        // bob 只能访问 server
        {"action": "accept", "src": ["bob"], "dst": ["alice:server:22"]},

        // 默认 deny(不写 accept 的都不通)
    ],

    "groups": {
        "group:admins": ["alice"],
    },

    "tagOwners": {
        "tag:prod-server": ["group:admins"],
    },
}
sudo headscale policy set -f /etc/headscale/acl.hujson

类似 K8s NetworkPolicy 但更简单。

subnet routes(让 VPN 节点暴露内网)

server 节点 advertise 内网:

sudo tailscale up --login-server https://hs.example.com \
    --advertise-routes=192.168.1.0/24

headscale 上 approve:

sudo headscale nodes routes list
sudo headscale nodes routes enable -r 1

之后所有 mesh 节点能通过这个 server 访问 192.168.1.x(家庭内网)。
等于"VPN 入口"。

exit nodes(用某节点作出口)

# server 节点 advertise 为 exit node
sudo tailscale up --advertise-exit-node

# headscale approve
sudo headscale nodes routes enable -r 1 --all

# 客户端用:
sudo tailscale up --exit-node=server

客户端流量全部走 server 出公网。"自建 VPN" 替代商业 VPN。

跟 cloud Tailscale 对比

Tailscale Cloud headscale
控制平面 Tailscale 公司 自己
数据流量 不经控制平面 不经控制平面
客户端 app 一样 一样(开源 tailscaled)
ACL UI Web 仪表盘 CLI / hujson 文件
OIDC 完善 基础支持
MagicDNS
DERP relay 全球 + 免费 用官方 DERP 或自建
价格 免费 100 节点 / 团队收费 完全免费
适合 普通团队 大规模 / 隐私敏感

非企业 + 100 节点以内 → 用 cloud Tailscale 省心。

100 设备 / 隐私敏感 / 跑公司基础设施 → headscale 自托管。

与 WireGuard 直接配相比

裸 WireGuard:

  • 每加设备改所有节点 conf
  • 没 NAT 穿透(双 NAT 后设备难互联)
  • 没 MagicDNS
  • 维护 100 节点想哭

Tailscale / headscale:

  • 自动 P2P discovery + NAT 穿透
  • 加设备一行命令
  • ACL 中央管理
  • MagicDNS

mesh 规模上去后 headscale > 裸 WireGuard 远不止一个量级。

实际效果

我家庭 + 公司 + VPS 一共 12 个设备:

  • 手机 / 笔记本 在外能直连家里 NAS(之前要开 OpenVPN 客户端)
  • 公司 / 家里互通(VPC peering 替代品)
  • 一台 VPS 当 exit node:手机走 VPN 翻墙
  • ACL 让"工作笔记本只能访问 work server" 严格隔离
  • headscale 自己跑在一台 $5 / 月 VPS 上,零成本控制平面

完全替代了我之前的 OpenVPN + 维护 WireGuard config。

踩过的坑

  1. server_url 错 → 客户端连不上。必须包含 https:// 和正确域名,
    且证书有效。

  2. 客户端不接受 --login-server 指向 IP:必须用域名 + 证书。

  3. NAT 双层穿不过 → fallback 到 DERP relay。自建 DERP server 加速。

  4. OIDC 配 SSO 时 callback URL 错:headscale callback 是
    /oidc/callback,注册 SSO 时填对。

  5. 0.22 → 0.23 数据库迁移:headscale 大版本升级偶尔需 migrate
    命令。release notes 仔细看。

精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。