求助, HeadScale 自建后, win 端 Tailscale 无法登陆 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liuzimin
V2EX    服务器

求助, HeadScale 自建后, win 端 Tailscale 无法登陆

  •  
  •   liuzimin 218 天前 2335 次点击
    这是一个建于 218 天前的主题,其中的信息可能已经有所发展或是发生改变。

    背景:

    • 服务器:阿里云轻量
    • 主体搭建方案: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 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 
    • 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 } } 
    21 条回复    2025-06-25 15:23:57 +08:00
    lexfoxrush
        1
    lexfoxrush  
       218 天前
    tailscale login --login-server https://<服务器域名>:61111 --auth-key 带上 auth-key 能行吗
    lexfoxrush
        2
    lexfoxrush  
       218 天前
    t/1055354 我看这里有说未备案会被阻断 我用的 ip 和自签名证书,客户端添加下信任
    liuzimin
        3
    liuzimin  
    OP
       218 天前
    @lexfoxrush 试了下,还是一样的症状。
    liuzimin
        4
    liuzimin  
    OP
       218 天前
    @lexfoxrush 不过为啥用域名能打开 https://域名/windows 的指引页呢?而且安卓、linux 端用这个域名方式可以成功添加。
    之前我也考虑过纯 IP+自签名证书方案,后来不知道哪里没搞对,把证书装上还是没走通 https ,就放弃了。
    liuzimin
        5
    liuzimin  
    OP
       218 天前
    @lexfoxrush 我用纯 IP 的方式试了下,windows 端可以正常显示一个加入链接了。。。看来确实问题出在 https 上,可能真的是域名备案问题吧。。唉,太恶心了。
    liuzimin
        6
    liuzimin  
    OP
       217 天前
    @lexfoxrush 老哥救命。。。。我现在也在尝试纯 IP 加自签名。
    现在的情况是:
    - 自签名的证书导入电脑,局域网内电脑架个纯 IP 的 Web 页面,可以成功走 https ,感觉自签名方式没问题
    - headscale-ui 走 http 就能正常访问页面
    - 给服务器自签名( Caddy 反代里面配置),用 https 访问 headscale-ui ,Chrome 始终报错:ERR_SSL_PROTOCOL_ERROR

    (headscale 在 8080 ,headscale-ui 在 3000)

    我感觉这是 Caddy 有问题?
    Caddyfile:
    ```
    {
    auto_https disable_redirects
    }

    https://<服务器 IP>:8081 {
    tls /home/admin/docker/cert/crt.crt /home/admin/docker/cert/private.key #全局签名

    @cors header Origin http://<服务器 IP>:3000
    @options method OPTIONS

    handle @options {
    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"
    respond 204
    }

    handle @cors {
    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 127.0.0.1:8080
    }


    https://<服务器 IP>:3001 {
    tls /home/admin/docker/cert/crt.crt /home/admin/docker/cert/private.key

    reverse_proxy 127.0.0.1:3000
    }

    ```
    lexfoxrush
        7
    lexfoxrush  
       217 天前
    openssl s_client -connect ip:8080
    openssl s_client -connect ip:3000
    客户端命令行试试这俩正常吗
    liuzimin
        8
    liuzimin  
    OP
       216 天前
    @lexfoxrush openssl s_client -connect <服务器 ip>:3001 -showcerts
    CONNECTED(00000003)
    801B3E9AD17F0000:error:0A000438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:../ssl/record/rec_layer_s3.c:1584:SSL alert number 80
    ---
    no peer certificate available
    ---
    No client certificate CA names sent
    ---
    SSL handshake has read 7 bytes and written 293 bytes
    Verification: OK
    ---
    New, (NONE), Cipher is (NONE)
    Secure Renegotiation IS NOT supported
    Compression: NONE
    Expansion: NONE
    No ALPN negotiated
    Early data was not sent
    Verify return code: 0 (ok)
    liuzimin
        9
    liuzimin  
    OP
       216 天前
    @lexfoxrush 老哥你确定国内的云用 IP+自签名可以成功走 https 吗?
    我今天又试了下,在本地电脑的 WSL 上搭了同一套,用 IP+自签名一点毛病都没有,正常启用 https 访问;但阿里云上就死活调不通
    lexfoxrush
        10
    lexfoxrush  
       216 天前
    我用的就是阿里云的,不过没有 headscale-ui
    lexfoxrush
        11
    lexfoxrush  
       216 天前
    自己装的 nixos ,没用自带的,不能是这个的影响吧
    liuzimin
        12
    liuzimin  
    OP
       216 天前
    @lexfoxrush 我用的阿里云的 Ubuntu22.04 ,和我 WSL 的版本一致。
    liuzimin
        13
    liuzimin  
    OP
       216 天前
    @lexfoxrush 卧槽,老哥我解决了!!我放弃了 Caddy ,让 deepseek 帮我写了一个 nginx 的反代,直接一步成功,现在自签名证书走 https 稳稳的,太爽了。。还是老玩意儿比新东西成熟靠谱啊。

    -----

    在这做下笔记,给后人指个路:
    - 自签名证书生成 (假设服务器 IP 以 192.168.1.1 为例):
    ```
    openssl req -x509 -newkey rsa:4096 -sha256 -keyout private.key -out cert.crt -days 3650 -subj "/CN=192.168.1.1" -addext "subjectAltName=IP:192.168.1.1" -nodes
    ```

    - nginx 反代配置:
    ```
    # Headscale-UI 的反向代理( 3001 -> 3000 )
    server {
    listen 3001 ssl;
    server_name 192.168.1.1; # 替换为你的服务器 IP 或域名

    # 自签名证书路径
    ssl_certificate /home/admin/docker/cert/192.168.1.1.crt;
    ssl_certificate_key /home/admin/docker/cert/192.168.1.1.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
    proxy_pass http://localhost:3000; # 将请求转发到 Headscale-UI
    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 X-Forwarded-Proto $scheme;
    }
    }

    # Headscale 的反向代理( 8081 -> 8080 )
    server {
    listen 8081 ssl;
    server_name 192.168.1.1; # 替换为你的服务器 IP 或域名

    # 自签名证书路径
    ssl_certificate /home/admin/docker/cert/192.168.1.1.crt;
    ssl_certificate_key /home/admin/docker/cert/192.168.1.1.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
    proxy_pass http://localhost:8080; # 将请求转发到 Headscale
    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 X-Forwarded-Proto $scheme;
    }
    }
    ```
    Nginx 启动!!

    -END-
    ray1980
        14
    ray1980 &nsp;
       215 天前
    不用设置 derp 吗?
    liuzimin
        15
    liuzimin  
    OP
       215 天前
    @ray1980 derp 我以前自建过。所以这里只讨论了 headscale 相关。
    liuzimin
        16
    liuzimin  
    OP
       215 天前
    头大,现在 https 网页访问没问题了,但 tailscale 客户端用 https 的服务器地址就死活不跳转。。。
    nunterr
        17
    nunterr  
       110 天前
    我开始也自建过,各种问题,现在是使用 tailscale+derp,这种使用很方便
    liuzimin
        18
    liuzimin  
    OP
       110 天前 via Android
    @nunterr #17 tailscale+derp 确实省心。但就国内这恶劣的网络环境,我很担忧未来某一天 tailscale 会被和谐,所以我才折腾 headscale 实现全流程可控。

    最后我还是老老实实备案了域名,然后一切都跑通了,直到目前一直很稳定。
    nunterr
        19
    nunterr  
       109 天前
    @liuzimin 我使用 HeadScale 很难打通直连,tailscale 大部分都能打洞成功,不成功的走国内 derp
    liuzimin
        20
    liuzimin  
    OP
       109 天前
    @nunterr #19 哈哈我从来就没打洞成功过。。。不管是用哪种方案,都是走自建 derp 中转。
    nunterr
        21
    nunterr  
       109 天前
    @liuzimin 找宽带服务商开启外网访问服务。应该就可以了,我当初就找了移动宽带开通后就全部打通了,还有 tailscale 打洞成功概率确实比 head 好很多
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5508 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 01:28 PVG 09:28 LAX 18:28 JFK 21:28
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86