MongoDB的SSL实现分析
1. OPENSSL接口封装
MongoDB封装了OPENSSL的SSL通信接口,代码在mongo/util/net目录。
主要包括以下几个方面:
1) SSL配置参数,在ssl_options(.cpp/.h)
定义了数据结构SSLGlobalParams,SSLGlobalParams中保存了与SSL相关的所有的配置参数。
在ssl_options中定义了一个SSLGlobalParams类型的全局变量sslGlobalParams,在客户端或者服务器进程启动时会通过相关接口从全局的配置参数中将SSL相关的配置参数保存到sslGlobalParams之中。
主要接口:
addSSLServerOptions()
addSSLClientOptions()
2) SSL证书过期检测,在ssl_expiration(.cpp/.h)
定义了类CertificateExpirationMonitor,该类继承了PeriodicTask,会定期将证书的过期时间与当前时间做比较。如果证书过期,会报警告。
3)SSL连接管理,在ssl_manager(.cpp/.h)
定义了类SSLConnection,该类封装了SSL通信时使用的SSL句柄,BIO以及Socket。
定义了接口类SSLManagerInterface,该类定义了建立SSL连接、验证SSL证书、SSL读写数据等接口。
定义了类SSLThreadInfo,该类用于处理SSL多线程环境下使用的问题。
定义了结构Params,该类保存了所有的SSL配置参数。
定义了类SSLManager,该类继承了接口类SSLManagerInterface,内部保存了SSL通信的Context,通过调用OPENSSL接口实现了SSLManagerInterface定义的接口。
定义了SSLManagerInterface指针类型的全局变量theSSLManager,提供接口getSSLManager(),实现了单例模式。
2. SSL通信与Socket通信的整合
除了封装SSL的代码之外,在MongoDB源代码中使用了SSL接口的地方都使用了宏定义MONGO_SSL。在编译时只有预定义了MONGO_SSL才会编译支持SSL的MongoDB。
MongoDB与通信相关的接口主要是Socket类和MessagingPort类定义的接口。
Socket类封装了socket通信相关的接口。SSL版本在Socket中增加了SSLMangerInterface指针和SSLConnection指针,增加了secure()和doSSLHandshake()接口用于创建SSL连接。
在Socket上创建了SSL连接之后,数据通信会调用SSLManagerInterface的SSL读写接口。这样Socket数据收发接口保持不变,但增加了SSL数据收发的功能。
MessagingPort类中保存了Socket类的变量,增加了一个secure()接口用于创建SSL连接,该接口直接调用Socket的secure()接口。另外修改了recv()接口,增加了接收到SSL握手请求的处理。
在客户端定义了类DBClientConnection用于建立连接,该类包含一个MessagingPort变量,通过调用MessagingPort接口实现通信。
类接口调用关系如下:
DBClientConnection::connect()
|
|---> DBClientConnection::_connect() // 建立socket连接
| |
| |---> MessagingPort::connect()
| |
| |---> Socket::connect()
|
|---> MessagingPort::secure() // 建立SSL连接
|
|---> Socket::secure()
| |
| |---> SSLManager::connect()
| |
| |---> SSL_connect()
|
|---> SSLManager::parseAndValidatePeerCertificate()
MessagingPort::recv()
|
|---> Socket::doSSLHandshake()
|
|---> SSLManager::accept() // 建立SSL连接
| |
| |---> SSL_accpet()
|
|---> SSLManager::parseAndValidatePeerCertificate()
Socket::send()
|
|---> SSLManager::SSL_write()
|
|---> SSL_write()
Socket::recv()
|
|---> Socket::_recv()
|
|---> SSLManager::SSL_read()
|
|---> SSL_read()