headscale保底设施之DERP中继服务器自建

背景

上次我们完成了headscale私有部署,但是为了保证headscale组网中的稳定和可靠性,为设备p2p连接设置一个保底措施,我们需要自建derp服务器。

什么是DERP

DERP 即 Detoured Encrypted Routing Protocol,这是 Tailscale 自研的一个协议:

  • 它是一个通用目的包中继协议,运行在 HTTP 之上,而大部分网络都是允许 HTTP 通信的。
  • 它根据目的公钥(destination’s public key)来中继加密的流量(encrypted payloads)。

Tailscale 会自动选择离目标节点最近的 DERP server 来中继流量
Tailscale 使用的算法很有趣: __所有客户端之间的连接都是先选择 DERP 模式(中继模式),这意味着连接立即就能建立(优先级最低但 100% 能成功的模式),用户不用任何等待。然后开始并行地进行路径发现,通常几秒钟之后,我们就能发现一条更优路径,然后将现有连接透明升级(upgrade)过去,变成点对点连接(直连)__。

因此, DERP 既是 Tailscale 在 NAT 穿透失败时的保底通信方式(此时的角色与 TURN 类似),也是在其他一些场景下帮助我们完成 NAT 穿透的旁路信道。换句话说,它既是我们的保底方式,也是有更好的穿透链路时,帮助我们进行连接升级(upgrade to a peer-to-peer connection)的基础设施。

Tailscale 官方内置了很多 DERP 服务器,分步在全球各地,惟独不包含中国大陆,原因你懂得。这就导致了一旦流量通过 DERP 服务器进行中继,延时就会非常高。而且官方提供的 DERP 服务器是万人骑,存在安全隐患。

官方内置了很多 DERP 服务器

1
2
3
4
5
6
7
8
9
10
11
12
- tok: 96.6ms  (Tokyo)
- sfo: 179.8ms (San Francisco)
- sea: 181.3ms (Seattle)
- dfw: 218.6ms (Dallas)
- ord: 229.7ms (Chicago)
- nyc: 233.8ms (New York City)
- fra: 257.7ms (Frankfurt)
- lhr: 259.1ms (London)
- sin: 265ms (Singapore)
- syd: 317.1ms (Sydney)
- sao: 352.8ms (São Paulo)
- blr: 368.5ms (Bangalore)

为了实现低延迟、高安全性,我们可以参考 Tailscale 官方文档自建私有的 DERP 服务器。
Custom DERP Servers

部署DERP

还是docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
version: '3.5'
services:
derper:
container_name: derper
image: fredliang/derper
restart: always
volumes:
- ./cert:/cert
ports:
- 3478:3478/udp
- 23479:23479
environment:
DERP_DOMAIN: derp.XXXX.cn
DERP_ADDR: ":23479"
DERP_CERT_MODE: manual
DERP_CERT_DIR: /cert
  • stunport: 3478 默认情况下也会开启 STUN 服务,UDP 端口是 3478
  • derpport: 23479
1
2
3
2022/09/06 01:21:27 no config path specified; using /var/lib/derper/derper.key
2022/09/06 01:21:27 derper: serving on :23479 with TLS
2022/09/06 01:21:27 running STUN server on [::]:3478

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

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

  • 另一种是本地文件,格式是 YAML。
    我们可以直接使用本地的 YAML 配置文件,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # /etc/headscale/derp.yaml
    regions:
    901:
    regionid: 901
    regioncode: gz
    regionname: Tencent Guangzhou
    nodes:
    - name: 901a
    regionid: 901
    hostname: '实际域名'
    ipv4: '可不需要'
    stunport: 3478
    stunonly: false
    derpport: 23479
  • regions 是 YAML 中的对象,下面的每一个对象表示一个可用区,每个可用区里面可设置多个 DERP 节点,即 nodes。
    每个可用区的 regionid 不能重复。

  • 每个 node 的 name 不能重复。

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

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

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

  • hostname和 ipv4部分根据你的实际情况填写。

接下来还需要修改 Headscale 的配置文件,引用上面的自定义 DERP 配置文件。需要修改的配置项如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# /etc/headscale/config.yaml
derp:
# List of externally available DERP maps encoded in JSON
urls:
- https://controlplane.tailscale.com/derpmap/default

# Locally available DERP map files encoded in YAML
#
# This option is mostly interesting for people hosting
# their own DERP servers:
# https://tailscale.com/kb/1118/custom-derp-servers/
#
# paths:
# - /etc/headscale/derp-example.yaml
paths:
- /etc/headscale/derp.yaml

# If enabled, a worker will be set up to periodically
# refresh the given sources and update the derpmap
# will be set up.
auto_update_enabled: true

# How often should we check for DERP updates?
update_frequency: 24h

在 Tailscale 客户端上使用以下命令查看目前可以使用的 DERP 服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ tailscale netcheck
Report:
* UDP: true
* IPv4: yes, 14.154.29.252:50713
* IPv6: yes, [::ffff:14.154.29.252]:50713
* MappingVariesByDestIP: false
* HairPinning: false
* PortMapping: UPnP, NAT-PMP, PCP
* Nearest DERP: Tencent Guangzhou
* DERP latency:
- gz: 8.5ms (Tencent Guangzhou) -----> 这就是我们刚建的derp服务器
- tok: 96.6ms (Tokyo)
- sfo: 179.8ms (San Francisco)
- sea: 181.3ms (Seattle)
- dfw: 218.6ms (Dallas)
- ord: 229.7ms (Chicago)
- nyc: 233.8ms (New York City)
- fra: 257.7ms (Frankfurt)
- lhr: 259.1ms (London)
- sin: 265ms (Singapore)
- syd: 317.1ms (Sydney)
- sao: 352.8ms (São Paulo)
- blr: 368.5ms (Bangalore)

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

Tailscale 命令行工具来测试:

查看与通信对端的连接方式及状态

1
2
3
4
tailscale status

100.64.0.1 junyao-lxgc8rs7 junyao macOS -
100.64.0.2 homeds-2xqvj919 junyao linux active; direct [::ffff:14.154.29.252]:41641, tx 518960 rx 7674032

ping一下

1
2
3
4
tailscale ping 100.64.0.2

pong from homeds-2xqvj919 (100.64.0.2) via [::ffff:14.154.29.252]:41641 in 6ms --->通过点对点
pong from homeds-2xqvj919 (100.64.0.2) via DERP(thk) in 104ms --->通过DERP

这个tailscale ping更加友好一点,会直接告诉你是通过DERP中继服务器还是点对点来和对方通信的。

DERP安全

我们总要设置一下DERP的访问权限,不能成为万人骑

步骤:
1、在 DERP 服务器上安装 Tailscale。
2、derper 启动时加上参数 –verify-clients

tip:因为derp是通过本地tailscale进行检测,所以docker部署的derper必须在容器内启有Tailscale客户端的实例,我们把服务器的/var/run/tailscale/tailscaled.sock映射进入derp容器目录中,让derp能检测到tailscale
tip:如果是使用docker安装的tailscale,请将/var/run/tailscale/tailscaled.sock映射出来,然后再映射进入derp,即让derp和tailscale容器共享volume(/var/run/tailscale/tailscaled.sock)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
version: '3.5'
services:
derper:
container_name: derper
image: fredliang/derper
restart: always
volumes:
- ./cert:/cert
- /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
ports:
- 3478:3478/udp
- 23479:23479
environment:
DERP_DOMAIN: derp.webhunt.cn
DERP_ADDR: ":23479"
DERP_CERT_MODE: manual
DERP_CERT_DIR: /cert
DERP_VERIFY_CLIENTS: "true"