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