近期在使用 Docker 登录自建 Gitea 镜像仓库时,遇到以下错误:
Error response from daemon: Get "https://*.com/v2/": remote error: tls: handshake failure常见原因排查(已排除)
根据搜索资料和 AI 建议,常见原因包括:
- 证书链不完整(最常见):服务器证书缺少中间 CA 证书
- 自签名证书:Docker 默认不信任私有仓库的自签名证书
- 证书过期:证书已过期
- 系统 CA 证书库缺失:系统缺少根证书
关键排除点:
我们的证书 非自签名、在有效期内,且浏览器访问无证书警告。
→ 排除证书本身问题,问题出在 Docker 客户端的 TLS 配置。
排查过程与关键发现
步骤 1:尝试标准自签名证书方案(失败)
按常规操作导出根证书并配置:
# 从仓库服务器导出 CA 证书
openssl s_client -connect registry-host.com:443 -showcerts </dev/null 2>/dev/null | \
awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/' > docker-ca.crt
# 复制到 Docker 证书目录
sudo cp docker-ca.crt /etc/docker/certs.d/registry-host.com/ca.crt
sudo systemctl restart docker问题依旧存在,说明不是证书信任问题。
步骤 2:深入 Docker 源码定位
通过单步调试定位到关键代码位置:
// daemon/pkg/registry/auth.go
func PingV2Registry(...){
resp, err := pingClient.Do(req) // 此处返回 tls: handshake failure
}发现 pingClient 的 Transport 参数导致问题:
pingClient := &http.Client{
Transport: authTransport, // 问题根源!
Timeout: 15 * time.Second,
}步骤 3:分析 authTransport 的 TLS 配置
追踪 authTransport 的生成逻辑,发现 Docker 默认的 TLS 配置:
var defaultCipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
}
// 默认 TLS 配置
func defaultConfig(ops ...func(*tls.Config)) *tls.Config {
return &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: defaultCipherSuites, // 仅支持 ECDHE 套件
}
}关键发现:
我们的自建仓库部署在 华为 Web 应用防火墙 (WAF) 后,WAF 的默认加密套件配置为 兼容性优先(包含 RSA 套件),而 Docker 默认 仅支持 ECDHE 套件,导致握手失败。
问题总结
| 问题点 | 原因 | 解决方案 |
|---|---|---|
| TLS 握手失败 | Docker 默认 CipherSuites 不兼容 WAF 配置 | 修改 WAF 配置 |
| 证书链/自签名方案无效 | 证书本身无问题 | 无需额外配置 CA 证书 |
经验教训:
当使用云厂商 WAF 或负载均衡器时,需确认其加密套件配置,避免与 Docker 默认配置冲突。Docker 的 TLS 配置需与后端服务的加密套件严格一致。
参考文档
提示:若使用其他 WAF(如 Nginx、AWS ALB),同样需检查其 TLS 配置与 Docker 的兼容性。
本文已通过实际环境验证,成功解决华为 WAF 后端 Gitea 镜像仓库的 Docker 登录问题。
关键点:不是证书问题,而是 TLS 加密套件不匹配!
相关推荐
- PHP Docker 镜像瘦身方案 2025-05-16
评论0
暂时没有评论