给MongoDB启用TLS之后,最磨人的一类故障就是报错码1210。客户端拿着证书去连,服务端日志里却反复刷出证书验证失败的记录,握手阶段直接中断,业务连不上库。这个错误码背后指向的是x.509证书链不完整:MongoDB在校验对端证书时,无法从叶子证书一路追溯到受信任的根CA,中间缺少了签发者证书,整条信任链就断了。要彻底解决它,需要先把证书链的校验逻辑弄明白,再逐环排查断点,最后把PEM文件按正确顺序重新拼装。

故障码1210的底层逻辑:MongoDB怎样校验证书链
一套标准的x.509证书体系分三层。最顶端是根CA,它自签名,是整个信任体系的锚点;中间是若干中间CA,由根CA签发,商业证书厂商几乎都靠中间CA来签发最终用户证书;最末端是叶子证书,也就是部署在MongoDB服务器上的那张证书。校验时,OpenSSL会从叶子证书出发,读取其签发者字段,逐级向上寻找对应的签发证书,一路走到根CA,并且每一级的数字签名都要验算通过,这条链才算完整。
问题在于,MongoDB在TLS握手时只能拿到对方PEM文件里实际包含的内容。如果PEM文件里只塞了一张叶子证书,中间CA的证书既不在里面,也不在本地信任库中,OpenSSL就找不到上一级签发者,链条在第一跳就断了,MongoDB随即抛出1210错误。很多云厂商或者证书机构下发材料时会给出多个文件,运维同学只挑了域名证书那一个去拼PEM,中间证书被漏掉,正是这个错误最典型的成因。
还有一个容易迷惑人的地方:同一张证书在浏览器里访问HTTPS完全正常,一到MongoDB就报1210。原因在于现代浏览器会通过证书里的AIA扩展字段自动去下载缺失的中间证书,把断链悄悄补上;而MongoDB底层依赖的OpenSSL默认不做这个动作,它只认PEM文件和CA文件里实实在在存在的内容。所以浏览器验证通过,并不能作为证书链完整的证据。
排查思路:先定位链条断在哪一环
拿到1210报错后别急着改配置,第一步是把日志里的完整报错信息翻出来看。日志通常会附带OpenSSL的错误细节,比如提示无法获取本地签发者证书,或者校验返回码不匹配,这些细节能帮你判断到底是链条断了、证书过期还是别的问题。确认是链条问题后,再动手检查证书本身。
用OpenSSL查看证书的签发关系是最直接的手段。先看叶子证书的签发者是谁,再看你准备交给MongoDB的CA文件里有没有对应的那张证书:
# 查看服务器证书的持有者和签发者 openssl x509 -in server.pem -noout -subject -issuer # 查看CA文件里装的是哪张证书 openssl x509 -in ca.pem -noout -subject # 直接做一次链校验,模拟MongoDB的验证过程 openssl verify -CAfile ca.pem server.pem
如果verify命令输出unable to get local issuer certificate,基本可以锁定问题:叶子证书的签发者是某张中间CA,而手头的材料里没有它。此时还要顺手检查PEM文件内部证书的排列顺序,正常应该是叶子证书在前、中间证书在后,顺序颠倒同样会干扰校验。另外确认一下证书有没有过期,过期证书会报出另外的错误信息,处理方式是重新签发而不是补链。
修复方案:按正确顺序拼装PEM并调整mongod配置
修复的核心动作是把中间证书补进PEM文件。假设证书机构下发了三个文件:域名证书server.crt、中间证书chain.crt(有些厂商命名为intermediate或ca_bundle)、私钥server.key。拼装顺序必须是叶子证书在前,中间证书紧随其后,私钥放在最后,用cat命令合并即可:
# 先拼证书链:叶子证书在前,中间证书在后 cat server.crt chain.crt > server-fullchain.crt # 再把私钥追加进去,得到MongoDB要用的PEM文件 cat server-fullchain.crt server.key > server.pem # 合并完成后再次校验,确认链条完整 openssl verify -CAfile ca.pem server-fullchain.crt
拼好之后修改mongod的配置文件。CA文件里放根证书,如果客户端证书也是同一个体系签发的,可以连同中间证书一起放进去,方便双向校验。配置示例如下:
net:
port: 27017
bindIp: 0.0.0.0
tls:
mode: requireTLS
certificateKeyFile: /etc/mongodb/certs/server.pem
CAFile: /etc/mongodb/certs/ca.pemWindows环境下路径写法注意用反斜杠,比如C:\mongodb\certs\server.pem,配置文件里同样遵循这个写法。改完配置重启mongod,如果启动时直接报私钥权限错误,检查一下PEM文件的读权限,mongod出于安全考虑要求私钥文件不能被其他用户随意读取,Linux下把权限收紧到600通常就能解决。
客户端这边也别忽略。mongosh连接时要通过tlsCAFile参数指定信任的根证书,否则客户端这一侧同样会因为找不到签发者而握手失败,报出来的错误编号虽然不同,但根因是一样的。
修复后的验证方法与常见残留问题
配置改完不等于万事大吉,建议先用OpenSSL模拟一次完整的TLS握手,看看服务端实际下发的证书链长什么样:
# 连接27017端口,查看服务端返回的证书链 openssl s_client -connect 127.0.0.1:27017 -CAfile ca.pem # 用mongosh带证书和CA做真实连接测试 mongosh --host mongo.ipipp.com --port 27017 \ --tls \ --tlsCertificateKeyFile /etc/mongodb/certs/client.pem \ --tlsCAFile /etc/mongodb/certs/ca.pem
s_client的输出里会列出服务端发来的每一张证书,数一数是否包含了中间证书,再看最底下的Verify return code是不是ok。两项都通过,再用mongosh做一次真实连接,能正常执行db.version()就说明链路彻底调通了。
还有几个残留问题值得留意。一是证书里的SAN字段必须覆盖客户端连接时用的主机名,用IP直连就要确认IP在SAN里,否则会撞上主机名不匹配的报错,和1210长得像但成因不同。二是副本集或者分片集群的每个成员都要做同样的证书链修复,内部节点之间的双向校验同样依赖完整链条,漏掉一台机器,复制链路照样起不来。三是证书到期时间要纳入监控,这次补好了链,半年后叶子证书过期又会出现新的握手失败,提前在运维平台加上到期告警,能省去不少被动救火的时间。