使用 headscale 异地组网

很久之前看过柠檬大佬的使用 N2N 进行异地组网的文章，大受震撼，但是 N2N 的部署体验很难说得上令人愉悦。

然后就听说了 wireguard 被加入 linux 内核，以下是 wireguard 的介绍：

WireGuard是由 Jason A. Donenfeld 开发的开放源代码 VPN程序及协议[1]，基于Linux 内核实现，利用Curve25519进行密钥交换，ChaCha20用于加密，Poly1305用于数据认证，BLAKE2用于散列函数运算[1]，支持IPv4和IPv6的第 3 层。[2]WireGuard 旨在获得比IPsec和OpenVPN更好的性能[3]。

确实，wireguard 性能十分不错，但是配置实在是过于繁琐了，如果要往 wireguard 网络中加入一台设备，则需要修改几乎所有连入网络设备的配置文件，实在是太麻烦了，好在现在已经有了 tailscale 这个服务提供商提供了基于 wireguard 的 0 配置的 VPN 组网方案。

而 headscale 就是 tailscale 中控服务端的开源版本，开源版本支持自己部署，同时没有连入设备的数量限制，于是我花了一点时间将 headscale 部署了一下。

使用到的项目

Github/headscale
Github/headscale-ui

部署 headscale

这里我使用 docker-componse 进行部署

Copy
version: '3.5'
services:
headscale:
image: headscale/headscale:latest-alpine
container_name: headscale
volumes:
- ./container-config:/etc/headscale
- ./container-data/data:/var/lib/headscale
ports:
- 27896:8080
command: headscale serve
restart: unless-stopped
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
restart: unless-stopped
container_name: headscale-ui
ports:
- 9443:443

同时我还使用了 nginx 来进行反向代理，将 headscale-ui 和 headscale 分别部署在了不同的域名下面，因此要做一些 CORS 的处理，Nginx 配置文件参考如下

Copy
location / {
add_header 'Access-Control-Allow-Origin' '{{UI的域名}}' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS,DELETE ,PUT' always;
add_header 'Access-Control-Allow-Headers' 'authorization ,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '{{UI的域名}}' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS,DELETE ,PUT ' always;
add_header 'Access-Control-Allow-Headers' 'authorization ,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
proxy_pass {{headscale的地址}};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_redirect default;
}

配置 headscale-ui

headscale-ui 这个项目是一个纯前端的项目，是通过用户的浏览器直接调用 headscale 的 API 接口，所以需要自己去 headscale container 内部创建 apikey 来通过鉴权

Copy
docker exec -it headscale headscale apikey create

这个指令会创建一个 apikey，将他填入 headscale-ui 的配置里面即可，这里的配置仅保留在本地，不会上传到任何地方，所以如果更换了设备或者浏览器想要进行 headscale 的配置的话，要将这一步重复一遍。

配置 ACL

headscale 同时还支持 ACL 功能，从而控制在这个大内网之中设备能访问的设备，这边我写了个比较简单的 ACL 配置文件

Copy
// ./container-config/acl.json
{
"groups": {
"group:admin": ["admin"], // 管理员用户
"group:user": ["user"], // 普通用户
},
"acls": [
// { "action": "accept", "src": ["*"], "dst": ["*:*"] },
{ "action": "accept", "src": ["group:admin"], "dst": ["*:*"] }, // 管理员用户的设备能访问所有设备
{ "action": "accept", "src": ["group:user"], "dst": ["tag:share:*","autogroup:self:*"] }, // 普通用户仅能访问带 share 标签以及自己的设备
],
"ssh": [
{
"action": "check",
"src": [
"autogroup:members"
],
"dst": [
"autogroup:self"
],
"users": [
"autogroup:nonroot",
"root"
]
},
],
"tagOwners": {
"tag:share": ["group:admin"],
},
}

同时，要修改配置文件里面的 acl_policy_path

Copy
acl_policy_path: "/etc/headscale/acl.json"

这样基本上就大功告成了，客户端的配置网上基本上都有，就不多写了。