sslMode: requireSSL 仅强制TLS加密通道,不校验客户端证书;双向认证必须同时设置 sslClientCertificateRequired: true(或 allowConnectionsWithoutCertificates: false)以启用客户端证书强制校验。

为什么 MongoDB 的 sslMode: requireSSL 不等于双向认证
很多人误以为只要配置了 sslMode: requireSSL 就启用了双向认证,其实它只强制客户端走 TLS 通道,但**不校验客户端证书**。真正的双向认证(mTLS)必须显式开启证书校验,否则攻击者仍可伪造任意客户端身份连接——只要能连上端口,就能发请求。
关键区别在于:sslMode: requireSSL 是“通道加密”,而双向认证是“身份绑定”。MongoDB 中控制后者的核心参数是 net.ssl.allowConnectionsWithoutCertificates,它默认为 true,即允许无证书连接;设为 false 才真正启用客户端证书强制校验。
- 必须同时设置
sslMode: requireSSL和allowConnectionsWithoutCertificates: false - 仅设前者,日志里看不到错误,但
db.runCommand({connectionStatus: 1})的authInfo.authenticatedUsers为空,说明没走证书认证 - 如果用自签名 CA,客户端连接时必须提供
--sslCAFile,否则报错unable to get local issuer certificate
net.ssl.clusterFile 和 net.ssl.PEMKeyFile 能不能共用一个 PEM 文件
物理路径可以一样,但语义不能混用——这是最常静默失败的点。在副本集场景下,PEMKeyFile 用于对外服务(比如应用连 mongos 或直连 mongod),而 clusterFile 专供副本集成员间通信(oplog 同步、心跳、rs.syncFrom)。
如果你只配了 PEMKeyFile 没配 clusterFile,节点可能启动成功,但从节点卡在 STARTUP2,主节点却显示 PRIMARY,看起来“正常”,实则集群内部无法完成 TLS 握手。
-
clusterFile必须是 PEM 格式,内容为「该节点私钥 + 该节点证书」,不能含 CA 证书 - 建议单独生成并命名明确,如
cluster-node1.pem,避免和对外证书混淆 - 若启用双向认证(
allowConnectionsWithoutCertificates: false),clusterFile的证书还必须包含extendedKeyUsage = clientAuth, serverAuth
客户端证书必须带 subjectAltName 吗?服务端证书呢
服务端证书**必须**有 subjectAltName(SAN),且每个可访问地址(DNS 名、IP)都得列全;客户端证书在 MongoDB 中**不强制要求 SAN**,但强烈建议加上,尤其当多个应用共用同一 CA、需按 SAN 做细粒度权限控制时(例如通过 $external 角色绑定 subjectName)。
MongoDB 6.0+ 强制校验服务端证书 SAN:哪怕你设了 sslAllowConnectionsWithoutCertificates: true,只要客户端发起 TLS 握手,服务端就会比对 SAN 是否匹配连接所用的 hostname 或 IP。不匹配就断连,报错 SSL peer certificate validation failed。
- 生成服务端证书时,必须用
-addext "subjectAltName = DNS:node1.example.com,IP:192.168.1.10,IP:127.0.0.1"显式添加 - OpenSSL 1.0.x 不支持
-addext,得用配置文件方式,否则 SAN 字段为空 - 客户端证书即使没 SAN,只要能被 CA 正确验证,也能通过身份校验;但缺失 SAN 会让后续基于 X.509 的 RBAC 策略难以落地
连接字符串里怎么传客户端证书?常见失败原因
不是所有驱动都默认读取系统信任库,尤其 Node.js、Python PyMongo、Java Spring Boot 这类运行时环境,必须显式指定证书路径。连接字符串本身不承载证书内容,靠驱动层参数或环境变量注入。
以 PyMongo 为例,直接写 mongodb://user:pass@host:27017/?ssl=true&ssl_cert_reqs=CERT_REQUIRED 是不够的;你还得在代码里传入 tlsCertificateKeyFile 和 tlsCAFile:
client = MongoClient(
"mongodb://localhost:27017/",
tls=True,
tlsCertificateKeyFile="/path/to/client.pem",
tlsCAFile="/path/to/ca.crt",
tlsAllowInvalidHostnames=False # 生产必须关
)
- Node.js 驱动要用
ca、cert、key字段传 Buffer,不能只传路径 - Java 驱动依赖 JVM truststore,把
ca.crt导入$JAVA_HOME/jre/lib/security/cacerts更稳妥 - 常见失败:客户端证书私钥被加密(含
-----BEGIN ENCRYPTED PRIVATE KEY-----),MongoDB 驱动不支持解密,必须用openssl rsa -in client-enc.key -out client.key解密后再合并
双向认证真正难的不是生成证书,而是让每条链路(客户端→服务端、成员→成员、CA→所有证书)的格式、用途、字段都对齐。漏掉任何一个环节,比如忘了给 clusterFile 加 clientAuth 扩展用途,或者客户端连的是 localhost 但证书 SAN 只写了域名,都会导致连接看似成功、实则未认证。











