背景:
- 服务器:阿里云轻量
- 主体搭建方案: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. - 查看 headscale 日志:
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) 配置文件
- headscale-config.yaml
server_url: https://<我的域名>:61111 liste_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 - compose.yaml 文件
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 - Caddyfile 配置文件
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 } } 