自建加密网络:Tailscale + Headscale 从零到打通

Headscale是Tailscale的开源替代方案,可以把协调服务器架在自己的VPS上。本文从部署到DERP中继,再到多设备接入,手把手教你自建加密虚拟局域网。

为什么你需要自建加密网络

如果你家里有一台NAS、一台装了Ollama的推理服务器、一台Windows开发机,还有一个挂在墙角吃灰的树莓派。你当然想让它们在任何地方都能互相访问——但是端口映射风险大,frp中转依赖第三方服务器,商业VPN又贵又不透明。

解决方案只有一个:在自己的服务器上搭一个加密网络控制平面。

去年我开始折腾Tailscale,用了大半年确实爽,但免费版100个节点的限制对我不是问题,真正让我下定决心自建的原因是:控制权。Tailscale的协调服务器(coordination server)在海外,万一哪天连不上了,你整个网络就瘫痪了。而Headscale完美解决了这个问题——它是Tailscale的开源替代方案,协议完全兼容,你可以把协调服务器架在自己的VPS上。

准备工作:你需要什么

  • 一台有公网IP的VPS(本文以LA节点为例,1核1G足矣,Headscale本身极轻量)
  • 一个自己的域名(用于HTTPS证书和DERP中继域名)
  • 各客户端设备(Linux/Windows/macOS/手机)

第一步:部署 Headscale 控制服务器

Headscale的部署方式非常灵活,官方推荐直接二进制文件,但为了方便管理和升级,我选择Docker Compose方式。

# 创建目录
mkdir -p /opt/headscale
cd /opt/headscale

# 创建 docker-compose.yml
cat <<'EOF' > docker-compose.yml
version: '3.8'

services:
  headscale:
    image: headscale/headscale:latest
    container_name: headscale
    restart: unless-stopped
    ports:
      - "8080:8080"
      - "3478:3478/udp"
    volumes:
      - ./config:/etc/headscale
      - ./data:/var/lib/headscale
    command: headscale serve
EOF

# 创建配置目录
mkdir -p config data

# 下载默认配置并修改
docker run --rm headscale/headscale:latest headscale config > config/config.yaml

然后编辑 config/config.yaml,关键配置项如下:

server_url: https://hs.yourdomain.com:8080
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
grpc_listen_addr: 0.0.0.0:50443

# DERP 服务器配置
derp:
  server:
    enabled: true
    region_id: 999
    region_code: "hs"
    region_name: "Headscale Embedded DERP"
    stun_listen_addr: "0.0.0.0:3478"
  urls: []
  paths: []
  auto_update_enabled: true

dns:
  magic_dns: true
  base_domain: yourdomain.com

# 禁止未授权的节点加入
acl_policy_path: ""
docker compose up -d

第二步:配置反向代理(自动HTTPS)

Headscale默认监听8080端口,但直接用IP+端口既不安全也不优雅。我用Caddy做反向代理,自动申请Let’s Encrypt证书。

# 在宿主机上安装Caddy或使用Docker版
cat <<'EOF' > /etc/caddy/Caddyfile
hs.yourdomain.com {
    reverse_proxy localhost:8080
    
    header / {
        # 安全头
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
        Referrer-Policy "strict-origin-when-cross-origin"
    }
}
EOF

systemctl reload caddy

需要注意的是,Caddy会自动申请并续期HTTPS证书,比手动配置Nginx+acme.sh省心太多。如果你还在用Nginx手动配证书,强烈建议试试Caddy,一条配置顶Nginx十行,这一点之前的文章已经详细说过了。

第三步:注册第一个节点

Headscale采用预授权密钥(pre-auth key)的方式管理节点准入。每次新设备加入网络都需要一个有效的key,你可以控制key的有效期和标签。

# 进入容器
docker exec -it headscale headscale

# 创建命名空间(Tailscale称之为User,Headscale称之为Namespace)
headscale namespaces create default

# 生成预授权密钥(有效期一年)
headscale preauthkeys create -n default --expiration 2027-06-28

# 输出类似:e5c8c9b8f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6

这里有个教训:不要用90天有效期。之前我用90天有效期,结果每隔三个月就要重新生成一次key、重新登录所有设备,折腾了几轮之后彻底烦了。直接一年起,省心。

然后在客户端执行:

# Linux 客户端
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server=https://hs.yourdomain.com --authkey=e5c8c9b8f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6

# Windows 客户端
# 下载Tailscale Windows客户端,安装后在命令行执行:
tailscale up --login-server=https://hs.yourdomain.com --authkey=你的key --accept-dns=false

Headscale节点列表示例

第四步:DERP中继——绕过UDP封锁

这是整个部署中最容易踩坑的一步,没有之一。

Tailscale/Headscale默认优先尝试STUN打洞建立P2P直连。直连不走服务器中转,延迟最低、带宽最大。但现实很骨感——大多数公司网络和部分家庭网络对UDP包做了严格的限制甚至完全封锁。

我遇到的情况:办公室网络UDP全封,所有节点之间的直连全部失败,只能依赖中继。Headscale自带了嵌入式DERP服务(就是上面配置中region_id: 999那个),但你需要确保两点:

1. VPS的3478 UDP端口必须开放

# 检查端口是否可达
nc -z -u your-vps-ip 3478

2. 客户端必须能访问3478端口做STUN探测

如果客户端也只能TCP出站(办公室防火墙连UDP都不放行),那连STUN都跑不通。这时有两个选择:

方案A:使用 udp2raw 包装UDP流量(推荐)

udp2raw是一个能把UDP包伪装成TCP流量的隧道工具,专门用来过防火墙。简单的单向用法:

# 服务端(VPS上)
udp2raw -s -l 0.0.0.0:28900 -r 127.0.0.1:3478 --raw-mode faketcp -k "your_password" &

# 客户端
udp2raw -c -l 0.0.0.0:3478 -r your-vps-ip:28900 --raw-mode faketcp -k "your_password" &

这样客户端访问本机的3478端口,udp2raw负责把流量伪装成TCP送到VPS,VPS再解包转发给DERP服务。整个过程对Tailscale透明。

方案B:自建外部DERP服务器

如果不想用Headscale自带的嵌入式DERP,可以单独启动一个DERP实例:

docker run -d \
  --name derper \
  --restart unless-stopped \
  -p 443:443 -p 3478:3478/udp \
  -v /root/certs:/certs \
  -e DERP_ADDR=:443 \
  -e DERP_DOMAIN=derp.yourdomain.com \
  ghcr.io/tailscale-dev/derper:main

然后在Headscale配置中引用这个DERP即可。

小提示:可以通过 tailscale status 查看节点连接状态。如果看到 relay 字样就代表走的是中继,direct 才是直连。

$ tailscale status
100.64.0.1 node-01 linux active; direct 192.168.1.5:41641
100.64.0.7 win-vm windows active; relay "hs" via 999

第五步:接入更多设备

Linux服务器/树莓派

这个最简单,Tailscale官方的Linux安装脚本一行搞定:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server=https://hs.yourdomain.com --authkey=你的key

注意:如果这台服务器之前注册过Tailscale官方的网络,需要先退出:

sudo tailscale logout

Windows客户端

Windows下有个坑:Tailscale默认接管DNS,会修改系统的DNS设置。如果你有内网自建DNS或者不想被改动,记得加 –accept-dns=false:

tailscale up --login-server=https://hs.yourdomain.com --authkey=你的key --accept-dns=false

QNAP NAS

QNAP安装Tailscale稍微麻烦一些。官方QPKG包可以从Tailscale官网下载,但注意:老版本可能与Headscale不兼容。

安装步骤:

# SSH进QNAP
ssh admin@your-qnap-ip

# 下载最新版QPKG
wget https://pkgs.tailscale.com/stable/tailscale_1.80.x_qpkg.zip

# 安装(通过QNAP的App Center或命令行)
# 安装后tailscale二进制在 /share/CACHEDEV1_DATA/.qpkg/Tailscale/tailscale

# 注册到Headscale
/share/CACHEDEV1_DATA/.qpkg/Tailscale/tailscale up --login-server=https://hs.yourdomain.com --authkey=你的key --accept-dns=false

QNAP的QPKG安装源(qnapclub.eu等)很多已经失效了,直接从Tailscale官方下载最靠谱。

iPhone/iPad(国行)

国行iPhone的App Store搜不到官方Tailscale客户端。第三方有个叫 Scale Manager 的App可以用,支持自定义控制服务器地址。

安装后配置:设置 → 填入你的Headscale服务器地址(https://hs.yourdomain.com),然后用浏览器打开该地址完成授权登录。

如果你有美区Apple ID,切过去直接搜Tailscale官方App更省心。

日常管理命令备忘

# 列出所有节点
docker exec headscale headscale nodes list

# 查看节点详情
docker exec headscale headscale nodes list -i node_id

# 删除过期节点
docker exec headscale headscale nodes delete -i node_id

# 查看预授权密钥
docker exec headscale headscale preauthkeys list -n default

# 登录Headscale Web界面(如果开启了)
# Headscale默认无WebUI,但社区有第三方面板:
# https://github.com/gurucomputing/headscale-ui

几点避坑总结

  1. UDP封锁必须先确认。 在部署DERP之前,用 nc -z -u VPS_IP 3478 测试UDP可达性。UDP不通的中继方案表现会很差,建议先用udp2raw兜底。
  2. Pre-auth key别设90天。 亲测每隔三个月去更新所有设备上的key非常痛苦,一年或永久省心得多。
  3. 域名一定要配HTTPS。 Headscale要求协调服务器必须走HTTPS,纯HTTP连不上。Caddy比Nginx简单得多,自动续约证书不用操心。
  4. Windows客户端记得加–accept-dns=false。 不然Tailscale会改动你的DNS设置,可能导致内网域名解析失败。
  5. 不要用官方Tailscale和控制服务器混用。 一旦设备注册过官方的Tailscale网络,需要先logout才能切换到自建Headscale。而且一台设备不要同时加入两个网络,会产生路由冲突。

自建加密网络之后,最直观的感受就是:所有设备都在一个虚拟局域网里,不管你在家还是在外面。NAS上的文件、本地AI的服务、开发环境,全都是直接访问——不需要开端口、不需要第三方穿透。

下一篇我会分享如何在这个网络上跑更多服务,包括跨节点的Docker编排和分布式AI推理。如果你也在自建网络的过程中踩了什么坑,评论区见。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注