kratos框架必须用http.server.listenandservetls启动https,因其不提供独立runtls方法;需显式构造*http.server并正确配置证书链、私钥权限及san,否则将panic或握手失败。

Kratos框架必须用http.Server.ListenAndServeTLS启动HTTPS
Kratos本身不提供独立的RunTLS方法,它底层仍是标准http.Server,所以不能像Gin或Echo那样调用框架专属TLS启动函数。你必须显式构造*http.Server,再调用其ListenAndServeTLS方法——这是唯一生效路径。
常见错误是直接把Kratos的http.Handler(比如app.HttpServer()返回值)传给http.ListenAndServeTLS,这会编译失败:Go 1.26+ 已严格校验类型,*http.ServeMux或Kratos封装的Handler可能未完全满足http.Handler接口契约(尤其在中间件链异常时)。正确做法是拿到Kratos内置的http.Server实例,或自己新建一个并注入Kratos的Handler。
- 推荐方式:用
kratos.NewApp时通过server.Server选项传入已配置TLSConfig的*http.Server - 不要复用HTTP端口:HTTPS必须监听独立地址,如
":443"或":8443";若与HTTP共用":8080",会导致accept tcp: operation not permitted - 证书参数顺序不可错:
addr、certFile、keyFile、handler;传反了会报tls: failed to find any PEM data in certificate input或crypto/tls: private key does not match public key
证书文件必须含完整链且私钥未加密
Kratos不处理证书链拼接,完全依赖crypto/tls.LoadX509KeyPair行为。这意味着certFile必须是PEM格式且包含完整证书链:先放域名证书,再追加中间CA(如Let’s Encrypt的fullchain.pem),只放cert.pem会导致Java客户端、iOS WebView或curl报x509: certificate signed by unknown authority。
私钥必须是未加密的(生成时用了-nodes),带密码的私钥会让Kratos进程在启动时panic,错误信息极简:仅tls: failed to parse private key,无更多上下文。Linux/macOS下还需确保keyFile权限为0600,否则底层crypto/tls直接拒绝加载,现象是服务静默退出,日志里只有accept tcp: operation not permitted。
- 验证命令:
openssl x509 -in cert.pem -text -noout看是否含Subject Alternative Name(尤其本地开发用localhost时) - 检查私钥:
openssl rsa -in key.pem -check -noout,输出RSA key ok才算有效 - 换行符必须是LF:Windows生成的CRLF会导致
failed to find any PEM data,用dos2unix cert.pem key.pem修复
单向TLS和mTLS配置差异极大,别混用
Kratos默认只做单向TLS(服务器认证),即浏览器或客户端验证Kratos服务身份。要启用mTLS(双向认证),必须显式配置tls.Config.ClientAuth并提供CA证书,否则客户端发来的证书会被忽略——这不是“开关打开就自动生效”,而是需要代码级介入。
单向场景下,只需确保certFile和keyFile正确;但mTLS要求额外三步:1)把CA根证书(ca.crt)读入tls.CertPool;2)设置ClientAuth: tls.RequireAndVerifyClientCert;3)在http.Server.TLSConfig中挂载该CertPool。漏掉任意一步,客户端证书都会被跳过,等效于单向模式。
- mTLS调试关键:用
curl --cert client.crt --key client.key https://localhost:443/health测试,若返回400 Bad Request或ssl handshake failure,大概率是CA证书没加载或ClientAuth级别设低了 - Kratos日志不会主动打印客户端证书详情,需在Handler里手动解析
r.TLS.PeerCertificates来确认是否收到 - 生产环境禁用
tls.VerifyClientCertIfGiven——它允许无证书访问,失去mTLS意义
本地开发用mkcert最省事,别手写openssl req
为localhost生成自签名证书时,现代浏览器(Chrome 117+、Firefox 119+)强制要求SAN(Subject Alternative Name),只填CommonName=localhost已无效。手写openssl req命令极易遗漏-addext "subjectAltName = DNS:localhost,IP:127.0.0.1",导致浏览器直接报ERR_CERT_COMMON_NAME_INVALID且无明确提示。
mkcert自动处理所有细节:mkcert -install注册根CA到系统信任库,mkcert localhost 127.0.0.1 ::1生成的证书自带SAN、使用正确密钥长度、私钥无密码、换行符为LF——所有Kratos启动失败的常见坑都被绕过。
- 生成后文件名固定为
localhost.pem和localhost-key.pem,直接传给ListenAndServeTLS即可 - 不要把
mkcert生成的根CA证书(rootCA.pem)当服务器证书用,那是给客户端信任用的 - 若用Docker部署,注意挂载证书路径权限:容器内
key.pem仍需0600,否则Go runtime拒绝加载
openssl s_client -connect localhost:443 -servername localhost直连验证握手是否成功,比改代码更高效。











