错误信息
x509: certificate is valid for example.com, not api.example.com
这两处名称随每次失败而填入:先是证书覆盖的名称,然后是你的客户端请求的名称。
Go crypto/x509 — HostnameError from VerifyHostname / TLS dials
含义
TLS 连接已经进行到能够读取证书的阶段,但证书中没有任何一个主题备用名称(SAN)与客户端连接的主机名匹配。错误消息列出了双方,因此差异一目了然:证书覆盖的是 X,而你请求的是 Y。注意,现代的验证只使用 SAN——旧式的通用名称字段会被忽略。
原因
SAN 列表中缺少正在使用的名称
常见为 example.com 签发的证书不覆盖 api.example.com(通配符 *.example.com 也只覆盖一级——既不覆盖 example.com 本身,也不覆盖 a.b.example.com)。新的子域名需要重新签发证书。
连接内部别名
常见连接 docker-compose 服务名或其他内部别名时,服务器出示的是它自认为的那个身份的证书——SAN 中是公共 DNS 名称,而不是你的别名,因此匹配失败。直接连接裸 IP 地址则会报出另一条相近的消息:“x509: cannot validate certificate for 10.0.0.5 because it doesn't contain any IP SANs”。
提供了错误的证书或默认证书(SNI)
偶见多租户 Ingress 根据 SNI 选择证书。客户端如果省略 ServerName,或者 Ingress 没有为该主机配置规则,得到的就是默认证书——通常是“Kubernetes Ingress Controller Fake Certificate”之类的占位证书。
修复方法
- 1.
重新签发证书,包含实际使用的所有名称
签发时把所有主机名都添加为 SAN;查看当前的 SAN 列表,了解目前覆盖了哪些名称。
bash echo | openssl s_client -connect host:443 -servername api.example.com 2>/dev/null \ | openssl x509 -noout -ext subjectAltName # request with SANs: openssl req -new -key key.pem -out req.csr \ -subj "/CN=example.com" -addext "subjectAltName=DNS:example.com,DNS:api.example.com" - 2.
连接该名称,或显式设置 ServerName
如果网络地址必须与证书中的名称不同(VIP、端口转发、sidecar),就明确告诉 TLS 你期望的是哪个身份,让验证依然严格有效。
go conn, err := tls.Dial("tcp", "10.0.0.5:443", &tls.Config{ ServerName: "api.example.com", // matched against SANs, sent as SNI })