yyhhyy's blog

yyhhyy

Tailscale+Headscale+自建Derp踩坑记录

376
2024-08-07

1. 缘由

最近想拉通家里与办公场地的网络(组网),先是用的wg,因为我自己用的ios surge 只支持wg(不然我应该会直接上Tailscale哈哈),然后就先拉通了wg回家,但是吧,感觉wg好麻烦,每次都得进wg服务端生成个密钥才能授权认证等,相对繁琐。因此有了本次折腾。

2. 什么是Tailscale

Tailscale 是一种基于 WireGuard 的虚拟组网工具,WG的势头目前确实越来越好。那Tailscale相比WG有什么优势呢?

  • 装个客户端就能用了

    • 无需配置防火墙

    • 没有额外的配置

  • 高安全性/私密性

    • 自动密钥轮换

    • 点对点连接

    • 支持用户审查端到端的访问记录

  • 在原有的 ICE、STUN 等 UDP 协议外,实现了 DERP TCP 协议来实现 NAT 穿透

但是相比于WG还是有一定缺点的,Tailscale 相比于内核态 WireGuard 性能会有所损失

其次的话就是 Tailscale 并不是完全免费

对于免费账户,他携带100个免费设备的资格 如果个人使用 完全足够咯

(这里就不探讨开源不开源了问题啦 个人是觉得 都ok 只不过 Tailscale的控制服务没开源罢了

当然如果不想经过Tailscale这层审计的话 自建Headscale是一个不错的选择 而我也选择这个方式

3. 什么是Headscale

Headscale 是一个由 Juan Font 开发的开源项目,它使用 Go 语言编写并在 BSD 许可下发布。它旨在实现 Tailscale 控制服务器的所有主要功能,并可以在企业内部进行部署。以下是一些关于 Headscale 的关键点:

  1. 功能实现:Headscale 实现了 Tailscale 控制服务器的主要功能,允许用户创建和管理虚拟网络,使设备之间能够安全地通信。

  2. 内部部署:与 Tailscale 不同,Headscale 可以在企业内部部署,不需要依赖外部的控制服务器。这意味着企业可以完全控制自己的网络基础设施。

  3. 无限设备支持:Headscale 不限制设备的数量,可以根据需要扩展网络,这对大型组织特别有利。

  4. 网络流量控制:所有的网络流量都由用户自己控制,不经过第三方服务器,这增强了隐私性和安全性。

  5. 开源和许可:Headscale 是开源的,在 BSD 许可下发布,这意味着任何人都可以自由地使用、修改和分发该软件。

  6. 使用场景:Headscale 适用于需要高度控制和安全性的环境,例如企业内部网络、私有云基础设施或任何不希望将网络流量通过第三方的情景。

通过这些特性,Headscale 提供了一个强大的、灵活的解决方案,使用户能够享受 Tailscale 的便利,同时保持对其网络的完全控制。

4. Headscale部署

网上很多不同方式的部署教程,这边只记录自己总结以及在自己的Debian服务器的折腾记录。

官网提供了两种不同的文档,一种是目前沿用的新版本的采用 .dev 的方式一键安装的形式,不过此版本有一定的坑,具体也是在后续启动headscale的时候没法启动(针对我自己的环境而言,因此我使用了二进制的安装方式)

这边罗列几篇部署参考的教程文档:

而我也是根据这几篇教程慢慢踩坑过来的。

截止到 2024/8/7 官网releases版本是 0.22.3 ,因此接下来都是以该版本进行

首先下载二进制文件

# 下载需要魔法 自行解决
wget -O /usr/local/bin/headscale https://github.com/juanfont/headscale/releases/download/v0.22.3/headscale_0.22.3_linux_amd64

给予二进制文件可执行权限

# 简单声明下为什么要a all_user 只是偷个懒 因为启动的时候不以root权限执行
chmod a+x /usr/local/bin/headscale

创建需要用到的文件夹

# 用来存放headscale的配置文件,以及后续自建derp也放这里(derp没说必须放这里 只是放一起好找)
mkdir /etc/headscale/
# 用于存放 Headscale 服务在运行时需要的临时文件,如 UNIX 域套接字文件 headscale.sock。
mkdir -p /var/run/headscale/

下载二进制同版本的示例配置文件

wget -O /etc/headscale/config.yaml https://raw.githubusercontent.com/juanfont/headscale/v0.22.3/config-example.yaml

创建 SystemD service 配置文件(简单理解就是后台运行的文件啦)

cat > /etc/systemd/system/headscale.service << EOF
[Unit]
Description=headscale controller
After=syslog.target
After=network.target

[Service]
Type=simple
User=headscale
Group=headscale
ExecStart=/usr/local/bin/headscale serve
Restart=always
RestartSec=5

# Optional security enhancements
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
WorkingDirectory=/var/lib/headscale
ReadWritePaths=/var/lib/headscale /var/run/headscale
AmbientCapabilities=CAP_NET_BIND_SERVICE
RuntimeDirectory=headscale

[Install]
WantedBy=multi-user.target
EOF

Headscale本身的作用实际就是取代Tailscale的控制中心的作用,因此不需要root权限

这边有个,我直接用root用户启动的时候各种报错,哪怕修改了 headscale.service 中的 User 也是报错,因此我不建议使用root用户,而是采用专门建立的 headscale 用户来进行下一步操作。

创建 headscale 用户

# 创建一个系统用户 "headscale"
useradd \
  --create-home \  # 如果主目录不存在,则创建主目录
  --home-dir /var/lib/headscale/ \  # 指定用户的主目录为 /var/lib/headscale/
  --system \  # 创建一个系统账户
  --user-group \  # 为用户创建一个同名的用户组
  --shell /usr/sbin/nologin \  # 禁止用户登录
  headscale

创建空的 SQLite 数据库文件和 derp配置文件,并给予 headscale 用户有这些文件夹/文件的权限

touch /var/lib/headscale/db.sqlite /etc/headscale/derp.yaml
chown -R headscale:headscale /var/run/headscale/ /var/lib/headscale
chmod a+r /etc/headscale/config.yaml /etc/headscale/derp.yaml

修改headscale的配置文件 /etc/headscale/config.yaml 这边只罗列需要修改的部分

server_url: http://<公网ip 或者ddns的域名>:8080

# 0.0.0.0才能监听到  当然可以用公网ip  或者  网卡的ip
listen_addr: 0.0.0.0:8080

# ip段范围(我用不到v6 所以注释)
ip_prefixes:
  # - fd7a:115c:a1e0::/48
  - 100.64.0.0/10

dns_config:
# 改为 false 不覆盖本地 DNS
  override_local_dns: false
  # 关闭 magic_dns
  magic_dns: false
  # 设置为你自己的标识,否则后续 tailscale 端连接上显示是 user@example.com
  # 比如我的域名是 ddns.cn 那客户端显示就的就是 user@ddns.cn  这里可改可不改  我是顺手改了
  base_domain: ddns.cn
# 随机端口要打开, tailscale 客户端会使用41641 端口建立 wireguard 链接,这个端口会被中间网络设备阻止
randomize_client_port: true

启动 headscale daemon 进程

# 测试文件 看看输出是否正常
headscale configtest

# 测试启动一次
headscale serve
# 这边需要注意  ctrl + c 的时候  请耐心等待自己结束进程  不然会出现很奇怪的问题  将会导致后续 systemd启动失败
# 配置文件没问题就 ctrl + c 取消掉使用 systemd 启动(后台静默运行)
systemctl daemon-reload
systemctl enable --now headscale
# 查看运行状态是否正常启动
systemctl status headscale

5. 客户端接入

5.1 创建用户

# default 自己取
headscale user create default

5.2 生成 pre-authkey 的 key

# 生成一个过期时间 365d 且可以重复使用的 authkey (因为我是自用  所以创建天数多点 方便) 不然正常24h就很多了  安全
headscale preauthkeys --user default create --reusable --expiration 365d 
# 查看下创建的authkey
headscale preauthkeys --user default list
ID | Key                                              | Reusable | Ephemeral | Used | Expiration          | Created             | Tags
1  | 4fe241dxxxxxxxxx | true     | false     | true | 2025-08-06 06:30:50 | 2024-08-06 06:30:50 | 

5.3 Windows接入

下面是 tailscale up 时候一些常用通用选项:

  • --login-server: 指定使用的中央服务器地址(必填)

  • --advertise-routes: 向中央服务器报告当前客户端处于哪个内网网段下, 便于中央服务器让同内网设备直接内网直连(可选的)或者将其他设备指定流量路由到当前内网(可选),多条路由英文逗号隔开

  • --accept-routes: 是否接受中央服务器下发的用于路由到其他客户端内网的路由规则(可选)

  • --accept-dns: 是否使用中央服务器下发的 DNS 相关配置(可选, 推荐关闭)

  • --hostname: 设置 machine name,否则默认会以 hostname 注册上去,特别安卓的 hostname 无法修改

tailscale cli 官方文档 https://tailscale.com/kb/1080/cli,也可以自己 tailscale --help 看命令帮助。

执行 tailscale up 需要 在 Tailscale 的安装目录 比如 D:\software\Tailscale 进行cmd

贴一个自用的命令

tailscale up --login-server <server_url> --hostname <自己取 需要英文> --accept-routes=true --accept-dns=false --authkey <就是刚刚生成的 authkey>

还有一些简单的命令 这些命令都需要在 安装目录下的cmd 进行 使用 不然就得加个 path 环境变量

# 这个可以看derp服务 用的哪条  哪些
tailscale netcheck

# 这个可以看状态  可以看看是否打洞成功还是走的derp服务等
tailscale status

# 用来ping测试
tailscale ping <headscale 分配的ip>

至此 headscale 部署就完成了 并且也能够接入不同的客户端了 但是但是 有个很严重的问题 由于 Tailscale和Headscale在国内都没有derp服务,而且说句实话,用人家的也觉得不是很安全,且使用的时候很卡ping测试都是上1000ms的 因此,我们需要自建derp中继服务

6. Derp部署

6.1 使用域名容器(推荐)

这种方案需要满足以下几个条件:

  • 要有自己的域名,并且申请了 SSL 证书

  • 需要准备一台或多台云主机

  • 如果服务器在国内,域名需要备案

如果以上条件都俱备,就可以按照下面的步骤开始部署了。

使用 yangchuansheng 构建的docker进行部署

docker run --restart always \
  --name derper -p 12345:12345 -p 3478:3478/udp \
  -v /root/.acme.sh/xxxx/:/app/certs \  # 这边并不需要用acme 自己申请证书 放到目录用来映射就行了 比如 /root/cert/ddns.cn/
  -e DERP_CERT_MODE=manual \
  -e DERP_ADDR=:12345 \
  -e DERP_DOMAIN=xxxx \
  -d ghcr.io/yangchuansheng/derper:latest

注意点:

  • 默认情况下也会开启 STUN 服务,UDP 端口是 3478

  • 防火墙需要放行端口 12345 和 3478;

  • 准备好 SSL 证书;

  • 域名部分我打了码,请换成你自己的域名。就是把 xxxx 替换成形如 ddns.cn

证书的话命名有严格要求的:

假设你的域名是 ddns.cn,那么证书的名称必须是 ddns.cn.crt一个字符都不能错!同理,私钥名称必须是 ddns.cn.key一个字符都不能错

查看docker日志

docker logs -f derper
2024/08/06 12:50:07 no config path specified; using /var/lib/derper/derper.key
2024/08/06 12:50:07 derper: serving on :12345 with TLS
2024/08/06 12:50:07 running STUN server on [::]:3478

部署好 derper 之后,就可以修改 Headscale 的配置来使用自定义的 DERP 服务器了。Headscale 可以通过两种形式的配置来使用自定义 DERP:

  • 一种是在线 URL,格式是 JSON,与 Tailscale 官方控制服务器使用的格式和语法相同。

  • 另一种是本地文件,格式是 YAML

# /etc/headscale/derp.yaml
regions:
  901:
    regionid: 901
    regioncode: home 
    regionname: Home XX 
    nodes:
      - name: 901a
        regionid: 901
        hostname: <就是上面的域名>
        ipv4: <公网ip  如果你跟我一样是ddns  那就写ddns的域名就行>
        stunport: 3478
        stunonly: false
        derpport: 12345

配置说明:

  • regions 是 YAML 中的对象,下面的每一个对象表示一个可用区,每个可用区里面可设置多个 DERP 节点,即 nodes

  • 每个可用区的 regionid 不能重复。

  • 每个 nodename 不能重复。

  • regionname 一般用来描述可用区,regioncode 一般设置成可用区的缩写。

  • ipv4 字段不是必须的,如果你的域名可以通过公网解析到你的 DERP 服务器地址,这里可以不填。如果你使用了一个二级域名,而这个域名你并没有在公共 DNS server 中添加相关的解析记录,那么这里就需要指定 IP(前提是你的证书包含了这个二级域名,这个很好支持,搞个泛域名证书就行了)。

  • stunonly: false 表示除了使用 STUN 服务,还可以使用 DERP 服务。

接下来还需要修改 Headscale 的配置文件,引用上面的自定义 DERP 配置文件。需要修改的配置项如下(只粘贴了需要修改的地方!!!):

# /etc/headscale/config.yaml
derp:
  server:
    # 不启用官网自带的derp
    enabled: false
  urls:
    # - https://controlplane.tailscale.com/derpmap/defaul
  paths:
    - /etc/headscale/derp.yaml
  # paths: []

修改完这两份配置后 需要重启headscale服务 (一般重启比较慢 慢慢等就行 )

systemctl restart headscale

之后在客户端执行 tailscale netcheck 就可以看到自己目前使用的DERP服务了

tailscale netcheck

Report:
        * UDP: true
        * IPv4: yes, xxxxx:11874
        * IPv6: no, but OS has support
        * MappingVariesByDestIP:
        * PortMapping:
        * Nearest DERP: Home xxx
        * DERP latency:
                - home: 31.8ms  (Home xxx)

输出类似以上。

这个命令实际上只检测了 3478/udp 的端口, 就算 netcheck 显示能连,也不一定代表 12345 端口可以转发流量。最简单的办法是直接打开 DERP 服务器的 URL:https://xxxx:12345,如果看到如下页面,且地址栏的 SSL 证书标签显示正常可用,那才是真没问题了。

6.2 使用纯IP容器

其实大部分人都有域名,但是呢都没备案,这点就很头疼了,只能用IP(针对国内服务器)。

但是但是但是,使用纯IP容器部署的话,有一个天坑,就是derper容器和headscale服务,不能处于同一台服务器中,用了一个下午才踩完的坑= = 这点一定要切记!!!!!! 如果你只有一台公网服务器,那么已经可以不用往下看了!!!!!!

使用 yangchuansheng 构建的纯IP docker进行部署

docker run --restart always --name derper -d -p 59443:443 -p 3478:3478 -p 3478:3478/udp ghcr.io/yangchuansheng/ip_derper

记得放行 594433478 这两个端口

这里贴一个作者的docker日志(懒得再新建一个容器了)

docker logs -f derper
Generating a RSA private key
.......................................+++++
..............+++++
writing new private key to '/app/certs//127.0.0.1.key'
-----
2022/03/26 14:30:31 no config path specified; using /var/lib/derper/derper.key
2022/03/26 14:30:31 derper: serving on :443 with TLS
2022/03/26 14:30:31 running STUN server on [::]:3478

接下来就是配置Derp的配置文件咯。

Headscale 的本地 YAML 文件目前还不支持这个配置项,所以没办法,目前只能使用在线 URL 了。(这点我没有验证,沿用作者的说法,不想在折腾这个纯IP了= =)

{
  "Regions": {
    "901": {
      "RegionID": 901,
      "RegionCode": "ali-sh",
      "RegionName": "Aliyun Shanghai",
      "Nodes": [
        {
          "Name": "901a",
          "RegionID": 901,
          "DERPPort": 443,
          "HostName": "xxxx",
          "IPv4": "xxxx",
          "InsecureForTests": true
        }
      ]
    }
  }
}

配置说明:

  • HostName 直接填 derper 的公网 IP,即和 IPv4 的值相同。

  • InsecureForTests 一定要设置为 true,以跳过域名验证。

需要把这个 JSON 文件变成 Headscale 服务器可以访问的 URL,比如在 Headscale 主机上搭个 Nginx,或者上传到对象存储(比如阿里云 OSS)。

一样的修改Headscale 的配置文件

# /etc/headscale/config.yaml
derp:
  server:
    # 不启用官网自带的derp
    enabled: false
  urls:
    - https://xxxxx/derp.json
  paths:
    # - /etc/headscale/derp.yaml
  # paths: []

修改完这两份配置后 需要重启headscale服务 (一般重启比较慢 慢慢等就行 )

systemctl restart headscale

之后在客户端执行 tailscale netcheck 就可以看到自己目前使用的DERP服务了

tailscale netcheck

Report:
        * UDP: true
        * IPv4: yes, xxxxx:11874
        * IPv6: no, but OS has support
        * MappingVariesByDestIP:
        * PortMapping:
        * Nearest DERP: Home xxx
        * DERP latency:
                - home: 31.8ms  (Home xxx)

输出类似以上。

这个命令实际上只检测了 3478/udp 的端口, 就算 netcheck 显示能连,也不一定代表 12345 端口可以转发流量。最简单的办法是直接打开 DERP 服务器的 URL:https://xxxx:12345,如果看到如下页面,且地址栏的 SSL 证书标签显示正常可用,那才是真没问题了。

7. 踩坑总结

  1. 建议不要用root 直接运行 headscale 否则会有很多奇奇怪怪的问题 用一上午的踩坑经验出来的

  2. 使用纯IP 一定要把derper建在非headscale服务所在的服务,否则大大的问题 用一下午的踩坑经验出来的

至此,教程结束,希望能够在本文的经验下,能够帮助到大家!