系统学习 故障排查学习路径:从方法论到系统、网络、数据库与 K8s

x509-unknown-authority

Go 程序报 x509: certificate signed by unknown authority 时的定位与修复

现象

Go 编写的客户端在 TLS 握手时报 x509: certificate signed by unknown authority,例如 kubectl 访问私有集群、Operator 调用内部 API、Prometheus 抓取 HTTPS 目标、容器运行时拉取私有仓库镜像。这个字符串来自 Go 的 crypto/x509,含义是服务端出示的证书链无法回溯到客户端信任的根证书,并不等于证书一定过期或域名一定不匹配。

同样的服务用浏览器或 curl 可能正常:浏览器有时会通过证书里的 AIA 地址补取中间证书,而 Go 的证书校验不会主动补链;容器里的信任库也可能和宿主机完全不同。因此要先分清是“服务端链不完整”还是“客户端不信任”。错误还可能被上层包装成 failed to do request、tls: failed to verify certificate 或镜像拉取失败,定位时要顺着日志找到最内层的 x509 信息。

快速定位

先用 OpenSSL 看服务端实际发送的证书链:

openssl s_client -connect <host>:443 -servername <host> -showcerts </dev/null
openssl verify -CAfile <ca.pem> <server.pem>
openssl crl2pkcs7 -nocrl -certfile <bundle.pem> | openssl pkcs7 -print_certs -noout
curl -v --cacert <ca.pem> https://<host>/

crl2pkcs7 那条命令用来列出客户端 bundle 里究竟包含哪些根证书,确认私有 CA 是否真的被装进去,而不只是文件存在。

确认 Go 客户端使用的信任来源:

env | grep -E 'SSL_CERT_FILE|SSL_CERT_DIR'
ls -l /etc/ssl/certs/ca-certificates.crt
ls -l /etc/pki/tls/certs/ca-bundle.crt
ls -l /etc/ssl/cert.pem

容器内要单独检查,不能只看宿主机:

kubectl exec -it <pod> -- ls -l /etc/ssl/certs /etc/ssl/cert.pem
kubectl logs <pod> --previous

Kubernetes 场景检查 kubeconfig 和内嵌 CA:

kubectl config view --raw
kubectl -n <ns> get secret <tls-secret> \
  -o jsonpath='{.data.ca\.crt}' | base64 -d | openssl x509 -noout -subject -issuer -dates

如果程序运行在 Kubernetes 中,还要确认三处信任来源:kubeconfig 的集群 CA、Pod 内挂载的 ServiceAccount CA,以及 Sidecar/Service Mesh 是否接管了流量。Sidecar 有自己的信任域,报错可能来自代理而不是业务进程,查看代理日志才能定位。

判断顺序:s_client 能拿到完整链且 openssl verify 通过,问题在 Go 客户端的信任库、环境变量或代理;s_client 连接或握手失败时,先区分连接问题与服务端证书链问题。再用默认信任库和显式 CA 各跑一次 curl,如果默认信任库成功、自定义 bundle 失败,说明 bundle 漏了根证书;反之则是系统信任库没有更新。

常见根因

  1. 服务端只发送叶子证书,缺少中间证书。桌面浏览器可能补链成功,Go 直接失败。
  2. 私有 CA 或自签 CA 没有加入客户端信任库,或只加到了宿主机、没有加到容器。
  3. 精简镜像没有安装 CA 包,scratch、distroless、部分 Alpine 镜像常见。
  4. SSL_CERT_FILE、SSL_CERT_DIR 指向旧文件或不存在的路径,覆盖了系统默认信任库。
  5. 企业代理或 WAF 做了 TLS 中间人替换,客户端不信任代理根 CA。
  6. 通过 IP 访问、SNI 不匹配,服务端返回了默认站点的自签证书。
  7. CA 轮换不完整:新根未下发,旧根已删除,或交叉签名链发生变化。
  8. Kubernetes 的 kubeconfig 缺少 certificate-authority-data,Pod 内没有挂载 ca.crt。
  9. 私有镜像仓库的 hosts.toml 没有配置 ca。
  10. 中间证书过期或链顺序错误,客户端在构建信任路径时失败。

修复

相关命令

参考链接

读完自测

合上页面,先回答,再展开对照。

  1. 浏览器能打开,Go 程序却报 unknown authority,常见原因有哪些?
  2. 怎样区分是服务端证书链不完整,还是客户端信任库有问题?
  3. 容器里出现这个问题,通常有哪两种修复方式?
答案
  1. Go 不会通过 AIA 自动补取中间证书,且可能使用与浏览器不同的信任库;容器镜像缺少 CA 包、SSL_CERT_FILE 指向错误文件、企业代理替换证书也都会导致该错误。
  2. 用 openssl s_client -showcerts 看服务端是否发送叶子加中间证书,再用 openssl verify -CAfile 验证;链完整但验证失败,问题在客户端信任库或环境变量,链缺失则先修服务端。
  3. 在镜像构建阶段安装 ca-certificates,或把 CA bundle 以只读卷挂载并用 SSL_CERT_FILE 显式指定;改完后重建或滚动重启工作负载。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令