背景:
- 服务器:阿里云轻量
- 主体搭建方案:headscale-ui 的 github 页提供的 docker-compose
- 域名:有域名,未备案,已配好 dns 解析
- 反代方案:caddy
- ssl 证书方案:自编译支持阿里云的 caddy ,走 dns 挑战
操作表现&&症状:
- 搭好后,浏览器访问 http://<服务器 IP>:3000/web,可成功访问 headscale-ui 后台页面
- hs-ui 后面页面填入 https://<服务器域名>:61111 与 apikeys ,可成功通过页面与 headscale 进行交互(增删用户、增删 PreAuthKey 等操作)
- 可正常通过 https:///<服务器域名>:61111/windows 打开 win 客户端的登陆指引页面,浏览器 https 标识正常,查看证书信息正常,显示 Let's Encrypt 证书,三个月后到期
- 安卓客户端通过修改 server ,可正常跳转到浏览器,但是打不开页面,显示 ERR_CONNECTION_RESET ,但复制链接里的 mkey 到后台,可以成功添加设备,后续安卓端可在线
- win 端按提示 tailscale login --login-server https://<服务器域名>:61111 ,执行后,会弹出小气泡,点击气泡和点击任务栏图标登陆都没反应
- 清除相关文件、卸载重装 tailscale ,问题依旧
- 查看 tailscale 日志:
Received error: fetch control key: Get "https://<服务器域名>:61111/key?v=113": read tcp 192.168.11.86:55438->服务器 IP:61111: wsarecv: An existing connection was forcibly closed by the remote host.
2025-03-08T09:05:21Z ERR noise upgrade failed error="noise handshake failed: decrypting machine key: chacha20poly1305: message authentication failed"
2025/03/08 09:05:21 http: response.WriteHeader on hijacked connection from github.com/juanfont/headscale/hscontrol.(*Headscale).NoiseUpgradeHandler (noise.go:83)
2025/03/08 09:05:21 http: response.Write on hijacked connection from fmt.Fprintln (print.go:305)
配置文件
server_url: https://<我的域名>:61111
listen_addr: 0.0.0.0: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:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
allocation: sequential
derp:
server:
region_id: 996
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
ipv6: 2001:db8::1
urls:
[]
paths:
- /etc/headscale/my-derp.yaml
auto_update_enabled: true
update_frequency: 24h
disable_check_updates: true
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
wal_autocheckpoint: 1000
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: <服务器域名>
nameservers:
global:
- 1.1.1.1
- 1.0.0.1
- 2606:4700:4700::1111
- 2606:4700:4700::1001
split:
{}
search_domains: []
extra_records: []
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
logtail:
enabled: false
randomize_client_port: false
version: '3.5'
services:
headscale:
image: headscale/headscale:0.24.0
container_name: headscale
volumes:
- ./headscale-config/:/etc/headscale/
- ./headscale-data/:/var/lib/headscale/
ports:
- 8080:8080
command: serve
restart: unless-stopped
headscale-ui:
image: ghcr.io/gurucomputing/headscale-ui:latest
restart: unless-stopped
container_name: headscale-ui
ports:
- 3000:8080
https://<服务器域名>:61111 {
tls {
dns alidns {
access_key_id "XXXXXXXXXXXXXXX"
access_key_secret "XXXXXXXXXXXXXXX"
}
}
#匹配跨域请求
@hs-options {
host 服务器域名
method OPTIONS
}
@hs-other {
host 服务器域名
}
#处理跨域请求
handle @hs-options {
header {
Access-Control-Allow-Origin "http://<服务器 IP>:3000"
Access-Control-Allow-Headers *
Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE"
Access-Control-Allow-Credentials true
}
respond 204
}
handle @hs-other {
header Access-Control-Allow-Origin "http://<服务器 IP>:3000"
header Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE"
header Access-Control-Allow-Headers *
header Access-Control-Allow-Credentials true
reverse_proxy http://localhost:8080
}
}
|