iris.TLS() 不支持双向认证,必须用 ConfigureHost + http.Server 配置 tls.Config 并设置 ClientAuth 和 ClientCAs;需手动解析 CA 证书到 x509.CertPool,且不能与 iris.TLS() 混用。

iris.TLS() 不支持双向认证,必须用 ConfigureHost + http.Server
iris.TLS("host:port", "cert.pem", "key.pem") 只做单向服务端证书下发,客户端证书校验完全不生效。要强制客户端提供并验证证书,得绕过 iris.TLS(),改用 ConfigureHost() 配合自定义 http.Server。
根本原因是 iris.TLS() 内部调用的是 http.ListenAndServeTLS(),它不暴露 ClientAuth 和 ClientCAs 配置入口;而双向认证必须由 tls.Config 显式控制。
- 必须在
app.ConfigureHost()里设置s.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert -
s.TLSConfig.ClientCAs要加载 CA 证书池(不能直接传 PEM 字节,得用x509.NewCertPool()解析) - 不能混用
iris.TLS()和ConfigureHost():一旦调用了iris.TLS(),ConfigureHost()里的 TLS 配置会被忽略
加载客户端 CA 证书的正确方式
常见错误是把 PEM 文件内容直接塞进 ClientCAs 字段——这会 panic,因为该字段类型是 *x509.CertPool,不是 []byte。
必须手动解析:
caPEM, _ := os.ReadFile("client-ca.pem")
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(caPEM)
如果 CA 证书链含多个根或中间证书,AppendCertsFromPEM() 会自动处理;但若文件里混有私钥或非 PEM 块,会静默跳过——建议先用 openssl x509 -in client-ca.pem -text -noout 确认格式干净。
- 证书文件路径必须可读,且进程有权限打开(Docker 中注意挂载和 SELinux 上下文)
- 不要用相对路径如
"./certs/client-ca.pem",启动目录不确定;推荐用filepath.Join(os.Getenv("PWD"), "certs", "client-ca.pem")或硬编码绝对路径
完整启动代码示例(含错误处理)
以下是最小可行配置,省略了日志和 panic 捕获,但保留关键校验逻辑:
app := iris.New()
// 加载服务端证书
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
// 加载客户端 CA
caPEM, err := os.ReadFile("client-ca.pem")
if err != nil {
log.Fatal(err)
}
caPool := x509.NewCertPool()
if !caPool.AppendCertsFromPEM(caPEM) {
log.Fatal("failed to parse client CA certificate")
}
app.ConfigureHost(func(s *http.Server) {
s.TLSConfig = &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: caPool,
MinVersion: tls.VersionTLS12,
}
})
app.Get("/api/secure", func(ctx iris.Context) {
// 客户端证书信息可通过 ctx.Request().TLS.PeerCertificates[0] 获取
ctx.WriteString("OK")
})
app.Run(iris.Server(&http.Server{
Addr: ":443",
}))
-
ctx.Request().TLS.PeerCertificates是切片,索引 0 是终端用户证书,后续可能是中间 CA;为空说明未通过校验(HTTP 400 或连接直接断开) - 浏览器访问会弹出证书选择框;curl 必须显式指定
--cert client.crt --key client.key --cacert ca.pem - 若用 Nginx 做反向代理,需透传
$ssl_client_cert,Iris 无法直接拿到原始证书链
调试双向认证失败的三个关键点
大部分问题卡在握手阶段,看不到 HTTP 日志。先确认底层是否通:
- 用
openssl s_client -connect localhost:443 -cert client.crt -key client.key -CAfile client-ca.pem测试:若返回Verify return code: 0 (ok)说明 TLS 层 OK;若为 21 表示证书不被信任,检查ClientCAs是否加载成功 - Wireshark 抓包看 ServerHello 后有没有 CertificateRequest —— 没有说明
ClientAuth未生效,大概率是ConfigureHost()调用时机错(必须在Run()前)或被iris.TLS()覆盖 - Go 的
tls.Config.GetConfigForClient不会被 Iris 调用,别试图在这里动态切换策略;双向认证是全局开关,不能 per-route 控制
真正麻烦的是证书链完整性、时间戳校验、名称约束(Name Constraints)这些 X.509 细节,它们在 TLS 握手时就失败,根本不会走到 Iris 的路由层。


















