Headscale 搭建笔记

前言

Headscale 是 Tailscale 服务端的一种实现,提供了大多数 Tailscale 官方服务器拥有的功能(比如多用户、OAuth认证、出口节点等)。Headscale 最好安装在同时支持 IPv4 与 IPv6 的 VPS 上,IPv6 在可用的情况下能让各个客户端之间做到点对点直连,而无需经过中转服务器。以下是一个以 Alpine Linux 当作服务端、Fedora Linux 当作客户端的笔记。

安装

由于我选择的是 Alpine Linux 系统,所以安装步骤可能与常见的发行版有所不同。

安装 Headscale

安装需要的一些组件:

1
doas apk add curl shadow

然后直接上我自己写的安装脚本:

1
doas sh -c "$(curl -sL https://github.com/MarksonHon/headscale-installer/raw/refs/heads/main/installer.sh)"

查看当前安装的版本:

1
headscale version

安装 Caddy

我选择使用 Caddy 来反向代理 Headscale,这样做的好处是 80 端口与 443 端口还可以承载其它域名与服务,而不用让 Headscale 独占这两个端口。

1
doas apk add caddy

配置

Headscale 配置

文件保存在 /etc/headscale/config.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
---
server_url: https://server_address #域名,Caddy代理后的域名是什么样的,它就什么样的。
listen_addr: 127.0.0.1:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 127.0.0.1:50443
grpc_allow_insecure: false
noise:
private_key_path: /var/lib/headscale/noise_private.key
prefixes:
v6: fd7a:115c:a1e0::/48
v4: 100.64.0.0/10
allocation: sequential
derp:
server:
enabled: true #开启内置中转服务器
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
private_key_path: /var/lib/headscale/derp_server_private.key
automatically_add_embedded_derp_region: true
ipv4: 1.2.3.4 #当前机器的IPv4
ipv6: 2001:db8::1 #当前机器的IPv6
urls:
- https://controlplane.tailscale.com/derpmap/default
paths: []
auto_update_enabled: true
update_frequency: 24h
disable_check_updates: false
ephemeral_node_inactivity_timeout: 30m

database:
type: sqlite
debug: false
gorm:
prepare_stmt: true
parameterized_queries: true
skip_err_record_not_found: true
slow_threshold: 1000
sqlite:
path: /var/lib/headscale/db.sqlite
write_ahead_log: true

acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: ""
tls_letsencrypt_hostname: ""
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
tls_letsencrypt_challenge_type: HTTP-01
tls_letsencrypt_listen: ":http"
tls_cert_path: ""
tls_key_path: ""

log:
format: text
level: info

policy:
mode: file
path: ""

dns:
magic_dns: true
base_domain: example.com
nameservers:
global:
- 1.1.1.1
- 1.0.0.1
- 2606:4700:4700::1111
- 2606:4700:4700::1001
split:
{}
# foo.bar.com:
# - 1.1.1.1
# darp.headscale.net:
# - 1.1.1.1
# - 8.8.8.8
search_domains: []
extra_records: []
use_username_in_magic_dns: false

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

logtail:
enabled: false

randomize_client_port: false

Caddy 配置

Caddy 的配置还是很简单的,直接反向代理一下即可。

文件保存在 /etc/caddy/Caddyfile

1
2
3
4
5
6
7
8
{
email email_address
acme_ca https://acme.zerossl.com/v2/DV90
}

https://server_address {
reverse_proxy localhost:8080
}

由于个人配置,我使用了 ZeroSSL 作为 CA 提供方,理论上这里可以配置为任何支持 ACME 协议的 CA 提供者。

运行

Caddy

1
2
doas rc-update add caddy
doas rc-service caddy start

Headscale

1
2
doas rc-update add headscale
doas rc-service headscale start

然后创建一个 Headscale 用户:

1
doas headscale users create user0

客户端登入

手动验证

在客户端使用以其它服务器登录的功能(移动客户端)或者使用命令行(桌面客户端与命令行工具),然后输入 Headscale 服务器的地址,比如:

1
sudo tailscale login --login-server https://server_address

然后点开客户端提示的地址,再从网页上复制相关命令粘贴到服务器所在的 SSH 会话中完成验证。记住,必须替换用户名为实际存在的 Headscale 用户,比如上文中的 user0。

预验证密钥

Headscale 可以提供一组预制的验证链接,客户端只需要登入这个链接就能加入网络。

在服务端运行:

1
doas headscale preauthkeys create -e 24h --user user0

上面的命令即创建了一个预验证密钥,有效期为 24 小时,用户为 user0。

然后就能在客户端上执行:

1
sudo tailscale login --login-server https://server_address --authkey your_key_just_got

出口节点与路由

宣告路由

要把单独的某个节点配置为出口节点,或者要让该设备同局域网内的其它没有安装 Tailscale 的设备也能被访问到,则可以配置路由功能。

Linux 客户端需要先行开启 IP 转发,开启的方式有多种,可以直接上网搜索。

在客户机上输入:

1
sudo tailscale web

然后按照提示,打开对应的 Tailscale 网页设置界面:

点击右上角进行登录,然后就可以配置了。点击 EXIT NODE 选择 Run as exit node 即可把当前客户端作为出口节点,配置 Subnet Router 可以转发 Tailscale 的访问到其它设备。

服务端接受宣告

客户端宣告路由之后,还需要在服务端接受对应的路由。

服务端查看现有的路由信息:

1
doas headscale routes list

然后,看仔细你要批准的路由的 ID 是多少,然后运行

1
doas headscale routes enable Your_ID

把 Your_ID 替换为实际的 ID。如果想批准多条路由规则,那就重复运行此命令。