Headscale、Derp自建记录
写于2024年10月20日

概述:

  • Headscale 控制服务器,Tailscale控制服务器的开源实现
  • Derp 中继服务器,Tailscale开源中继服务器

假设:

  • C1(Headscale 控制服务器)
  • D1(Derp 中继服务器)
  • 内网1设备A(电脑、手机、NAS等)
  • 内网2设备B(电脑、手机、NAS等)

设备A和设备B同时安装Tailscale客户端,并且连接C1
当A和B可以建立点对点连接时,连接为A⇌B
当A和B无法建立点对点连接时,连接回退为A⇌D1⇌B。如果发现有更优旁路,则又会透明升级为A⇌B

定义:

  • C1(Headscale 控制服务器)
  • D1(Derp 中继服务器)
  • <HEADSCALE_CONTAINER_NAME> headscale容器名称
  • <HEADSCALE_SERVER_NAME> headscale服务域名,默认协议头为https,例如:https://headscale.xxxx.com
  • <DERP_SERVER_NAME> derp服务域名,同上
  • <DERP_SERVER_NAME_HOSTNAME> derp服务域名主机名称,不包含协议头,例如:derp.xxxx.com

下文<>尖括号内容请自行替换变量
下文均以docker方式安装,请确保C1、D1已安装docker

安装Headscale 控制服务器(C1)

当前默认为root目录安装

1
2
3
4
5
mkdir -p /root/headscale/config
mkdir -p /root/headscale/lib
touch docker-compose.yaml
# nginx
mkdir -p /root/nginx/conf.d

docker-compose.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
services:
headscale:
image: headscale/headscale:stable
restart: always
volumes:
# 配置文件目录
- /root/headscale/config:/etc/headscale
# 数据库目录
- /root/headscale/lib:/var/lib/headscale
command: serve
networks:
- lan
# 可选 用于headscale服务web管理
headscale-admin:
image: goodieshq/headscale-admin:latest
restart: always
networks:
- lan
# 可选 反向代理
nginx:
image: nginx:latest
restart: always
volumes:
- /root/nginx/conf.d:/etc/nginx/conf.d
ports:
- 80:80
depends_on:
- headscale
- headscale-admin
networks:
- lan

networks:
lan:
driver: bridge

下载配置文件

1
curl -o /root/headscale/config/config.yaml https://raw.githubusercontent.com/juanfont/headscale/v0.23.0/config-example.yaml

修改配置文件config.yaml,主要为以下几个内容:

  • server_url,解析到当前headscale的域名,修改为<HEADSCALE_SERVER_NAME>
  • prefixes,子网IP网段,如果没有ipv6,就把v6注释掉
  • derp.server,内置的derp服务器,默认为关闭
  • derp.urls,Tailscale的derp服务器url列表,不用可以注释掉
  • derp.paths,本地derp服务器配置文件列表,下面会用到,暂时不作修改
  • dns.magic_dns,子网dns解析功能,不用可以注释掉
  • dns.base_domain,子网设备域名后缀,比如设置为zz,设备A加入后,可以通过A.zz访问
  • randomize_client_port,随机客户端端口,建议设置为true

修改完后保存,然后运行以下命令替换本地IP为0.0.0.0

1
sed -i s/127.0.0.1/0.0.0.0/g /root/headscale/config/config.yaml

nginx配置

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
server {
listen 80;
listen [::]:80;
server_name <HEADSCALE_SERVER_NAME>;

location / {
proxy_pass http://headscale:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
}

location /admin {
proxy_pass http://headscale-admin;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
add_header X-Cache $upstream_cache_status;
add_header Cache-Control no-cache;
}
}

启动服务

1
docker compose up -d

查看是否成功

1
2
docker ps <HEADSCALE_CONTAINER_NAME>
curl $(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <HEADSCALE_CONTAINER_NAME>):9090/metrics

创建用户和APIKEY(用于登录headscale-admin),用户名default可以修改为你喜欢的名称,记得保存输出的API KEY

1
2
docker exec -it <HEADSCALE_CONTAINER_NAME> headscale user create default
docker exec -it <HEADSCALE_CONTAINER_NAME> headscale apikey create

打开浏览器,输入<HEADSCALE_SERVER_NAME>/admin/settings/API URL<HEADSCALE_SERVER_NAME>API Key为上面创建的APIKEY,Legacy API (Headscale < 0.23)打勾去掉,点击保存后就能看到当前headscale服务器的所有用户、节点、路由等信息

安装Derp 中继服务器(D1)

当前默认为root目录安装
安装tailscale客户端(derp开启客户端验证需要本地安装tailscale客户端,如果你不需要开启客户端验证,可以跳过这一步)

1
2
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up --login-server=<HEADSCALE_SERVER_NAME>

通过acme.sh创建证书(没有证书请看这里)

1
2
3
4
mkdir -p /root/derp/ssl
export CF_Key=xxx
export CF_Email=xxx@qq.com
acme.sh --issue -d "<DERP_SERVER_NAME_HOSTNAME>" --fullchain-file /root/derp/ssl/<DERP_SERVER_NAME_HOSTNAME>.crt --key-file /root/derp/ssl/<DERP_SERVER_NAME_HOSTNAME>.key

这里用的是cloudflare的证书,其他服务商的请自行更换环境变量
注意这里一定要用全链证书--fullchain-file,如果使用--cert-file,后面会出现证书链问题
记得在相应的DNS服务商添加解析

复制证书到对应目录(有证书的请看这里)

1
2
cp your_cert.cert /root/derp/ssl/<DERP_SERVER_NAME_HOSTNAME>.crt
cp your_key.key /root/derp/ssl/<DERP_SERVER_NAME_HOSTNAME>.key

证书名称格式必须为<DERP_SERVER_NAME_HOSTNAME>.crt<DERP_SERVER_NAME_HOSTNAME>.key

创建Dockerfile文件

1
touch Dockerfile

Dockerfile文件输入以下内容

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
FROM golang:latest AS builder

WORKDIR /app

# https://tailscale.com/kb/1118/custom-derp-servers/
RUN go install tailscale.com/cmd/derper@main

FROM ubuntu
WORKDIR /app

ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
apt-get install -y --no-install-recommends apt-utils && \
apt-get install -y ca-certificates && \
mkdir /app/certs

ENV DERP_DOMAIN your-hostname.com
ENV DERP_CERT_MODE letsencrypt
ENV DERP_CERT_DIR /app/certs
ENV DERP_ADDR :443
ENV DERP_STUN true
ENV DERP_HTTP_PORT 80
ENV DERP_VERIFY_CLIENTS false

COPY --from=builder /go/bin/derper .

CMD /app/derper --hostname=$DERP_DOMAIN \
--certmode=$DERP_CERT_MODE \
--certdir=$DERP_CERT_DIR \
--a=$DERP_ADDR \
--stun=$DERP_STUN \
--http-port=$DERP_HTTP_PORT \
--verify-clients=$DERP_VERIFY_CLIENTS

编译镜像

1
docker build -t derper:latest .

启动服务,地址端口DERP_ADDR没有特别需求可以默认为:443,暴露端口也需要修改为443

1
2
3
4
5
6
7
8
9
10
docker run --restart always \
--name derper
-p 80:80 -p 12345:12345 -p 3478:3478/udp \ # 服务器对应端口需要放行
-v /root/derp/ssl:/app/certs \
-v /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock \ # 不需要客户端验证可以注释掉这行
-e DERP_CERT_MODE=manual \
-e DERP_ADDR=:12345 \
-e DERP_DOMAIN=<DERP_SERVER_NAME_HOSTNAME> \
-e DERP_VERIFY_CLIENTS=true \ # 不需要客户端验证可以注释掉这行
-d derper:latest

修改Headscale 控制服务器(C1)配置

创建derp配置文件

1
touch /root/headscale/config/derp.yaml

derp.yaml文件输入以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
regions:
900:
regionid: 900
regioncode: chn
regionname: My Derp Server
nodes:
- name: 900a
regionid: 900
hostname: <DERP_SERVER_NAME_HOSTNAME>
ipv4: xx.xx.xx.xx
stunport: 3478
stunonly: false
derpport: 12345

请自行修改配置,ipv4修改为derp服务器的IP
修改headscale配置文件/root/headscale/config/config.yaml,在derp.paths添加包含刚刚的derp.yaml配置文件(注意这里是容器内的目录)

1
2
3
derp:
paths:
- /etc/headscale/derp.yaml

重启headscale服务

1
docker compose restart headscale

安装tailscale客户端(A、B)

  • Windows
    • 键盘输入win + r,输入services.msc后回车,找到名称为IP Helper的服务,双击打开,启动类型选择为自动,然后点击启动,确定关闭
    • 在浏览器打开<HEADSCALE_SERVER_NAME>/windows,下载对应的tailscale客户端,安装
    • CMDPowerShell运行命令登录当前客户端tailscale login --login-server=<HEADSCALE_SERVER_NAME>,打开输出的网站,复制网站显示的命令,修改USERNAME为上面创建的用户名
    • 回到headscale服务器(C1),拼接命令后运行docker exec -it <HEADSCALE_CONTAINER_NAME> <上面复制的命令>,例如:docker exec -it root-headscale-1 headscale nodes register --user default --key mkey:xxxx
  • Macos
    • 在浏览器打开https://tailscale.com/download/mac,下载对应的tailscale客户端,安装
    • 在浏览器打开<HEADSCALE_SERVER_NAME>/apple,按照headscale: macOS configurationGUI节登录当前客户端,登录完后在弹出页面,复制网站显示的命令,修改USERNAME为上面创建的用户名
    • 回到headscale服务器(C1),拼接命令后运行docker exec -it <HEADSCALE_CONTAINER_NAME> <上面复制的命令>,例如:docker exec -it root-headscale-1 headscale nodes register --user default --key mkey:xxxx
  • iOS
    • 买不起苹果,请自行探索
  • Android
    • 在浏览器打开Connecting an Android client,按照说明下载客户端,登录完后在弹出页面,复制网站显示的命令,修改USERNAME为上面创建的用户名
    • 回到headscale服务器(C1),拼接命令后运行docker exec -it <HEADSCALE_CONTAINER_NAME> <上面复制的命令>,例如:docker exec -it root-headscale-1 headscale nodes register --user default --key mkey:xxxx
  • 群晖
    • 在浏览器打开Access Synology NAS from anywhere,按照说明下载对应的tailscale客户端,安装
    • ssh连接NAS,运行命令sudo tailscale up --login-server=<HEADSCALE_SERVER_NAME>,打开输出的网站,复制网站显示的命令,修改USERNAME为上面创建的用户名
    • 回到headscale服务器(C1),拼接命令后运行docker exec -it <HEADSCALE_CONTAINER_NAME> <上面复制的命令>,例如:docker exec -it root-headscale-1 headscale nodes register --user default --key mkey:xxxx

设置子网路由(A、B)

在中转机器上面安装tailscale客户端,这里以群晖为例
打开端口转发

1
2
3
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf

登录当前客户端

1
2
3
sudo tailscale up --advertise-routes=192.168.0.0/24 --login-server=<HEADSCALE_SERVER_NAME>
# 如果之前登录过了 执行下面这条
# sudo tailscale up --advertise-routes=192.168.0.0/24 --login-server=<HEADSCALE_SERVER_NAME> --force-reauth

回到headscale服务器(C1),查看节点路由并启用

1
2
3
4
# 获取路由ID
docker exec -it <HEADSCALE_CONTAINER_NAME> headscale routes list
# 启用
docker exec -it <HEADSCALE_CONTAINER_NAME> headscale routes enable -r <ID>

Android、iOS、macOS、tvOS和Windows会自动启用子网路由,如果是Linux则需要运行以下命令接受子网路由

1
sudo tailscale up --accept-routes

现在可以在其他设备访问内网IP

其他调试命令

1
2
3
4
5
6
7
headscale users list # 用户列表
headscale nodes list # 节点列表
headscale routes list # 路由列表
tailscale netcheck # 检测网络、derp服务器等
tailscale status # 查看节点状态
tailscale ping <节点IP> # 测试节点连通性
tailscale debug derp <regioncode> # 测试derp服务器

参考链接

Tailscale Doc
Headscale Doc
DERP
Tailscale 基础教程:Headscale 的部署方法和使用教程
Tailscale 基础教程:部署私有 DERP 中继服务器
headscale组网打通群晖局域网内部访问