问题背景
使用cloudflare的zero trust连接本机的ollama。通过newapi进行转发请求的时候报错。
x509: certificate signed by unknown authority
问题现象
场景描述:
- Go 语言编写的 API 转发程序
newapi - 部署在 Docker 容器中
- 需要请求 HTTPS 服务
https://example.com
对比测试:
宿主机测试正常:
# 在宿主机执行curl -v https://example.com/v1/models# 输出:SSL certificate verify ok# 成功返回数据容器内程序报错:
{ "message": "tls: failed to verify certificate: x509: certificate signed by unknown authority", "success": false}排查过程
第一步:检查容器环境
进入容器内部调查:
docker exec -it <容器名> /bin/sh查看操作系统:
cat /etc/os-release# 输出:Alpine Linux 3.22.1检查证书库:
ls -l /etc/ssl/certs/# 发现:证书目录存在且有内容初步结论: 证书库并非空白,问题不在于缺少证书库。
第二步:尝试更新证书
# 更新证书包apk updateapk upgrade ca-certificates
# 安装 curl 测试apk add curlcurl -v https://example.com/v1/models结果: 仍然报错 SSL certificate problem: unable to get local issuer certificate
结论: 最新的官方证书包也无法解决问题。
第三步:深入分析证书链
使用 OpenSSL 查看实际的证书链:
apk add opensslopenssl s_client -showcerts -connect example.com:443关键发现:
Certificate chain 0 s:CN=example.com i:C=US, O=CLOUDFLARE, INC. 1 s:C=US, O=CLOUDFLARE, INC. i:C=US, O=SSL Corporation 2 s:C=US, O=SSL Corporation i:C=GB, CN=AAA Certificate Services
Verify return code: 20 (unable to get local issuer certificate)分析结果:
- 证书链完整,由公共 CA 签发
- 没有企业内部证书或代理干扰
- 问题在于 Alpine 的证书库不包含根 CA
AAA Certificate Services
根本原因
问题的根源是:Alpine Linux 容器镜像内置的 ca-certificates 包不包含验证目标网站所需的特定根 CA 证书。
这就像你有一串钥匙,但恰好缺少开这扇门的那一把。
完美解决方案
既然容器的证书库”先天不足”,最简单的方法就是让容器使用宿主机的完整证书库。
实施步骤
1. 停止现有容器:
docker stop <容器名>docker rm <容器名>2. 重新启动容器并挂载证书目录:
docker run -d \ --name <新容器名> \ -p <宿主端口>:<容器端口> \ -v /etc/ssl/certs:/etc/ssl/certs:ro \ <镜像名称>关键参数解释
-v /etc/ssl/certs:/etc/ssl/certs:ro 这个参数的作用:
- 将宿主机的
/etc/ssl/certs目录挂载到容器的同名目录 :ro表示只读挂载,确保安全性- 容器内的所有程序都会使用宿主机的证书库
扩展应用
这个解决方案不仅适用于本文的场景,还可以解决:
- 企业内部 CA 证书信任问题
- 自签名证书的验证问题
- 各种语言和框架的 SSL 验证问题
总结
当遇到”主机正常,容器报错”的 SSL 证书问题时,记住这个万能解决方案:
-v /etc/ssl/certs:/etc/ssl/certs:ro让容器与宿主机共享同一份证书信任库,大多数 SSL 证书验证问题都会迎刃而解。
故障排查检查清单
遇到类似问题时,按以下顺序排查:
- 确认宿主机是否能正常访问
- 检查容器内证书库是否存在
- 使用 OpenSSL 分析证书链
- 确认是否有代理或企业 CA 干扰
- 应用证书目录挂载方案