Spark应用连接SSL启用Mongo集群报错求助(自签名证书)
解决Spark连接启用SSL的Mongo集群(自签名证书)的SSL握手错误
看起来你遇到的是典型的SSL握手失败问题,根源有两个:一是你的自签名证书里没有包含连接用的CLUSTER_NAME(主机名不匹配),二是当前的TrustAllX509TrustManager只跳过了证书信任校验,但没处理主机名验证的问题。结合你的Spark 2.2.0 + Mongo 3.4环境,给你几个针对性的解决方案:
方案1:修正自签名证书(生产环境推荐)
这个方案从根源解决问题,确保证书的主机名与你连接的集群名称匹配:
- 重新生成自签名证书时,务必在**Common Name(CN)或者Subject Alternative Name(SAN)**字段中包含你的
CLUSTER_NAME,如果是副本集,还要添加所有节点的主机名/IP地址。 - 用
openssl生成证书的示例命令:
openssl req -x509 -newkey rsa:4096 -keyout mongo-key.pem -out mongo-cert.pem -days 365 \ -subj "/CN=CLUSTER_NAME" \ -addext "subjectAltName=DNS:CLUSTER_NAME,DNS:node1.mongo.local,DNS:node2.mongo.local,IP:192.168.1.10,IP:192.168.1.11"
- 将生成的证书导入到Spark集群所有节点的JVM信任存储中(路径一般为
$JAVA_HOME/jre/lib/security/cacerts),或者在Spark应用中指定证书路径。
方案2:临时禁用主机名验证(仅限测试环境)
如果只是测试环境想快速打通连接,可以在Mongo连接URI中添加sslInvalidHostNameAllowed=true参数,跳过主机名匹配检查:
- 修改你的URI生成代码:
val uri: String = "mongodb://" + URLEncoder.encode(Login, "UTF-8") + ":" + URLEncoder.encode(Password, "UTF-8") + "@" + cluster + ":27017/" + database + "." + collection + "?authSource=" + (if (authenticationDatabase != "") authenticationDatabase else "admin") + (if (replicaset == null) "" else "&replicaSet=" + replicaset) + "&ssl=true&sslInvalidHostNameAllowed=true"
注意:这个方法会降低连接安全性,绝对不能在生产环境使用。
方案3:正确配置自定义SSL信任(生产规范做法)
不要用TrustAllX509TrustManager这种完全信任所有证书的方式,应该只信任你的自签名证书,同时保留主机名验证(如果证书已修正):
import java.io.FileInputStream import java.security.cert.X509Certificate import java.security.{KeyStore, SecureRandom} import javax.net.ssl.{SSLContext, TrustManagerFactory, CertificateFactory} import com.mongodb.client.MongoClients import com.mongodb.connection.SslSettings import com.mongodb.MongoClientSettings // 加载你的自签名证书 val certPath = "/path/to/your/mongo-cert.pem" val certInputStream = new FileInputStream(certPath) val certFactory = CertificateFactory.getInstance("X.509") val mongoCert = certFactory.generateCertificate(certInputStream).asInstanceOf[X509Certificate] // 创建仅信任该证书的信任管理器 val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm) val keyStore = KeyStore.getInstance(KeyStore.getDefaultType) keyStore.load(null, null) keyStore.setCertificateEntry("mongo-trust-cert", mongoCert) trustManagerFactory.init(keyStore) val trustManagers = trustManagerFactory.getTrustManagers // 初始化SSLContext val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, trustManagers, new SecureRandom()) // 构建Mongo客户端配置 val mongoClientSettings = MongoClientSettings.builder() .applyConnectionString(com.mongodb.ConnectionString(uri)) .sslSettings(SslSettings.builder() .enabled(true) .sslContext(sslContext) // 如果证书仍未匹配主机名,测试环境可临时开启这行,生产环境请删除 //.invalidHostNameAllowed(true) .build()) .build() // 创建Mongo客户端 val mongoClient = MongoClients.create(mongoClientSettings)
额外注意事项
- 确保Spark使用的Mongo驱动版本与Mongo 3.4兼容(推荐使用Mongo Java Driver 3.6.x版本,Spark 2.2.0对应的
mongo-spark-connector版本建议选2.2.x)。 - 如果Spark以集群模式运行,要保证所有Worker节点都能访问到证书文件,或者将证书统一导入到所有节点的JVM信任存储中。
内容的提问来源于stack exchange,提问作者knowledge_seeker
相关产品推荐
相关产品推荐

