必须设ClientAuth为tls.RequireAndVerifyClientCert,服务端才强制校验客户端证书;ClientCAs需加载可信CA根证书池;客户端须同时配置Certificates(自身证书链)和RootCAs(验证服务端)。

双向TLS在Go里怎么启用,不是单向
Go的http.Server和http.Client默认只做服务端证书验证(单向TLS),要强制客户端也提供证书并被服务端校验,必须显式配置TLSConfig的ClientAuth字段。不设这个,哪怕客户端发了证书,服务端也直接忽略。
关键点有三个:
• 服务端必须设置ClientAuth: tls.RequireAndVerifyClientCert(或tls.VerifyClientCertIfGiven按需)
• 服务端ClientCAs要加载CA证书池,用于验证客户端证书签名
• 客户端TLSConfig里得带上自己的Certificates(即client.crt + client.key打包的tls.Certificate)
证书和密钥怎么加载才不会panic
Go的tls.LoadX509KeyPair和x509.NewCertPool对文件路径、格式、权限很敏感。常见panic是“crypto/tls: failed to parse certificate PEM data”或“no certificate found”,基本都是文件内容不对。
- 服务端加载CA根证书(用于验证客户端):用
ioutil.ReadFile读ca.crt,再用roots.AppendCertsFromPEM(data),不能直接传文件路径 - 客户端加载自己证书+私钥:必须是
tls.LoadX509KeyPair("client.crt", "client.key"),两个文件缺一不可;私钥不能带密码,也不能是PKCS#8未解密格式(OpenSSL生成时加-nodes) - 服务端证书私钥同理,但注意:私钥文件权限必须是
0600,否则LoadX509KeyPair在某些系统上会静默失败
http.Client发起双向TLS请求时容易漏掉什么
很多人配完服务端,客户端只设了RootCAs(验证服务端),却忘了Certificates字段——这导致连接时服务端收不到客户端证书,直接拒绝,错误通常是remote error: tls: bad certificate。
立即学习“go语言免费学习笔记(深入)”;
正确写法:
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: roots, // 验证服务端
Certificates: []tls.Certificate{cert}, // 提交自己的证书
},
},
}
注意:cert是tls.LoadX509KeyPair返回值,不是文件路径;RootCAs和Certificates必须同时存在,少一个就断连。
为什么本地测试通了,放到K8s里就报x509: certificate signed by unknown authority
不是证书问题,是环境差异。K8s Pod里默认没有宿主机的CA信任库,roots := x509.NewCertPool()初始化后为空,即使你把CA证书内容读进来了,如果没调用AppendCertsFromPEM,roots还是空的——这就导致客户端无法验证服务端证书。
典型漏点:
- 忘记检查
AppendCertsFromPEM返回值:ok := roots.AppendCertsFromPEM(data),如果data是空或格式错,ok为false但没人处理 - CA证书文件被挂载到Pod后权限变了,
ioutil.ReadFile读出来是空字节或报错,但没打日志 - 用了自签名CA,但没把CA证书同时注入到服务端
ClientCAs和服务端RootCAs(客户端验证服务端、服务端验证客户端,两套CA要各自配)
调试时先打印roots.Subjects()长度,确认CA确实加载成功;再用openssl s_client -connect host:port -cert client.crt -key client.key -CAfile ca.crt手动验证链路,排除Go代码外的问题。


















