记一次被社区用户逼着修Bug经历
孔飞@快猫星云
2025-12-25 09:46:28
最近社区用户反馈了一个问题:使用最新版 Categraf (v0.4.34) 的 http_response 插件监控某域名 https://oa.sdu.edu.cn 时,报错 remote error: tls: handshake failure。但开发人员在自己的环境中测试却一切正常。吃瓜围观见issue, 用户对这个问题定性:“希望在新版本中修复这个bug”。
本文记录了该问题的完整排查过程、根因分析及最终修复方案。
1. 排查过程
欸?在我的环境明明好好的

复现的诡异性
- 开发环境 (Debian, 公网):没有报错,能够正常通过
curl和 Categraf 采集数据。 - 用户环境 (Anolis OS, 校园网环境):
curl正常,但 Categraf 持续报错tls: handshake failure。
初步怀疑方向集中在:
- TLS 版本不匹配?
- 中间人设备(防火墙、WAF)干扰?
- DNS 解析差异?
关键线索:Cipher Suite 与 IP 协议
通过对比开发人员与用户在各自环境下执行 curl -vI https://oa.sdu.edu.cn 的详细日志,我们发现了具体的差异。
开发人员环境(正常)
* Connected to oa.sdu.edu.cn (202.194.20.69) port 443 <-- IPv4
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
特点:访问的是 IPv4 地址,服务器支持 TLS 1.3 和现代化的 GCM 加密套件。
用户环境(报错)
* Connected to oa.sdu.edu.cn (2001:da8:7000:f03:202:194:20:69) port 443 <-- IPv6
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
...
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-SHA256
特点:
- 访问的是 IPv6 地址。
- 服务端不支持 TLS 1.3,协商降级到了 TLS 1.2。
- 最终选用的加密套件是
ECDHE-RSA-AES128-SHA256(即 CBC 模式)。
2. 根因分析:Go的安全策略变更
至此,问题原因浮出水面。
- IPv6 入口设备"老旧":该域名的 IPv6 入口(可能是负载均衡或防火墙)配置落后于 IPv4 入口,仅支持 TLS 1.2 且只接受 CBC 模式的加密套件。
- Categraf/Go 的默认行为:Categraf 是用较新的 Go 版本(1.24)编译的,而且升级了TLS的依赖库。Go 语言团队为了安全性,在默认的 Client Hello 加密套件列表中剔除了被认为不够安全的 CBC 模式套件(如
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)。 - 握手死锁:
- Categraf: “我支持 TLS 1.2/1.3,但我只用 GCM/Chacha20 等高性能安全算法,不提供 CBC。”
- Server (IPv6): “我只支持 TLS 1.2 且必须用 CBC。”
- 结果: 双方无法达成一致,服务端发送
handshake failure。
- Why Curl Works?:
curl使用的 OpenSSL 版本不高, 默认策略包含了旧的 CBC 套件,因此能与“老”服务器成功握手。
3. 修复方案
我们通过代码修改,赋予 Categraf “向下兼容” 的能力,允许用户手动指定那些被 Go 默认屏蔽的加密套件。
代码变更 (Pull Request)
-
修改
pkg/tls/config.go: 在ClientConfig中增加TLSCipherSuites字段,允许从配置文件读取tls_cipher_suites。 -
修改
pkg/tls/common.go: 补全 Go 标准库中存在但未在 Categraf 映射表中暴露的 Legacy Cipher Suites:// 添加 Legacy CBC 套件 "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // ...
用户配置
用户现在可以通过在 http_response.toml 中显式配置旧套件来解决此问题:
[[instances]]
targets = ["https://oa.sdu.edu.cn"]
# 强制开启兼容旧服务器所需的 CBC 套件
tls_cipher_suites = [
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", # 对应 curl 的 ECDHE-RSA-AES128-SHA256
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" # SHA1 版本作为备选
]
总结
在 IPv6 推进过程中,基础设施的更新往往不同步,导致 IPv4 和 IPv6 入口的 SSL 配置产生“代差”。作为监控客户端,保持对旧协议的适度兼容性配置能力,是应对复杂网络环境的关键。