安装服务端

创建headscale配置目录

1
2
$ mkdir -p /etc/headscale/{data,certs}
$ cd /etc/headscale

生成headscale配置文件

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
$ cat >config.yaml<<EOF
---
# 这个地址是告诉客户端需要访问的地址, 即使你需要在跑在
# 负载均衡器之后这个地址也必须写成负载均衡器的访问地址
server_url: http://35.241.79.221:8080

# Headscale 实际监听ip和端口
listen_addr: 0.0.0.0:8080

# 监控数据地址
metrics_listen_addr: 127.0.0.1:9090

# grpc 监听地址
grpc_listen_addr: 0.0.0.0:50443

# 是否允许不安全的 grpc 连接(非 TLS)
grpc_allow_insecure: false

private_key_path: /var/lib/headscale/private.key
noise:
private_key_path: /var/lib/headscale/noise_private.key

# 给vpn客户端分配的vpn网段
ip_prefixes:
- 192.168.66.0/24

# 中继服务器相关配置
derp:
server:
enabled: false
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
stun_listen_addr: "0.0.0.0:3478"
urls:
paths:
# 自定义的中继服务器配置文件,后面需要创建这个配置文件
- /etc/headscale/derp.yaml
auto_update_enabled: true
update_frequency: 24h
disable_check_updates: false
ephemeral_node_inactivity_timeout: 30m
node_update_check_interval: 10s

# SQLite config
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite

# acme自动生成ssl证书配置
acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: ""
# 这里不使用自动前面证书,我自己网上生成。
tls_letsencrypt_hostname: ""
tls_client_auth_mode: relaxed
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
acl_policy_path: ""
dns_config:
nameservers:
# 给客户端分配dns服务器
- 114.114.114.114
domains: []
magic_dns: true
base_domain: example.com
unix_socket: /var/run/headscale.sock
unix_socket_permission: "0770"
logtail:
enabled: false
randomize_client_port: false
EOF

创建中继服务器使用的证书目录及证书

1
2
3
4
5
6
# 在headscale的配置目录新建一个中继服务器用的证书目录
$ cd /etc/headscale

# 将生成的证书文件一定要以 你解析的域名.key 你解析的域名.crt 这种格式
$ vim certs/derper.xxx.cn.key
$ vim certs/derper.xxx.cn.crt

创建中继服务器配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ cd /etc/headscale
$ cat >derp.yaml<<EOF
regions:
900:
regionid: 900
regioncode: thk
regionname: aliyun SH
nodes:
- name: 900a
regionid: 900
hostname: derper.xxx.cn
ipv4: 35.241.xx.221
stunport: 3478
stunonly: false
derpport: 8082
EOF

配置说明:

  • regions 是 YAML 中的对象,下面的每一个对象表示一个可用区,每个可用区里面可设置多个 DERP 节点,即 nodes
  • 每个可用区的 regionid 不能重复。
  • 每个 nodename 不能重复。
  • regionname 一般用来描述可用区,regioncode 一般设置成可用区的缩写。
  • ipv4 字段不是必须的,如果你的域名可以通过公网解析到你的 DERP 服务器地址,这里可以不填。如果你使用了一个二级域名,而这个域名你并没有在公共 DNS server 中添加相关的解析记录,那么这里就需要指定 IP(前提是你的证书包含了这个二级域名,这个很好支持,搞个泛域名证书就行了)。
  • stunonly: false 表示除了使用 STUN 服务,还可以使用 DERP 服务。
  • 上面的配置中域名和 IP 部分我都打码了,你需要根据你的实际情况填写。

安装docker并生成docker-compose文件

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
$ curl https://www.putianhui.cn/package/script/install_docker.sh | bash

# 如果docker-compose版本过低,请在这下载最新版本的
$ wget https://github.com/docker/compose/releases
$ cat > docker-compose.yaml<<EOF
version: '3.9'

services:
headscale:
container_name: headscale
image: headscale/headscale:0.16.4
ports:
- '8080:8080'
cap_add:
- NET_ADMIN
- NET_RAW
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv6.conf.all.forwarding=1
restart: always
volumes:
# 这里挂载当前目录到容器中
- ./:/etc/headscale
- ./data:/var/lib/headscale
command: ['headscale', 'serve']
derper:
container_name: derper
image: fredliang/derper
ports:
- '8082:8082'
- '3478:3478/udp'
environment:
# 这里域名改成你的
- DERP_DOMAIN=derper.xxx.cn
- DERP_CERT_MODE=manual
- DERP_ADDR=:8082
- DERP_HTTP_PORT=-1
restart: always
volumes:
# 这里把本地证书目录挂载到容器中,注意证书的名称一定要遵循格式
- ./certs:/app/certs
volumes:
config:
data:
EOF

启动

1
docker-compose up -d

启动成功后注意防火墙放行如下端口

1
2
# tcp协议:8080、8082
# udp协议:3478

服务端常用命令

如果是docker运行的服务端就用 [docker exec headscale headscale ns list] 这样

namespcae相关

1
2
3
4
5
6
7
8
9
10
11
# 查看namespace列表
$ headscale ns list

# 创建namespace
$ headscale ns create test-net

# 删除namespace
$ headscale namespace destroy default

# 重命名namespace
$ headscale namespace rename default myspace

node相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看服务端注册进来的节点列表
$ headscale nodes list

# 列出所有的节点,同时显示出tag信息
$ headscale node ls -t

# 只查看namespace为default下的节点
$ headscale -n default node ls

# 根据id删除指定的节点,这里面的id是node list查询出来的id # 参考headscale nodes delete -i=6
$ headscale node delete -i<ID>

# 给id为2的node设置tag为tag:test
$ headscale node tag -i=2 -t=tag:test

# 查看对应节点的routes,-i 1 这里是指节点列表前的id
$ headscale nodes routes list -i 1

route相关

1
2
3
4
5
# 列出节点9的所有路由信息
$ headscale routes list -i=9

#将节点9的路由中信息为192.168.10.0/24的设置为true,这样除了虚拟内网ip,原先的内网ip网段为192.168.10的也能访问了,后面的/24表示子网掩码是24个1,就是255.255.255.0
$ headscale routes enable -i=9 -r=192.168.10.0/24

preauthkeys相关

preauthkeys主要是方便客户端快速接入,创建了preauthkeys后客户端直接使用该key就可以直接加入namespace

1
2
3
4
5
6

# 查看名称为default的namespace中已经生成的preauthkeys
$ headscale -n default preauthkeys list

# 给名称为default的namespace创建preauthkeys
$ headscale preauthkeys create -e 24h -n default

apikeys相关

apikeys是为了客户端和headscale做http鉴权用的,http请求的时候需要设置头部authorization值为固定的字符串”Bearer “加apikeys创建的key

1
2
3
4
5
# 创建apikeys,在创建的时候需要记录下完整的值,后续查询出来的都是prefix值类似于zs3NTt7G0w.pDWtOtaVx_mN9SzoM24Y02y6tfDzz5uysRHVxwJc1o4
$ headscale apikeys create

# 查询headscale的apikeys,并将结果输出成json格式
$ headscale apikeys list -o=json

安装服务端管理UI(可选)

注意:

我这里用test-headscale.putianhui-local.com域名作为ui的访问域名。

headscale服务端(默认使用8080端口)和headscal-ui需要部署在同一台主机上(使用nginx通过前面那个域名把前后端代理出去,解决跨域问题)

1、先将test-headscale.putianhui-local.com域名解析到headscale服务端ip上。

2、申请test-headscale.putianhui-local.com域名的ssl证书,可以在阿里云申请免费的,也可以看我这个文档去申请免费通配证书。

3、服务端安装nginx

1
2
3
4
5
6
7
8
9
10
# 安装nginx
$ yum install -y nginx unzip && systemctl enable nginx && systemctl start nginx

# 创建nginx使用证书目录。
$ mkdir -p /etc/nginx/cert

# 将https证书的公私钥更名为你对应域名的证书名称,并移动到/etc/nginx/cert目录下。
$ ll /etc/nginx/cert
test-headscale_putianhui-local_com.key
test-headscale_putianhui-local_com.pem

4、下载headscale-ui源码到本地。

1
2
3
4
5
6
7
8
# 这里需要注意的是 如果你的headscale版本19+ 那么就用2023-01-30+的ui版本,低于19+就用<2023-01-30的ui版本
$ wget https://github.com/gurucomputing/headscale-ui/releases/download/2022.12.23.2-beta/headscale-ui.zip

# 解压并重命名
$ unzip headscale-ui.zip && headscale-ui

# 创建data目录并移动headscale-ui到/data目录下
$ mkdir -p /data/ && mv headscale-ui /data

5、创建headscale-ui的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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
$ vim /etc/nginx/conf.d/headscale-ui.conf
map $http_upgrade $connection_upgrade {
default keep-alive;
'websocket' upgrade;
'' close;
}

server {
# 这里为你的域名
server_name test-headscale.putianhui-local.com;

location /web {
# 这里为你的headscale-ui的nginx目录
alias /data/headscale-ui;
index index.html;
}

location / {
# 如果你前面部署headscale时更换了端口号,那这里的端口号也需要变。
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $server_name;
proxy_redirect http:// https://;
proxy_buffering off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
}

listen 443 ssl;
# 这里为你前面重命名的公私钥
ssl_certificate cert/test-headscale_putianhui-local_com.pem;
ssl_certificate_key cert/test-headscale_putianhui-local_com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
}

server {
# 改为你的域名
if ($host = test-headscale.putianhui-local.com) {
return 301 https://$host$request_uri;
}

# 你的域名
server_name test-headscale.putianhui-local.com;
listen 80;
return 404;
}

测试nginx配置文件正确性并重载配置文件。

1
2
3
4
5
$ nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

$ nginx -s reload

配置重载成功后就可以使用前面的域名https://test-headscale.putianhui-local.com/web访问到headscale-ui了,如果无法访问请检查你服务器的80、443端口是否放开。

配置headscale-ui

在headscale服务端创建一个apiKey,后面的web页面访问需要用到

1
2
$ docker exec headscale headscale apikeys create
zX79yATj2w.DmfLc0oqBRjnQNiVhcVp_lwEJUIoPkLeQ2HUFhuxxxx

安装客户端

Linux 用户目前只需要使用以下命令安装即可:

1
curl -fsSL https://tailscale.com/install.sh | sh

节点加入到服务端

1
tailscale up --login-server http://35.241.79.221:8080 --advertise-routes=192.168.0.0/24 --accept-routes=true --accept-dns=false

关于选项设置:

  • --login-server: 指定使用的中央服务器地址(必填)
  • --advertise-routes: 向中央服务器报告当前客户端处于哪个内网网段下, 便于中央服务器让同内网设备直接内网直连(可选的)或者将其他设备指定流量路由到当前内网(可选)
  • --accept-routes: 是否接受中央服务器下发的用于路由到其他客户端内网的路由规则(可选)
  • --accept-dns: 是否使用中央服务器下发的 DNS 相关配置(可选, 推荐关闭)
  1. 执行加入命令完成后, tailscale 将会卡住, 并打印一个你的服务器访问地址; 浏览器访问该地址后将会得到一条命令

  2. 复制到浏览器访问显示的链接,然后浏览器会显示一条服务端执行的命令。

  3. 复制浏览器显示的命令,去服务端执行一下。

    1
    2
    3
    4
    5
    6
    # NAMESPACE换成你自己的namespace名称,如果没有用下面的命令创建一个。
    # 创建namespace
    $ headscale ns create test-net

    # 执行浏览器显示的命令,注意替换NAMESPACE为你自己的。
    $ docker exec headscale headscale -n NAMESPACE nodes register --key 584b3fc394e9c8a310ac1c23122dfc03a16422674f38dfe2cac780cdb6cd1503
  4. 当服务端执行命令后,几秒钟客户端就自动退出一直卡住的界面了。

  5. 此时客户端就可以ping通对端集群内的客户端vpn-ip了。

上面步骤完成后只能客户端之间ping通客户端的vpn-ip,如果想ping对端客户端所在的内网其他主机,需要用下面命令开启路由功能.

1
2
3
4
5
# 以下命令在服务端执行
# 查看服务端注册进来的节点列表
$ headscale nodes list
# 查看对应节点的routes,-i 1 这里是指节点列表前的id,把所有节点都开启路由
$ headscale nodes routes list -i 1

此时就可以在一个客户端去ping对端内网的其他主机了。

客户端常用命令

1
2
3
4
5
6
7
8
9
10
11
# 查看当前节点vpn网段ip
$ tailscale ip

# 查看当前节点到其他对等网的信息,显示字段依次是 主机名,namespace,操作系统,客户端状态,direct|relay
$ tailscale status

# 网络到其他地域的延迟检查
$ tailscale netcheck

# ping其他对端的vpn-ip,如果是直连ping一下就停止,如果走了中继服务器就一直ping
$ tailscale ping 192.168.66.1

自建私有中继服务器

中继服务器部署

1
2
3
4
5
6
7
8
9
10
11
12
13
# 在headscale的配置目录新建一个中继服务器用的证书目录
$ pwd
/etc/headscale

# 创建中继服务器使用的证书目录
$ mkdir certs

# 将生成的证书文件一定要以 你解析的域名.key 你解析的域名.crt 这种格式
$ vim certs/derper.xxx.cn.key
$ vim certs/derper.xxx.cn.crt

# 启动服务
docker run -d --name derper -v /etc/headscale/certs:/app/certs -e DERP_DOMAIN=derper.xxx.cn -e DERP_CERT_MODE=manual -e DERP_ADDR=:8082 -e DERP_HTTP_PORT=-1 -p 8082:8082 -p 3478:3478/udp fredliang/derper

启动参数介绍

  • -d:容器后台运行
  • --name:指定容器名称
  • -v:将宿主机/etc/headscale/certs这个ssl证书目录挂载到容器中的/app/certs证书目录。
  • -e DERP_DOMAIN:指定你解析过来的二级域名
  • -e DERP_CERT_MODE:ssl证书的模式使用自定义
  • -e DERP_ADDR:自定义端口

配置headscale使用自定义中继服务器

1、在headscale的配置目录下创建一个中继服务器列表文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ cat >derp.yaml<<EOF
regions:
900:
regionid: 900
regioncode: thk
regionname: aliyun SH
nodes:
- name: 900a
regionid: 900
hostname: derper.xxx.cn
ipv4: 35.241.xx.221
stunport: 3478
stunonly: false
derpport: 8082
EOF

配置说明:

  • regions 是 YAML 中的对象,下面的每一个对象表示一个可用区,每个可用区里面可设置多个 DERP 节点,即 nodes
  • 每个可用区的 regionid 不能重复。
  • 每个 nodename 不能重复。
  • regionname 一般用来描述可用区,regioncode 一般设置成可用区的缩写。
  • ipv4 字段不是必须的,如果你的域名可以通过公网解析到你的 DERP 服务器地址,这里可以不填。如果你使用了一个二级域名,而这个域名你并没有在公共 DNS server 中添加相关的解析记录,那么这里就需要指定 IP(前提是你的证书包含了这个二级域名,这个很好支持,搞个泛域名证书就行了)。
  • stunonly: false 表示除了使用 STUN 服务,还可以使用 DERP 服务。
  • 上面的配置中域名和 IP 部分我都打码了,你需要根据你的实际情况填写。

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

1
2
3
4
5
6
7
8
9
10
11
12

# /etc/headscale/config.yaml
derp:
# List of externally available DERP maps encoded in JSON
# 把默认使用官方的中继服务器配置禁用掉,方便测试。
urls:
# - https://controlplane.tailscale.com/derpmap/default

#
# 这里的中继服务器配置列表用我们新创建的。
paths:
- /etc/headscale/derp.yaml

修改完配置后,重启 headscale 服务

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

1
2
3
4
5
6
7
8
9
10
11
$ tailscale netcheck
Report:
* UDP: true
* IPv4: yes, 120.26.48.71:39855
* IPv6: no, but OS has support
* MappingVariesByDestIP:
* HairPinning: false
* PortMapping:
* Nearest DERP: aliyun SH # 这里显示我们使用的中继服务器
* DERP latency:
- thk: 33.9ms (aliyun SH)