错误信息
tls: failed to verify certificate: x509: certificate signed by unknown authority
Go crypto/tls (Go ≥ 1.20) — wrapping x509 verification errors
含义
从 Go 1.20 开始,TLS 握手失败时会给底层的 x509 错误加上“tls: failed to verify certificate:”前缀,因此日志中既能看到出错的层(TLS 握手),也能看到根本原因(证书链构建失败)。后缀各不相同——未知颁发机构、证书过期、主机名不匹配——真正需要处理的是后缀。这个前缀也很适合用来过滤日志:用 grep 搜索它,就能把 TLS 信任失败和应用层错误区分开,而这正是加上这层包装的原因。
原因
任何会导致“未知颁发机构”错误的原因
常见缺少中间证书、私有 CA、没有 CA 证书的容器——所有会产生“x509: certificate signed by unknown authority”的情况,只要发生在 TLS 拨号过程中,而不是显式的 Verify 调用中,现在都会带上这个前缀。
用默认客户端访问开发环境或自签名的端点
常见用 http.Get 访问使用自签名证书的本地服务器(另一个进程中的 httptest.NewTLSServer、minikube 的 Ingress、开发代理)时,如果没有把服务器的 CA 提供给客户端,就会报出这个错误。
后缀各不相同——要看清后缀
偶见“...: x509: certificate has expired”和“...: x509: certificate is valid for X, not Y”是带着同一前缀的不同问题;解决办法取决于后缀,而不在 TLS 层。
修复方法
- 1.
为客户端提供正确的信任锚
与不带前缀的 x509 错误解法相同:准备一个包含签发该证书的 CA 的根证书池,并把它设置到实际用于拨号的 Transport 上。
go pool := x509.NewCertPool() caPEM, _ := os.ReadFile("ca.pem") pool.AppendCertsFromPEM(caPEM) client := &http.Client{Transport: &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: pool}, }} resp, err := client.Get("https://internal.example:8443/healthz") - 2.
查看服务器实际出示的内容
在改动客户端代码之前,先查看服务器提供的证书链、其中各证书的颁发者和有效期——大多数问题最终都要通过服务端的证书链配置来解决。
bash openssl s_client -connect internal.example:8443 -servername internal.example \ </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates - 3.
保持验证开启,测试中也一样
httptest 服务器会直接给你一个预先配置好的客户端;使用它,而不是全局禁用验证。
go srv := httptest.NewTLSServer(handler) defer srv.Close() resp, err := srv.Client().Get(srv.URL) // trusts srv's cert, nothing else