SERVER-125887 Add support for encrypted PEM files for gRPC egress (#53238)
Co-authored-by: Erwin Pe <erwin.pe@mongodb.com> GitOrigin-RevId: 071e3964bf43e7fa728a5df6390e0b960e335bdf
This commit is contained in:
parent
7139513a1c
commit
5f89b25908
57
jstests/libs/client_password_protected.pem
Normal file
57
jstests/libs/client_password_protected.pem
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Autogenerated file, do not edit.
|
||||||
|
# Generate using jstests/ssl/x509/mkcert.py --config jstests/ssl/x509/certs.yml client_password_protected.pem
|
||||||
|
#
|
||||||
|
# Client certificate using an encrypted private key.
|
||||||
|
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDsDCCApigAwIBAgIEfFChFjANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV
|
||||||
|
UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO
|
||||||
|
BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs
|
||||||
|
IFRlc3QgQ0EwHhcNMjYwNDI5MjM1NTE0WhcNMjgwNzMxMjM1NTE0WjBwMQswCQYD
|
||||||
|
VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENp
|
||||||
|
dHkxEDAOBgNVBAoMB01vbmdvREIxEzARBgNVBAsMCktlcm5lbFVzZXIxDzANBgNV
|
||||||
|
BAMMBmNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOKX9jEC
|
||||||
|
f1LiqN7BPNsEED21HXrehoJzOFmfEaCUJLtN/1J9r8ITDdXjkn9zS+X9TpDGAxQV
|
||||||
|
FOkZs7FHdLetFWDFm1OdZKIlySY8tKBYD6N6nso1R1vNmQVNw5wGj6yl0uir2v3k
|
||||||
|
9NsAlQoe3ijidDj6RbsfoLAK74maA26A5h7+5wgiekcb/zcICLFQ+BoBA+N2RVM3
|
||||||
|
plzlanOkln18NwQEW0mbDHbYmN9xiroA049C0C/ecoMcEhz27A+DZd34uIQTEMxA
|
||||||
|
eZZ4eEOyDraWRpGqzVNv42/uk0e16B79ATrsveStzqZgrMGxNhq4r301pD8XgVqi
|
||||||
|
G7/NCRj8SG0MEOsCAwEAAaNOMEwwCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwEwYD
|
||||||
|
VR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0OBBYEFJ0rjXp58BHb6dcJVbokPN2NtofF
|
||||||
|
MA0GCSqGSIb3DQEBCwUAA4IBAQDENVOR1a0lDZSzrCec6n3ZaWlWBZEbSvrz9pYw
|
||||||
|
HHFsxsNehpyNlJ+uinHOap7/gnaDzCAzkuxUBlZ8zx0XA0JzSxktQv2kj7svqXz8
|
||||||
|
5ncAYK873YtWdX/dytWZm337Rv85LEwJU8/MKGZVIgCyPOGcp+ECCTsrQUN/Js0J
|
||||||
|
NHd6AAOJc6EOR9a/9W18kEcVYauxLwEBuGs+YTluO08CYZffdglP8IRtmwmxipsa
|
||||||
|
Y8HMh2PO+EEYB8PKE3L8GlRWgsUHCHauWgurqDUL9gZOJqTC9JH8d8d3A905w6eg
|
||||||
|
GqpYVsZ0bAdadSvkcCt8bh71TMhKef/F8sIOhHC4D6i/FISz
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
1
jstests/libs/client_password_protected.pem.digest.sha1
Normal file
1
jstests/libs/client_password_protected.pem.digest.sha1
Normal file
@ -0,0 +1 @@
|
|||||||
|
7E414923FB657EDC6C45136FFEB8A29125ED110C
|
||||||
1
jstests/libs/client_password_protected.pem.digest.sha256
Normal file
1
jstests/libs/client_password_protected.pem.digest.sha256
Normal file
@ -0,0 +1 @@
|
|||||||
|
DDCBC0984AED440FDBA3ADC1B297D60FF9A908BB78FD2C565C5970C3BB4764B3
|
||||||
@ -17,6 +17,7 @@ export var TRUSTED_SERVER_CERT = getX509Path("trusted-server.pem");
|
|||||||
export var CA_CERT = getX509Path("ca.pem");
|
export var CA_CERT = getX509Path("ca.pem");
|
||||||
export var TRUSTED_CA_CERT = getX509Path("trusted-ca.pem");
|
export var TRUSTED_CA_CERT = getX509Path("trusted-ca.pem");
|
||||||
export var CLIENT_CERT = getX509Path("client.pem");
|
export var CLIENT_CERT = getX509Path("client.pem");
|
||||||
|
export var CLIENT_PASSWORD_PROTECTED_CERT = getX509Path("client_password_protected.pem");
|
||||||
export var TRUSTED_CLIENT_CERT = getX509Path("trusted-client.pem");
|
export var TRUSTED_CLIENT_CERT = getX509Path("trusted-client.pem");
|
||||||
export var DH_PARAM = "jstests/libs/8k-prime.dhparam";
|
export var DH_PARAM = "jstests/libs/8k-prime.dhparam";
|
||||||
export var CLUSTER_CERT = getX509Path("cluster_cert.pem");
|
export var CLUSTER_CERT = getX509Path("cluster_cert.pem");
|
||||||
|
|||||||
@ -224,6 +224,18 @@ certs:
|
|||||||
- {role: backup, db: admin}
|
- {role: backup, db: admin}
|
||||||
- {role: readAnyDatabase, db: admin}
|
- {role: readAnyDatabase, db: admin}
|
||||||
|
|
||||||
|
- name: "client_password_protected.pem"
|
||||||
|
description: Client certificate using an encrypted private key.
|
||||||
|
Subject: {OU: "KernelUser", CN: "client"}
|
||||||
|
Issuer: "ca.pem"
|
||||||
|
passphrase: "qwerty"
|
||||||
|
pkcs1: true
|
||||||
|
extensions:
|
||||||
|
basicConstraints: {CA: false}
|
||||||
|
subjectKeyIdentifier: hash
|
||||||
|
keyUsage: [digitalSignature, keyEncipherment]
|
||||||
|
extendedKeyUsage: [clientAuth]
|
||||||
|
|
||||||
- name: "cluster_cert.pem"
|
- name: "cluster_cert.pem"
|
||||||
description: Alternate cert for use in intra-cluster communication.
|
description: Alternate cert for use in intra-cluster communication.
|
||||||
Subject: {CN: "clustertest"}
|
Subject: {CN: "clustertest"}
|
||||||
|
|||||||
@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* Check that mongod can use the password protected intracluster certificates for
|
||||||
|
* communication with mongot.
|
||||||
|
*/
|
||||||
|
import {getUUIDFromListCollections} from "jstests/libs/uuid_util.js";
|
||||||
|
import {CA_CERT, SERVER_CERT, CLIENT_PASSWORD_PROTECTED_CERT} from "jstests/ssl/libs/ssl_helpers.js";
|
||||||
|
import {MongotMock} from "jstests/with_mongot/mongotmock/lib/mongotmock.js";
|
||||||
|
|
||||||
|
function runOneTest(mongodTlsOpts, badPassword) {
|
||||||
|
// Set up mongotmock and point the mongod to it.
|
||||||
|
const mongotmock = new MongotMock();
|
||||||
|
mongotmock.start({tlsMode: "requireTLS"});
|
||||||
|
const mongotConn = mongotmock.getConnection();
|
||||||
|
|
||||||
|
const opts = Object.assign(
|
||||||
|
{
|
||||||
|
sslMode: "requireSSL",
|
||||||
|
tlsAllowInvalidCertificates: "",
|
||||||
|
setParameter: {mongotHost: mongotConn.host, searchTLSMode: "requireTLS"},
|
||||||
|
},
|
||||||
|
mongodTlsOpts,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (badPassword) {
|
||||||
|
assert.throws(() => MongoRunner.runMongod(opts));
|
||||||
|
mongotmock.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conn = MongoRunner.runMongod(opts);
|
||||||
|
|
||||||
|
const db = conn.getDB("test");
|
||||||
|
const collName = "search";
|
||||||
|
const coll = db.getCollection(collName);
|
||||||
|
const searchQuery = {
|
||||||
|
query: "cakes",
|
||||||
|
path: "title",
|
||||||
|
};
|
||||||
|
const pipeline = [{$search: searchQuery}];
|
||||||
|
coll.drop();
|
||||||
|
assert.commandWorked(coll.insert({"_id": 1, "title": "cakes"}));
|
||||||
|
|
||||||
|
const collUUID = getUUIDFromListCollections(db, collName);
|
||||||
|
// Give mongotmock some stuff to return.
|
||||||
|
{
|
||||||
|
const cursorId = NumberLong(123);
|
||||||
|
const searchCmd = {search: collName, collectionUUID: collUUID, query: searchQuery, $db: "test"};
|
||||||
|
const history = [
|
||||||
|
{
|
||||||
|
expectedCommand: searchCmd,
|
||||||
|
response: {
|
||||||
|
cursor: {
|
||||||
|
id: NumberLong(0),
|
||||||
|
ns: "test." + collName,
|
||||||
|
nextBatch: [{_id: 1, $searchScore: 0.321}],
|
||||||
|
},
|
||||||
|
ok: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
assert.commandWorked(mongotConn.adminCommand({setMockResponses: 1, cursorId: cursorId, history: history}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a $search query.
|
||||||
|
let cursor = db[collName].aggregate(pipeline);
|
||||||
|
|
||||||
|
const expected = [{"_id": 1, "title": "cakes"}];
|
||||||
|
assert.eq(expected, cursor.toArray());
|
||||||
|
|
||||||
|
MongoRunner.stopMongod(conn);
|
||||||
|
mongotmock.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
const optsClusterFile = {
|
||||||
|
sslCAFile: CA_CERT,
|
||||||
|
sslPEMKeyFile: SERVER_CERT,
|
||||||
|
tlsClusterCAFile: CA_CERT,
|
||||||
|
tlsClusterFile: CLIENT_PASSWORD_PROTECTED_CERT,
|
||||||
|
};
|
||||||
|
// Test usage of password protected cluster file
|
||||||
|
runOneTest({...optsClusterFile, tlsClusterPassword: "foobar"}, true);
|
||||||
|
runOneTest({...optsClusterFile, tlsClusterPassword: "qwerty"}, false);
|
||||||
|
|
||||||
|
const optsPEMKeyFile = {
|
||||||
|
sslCAFile: CA_CERT,
|
||||||
|
sslPEMKeyFile: CLIENT_PASSWORD_PROTECTED_CERT,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test usage of password protected PEM key file
|
||||||
|
runOneTest({...optsPEMKeyFile, tlsCertificateKeyFilePassword: "foobar"}, true);
|
||||||
|
runOneTest({...optsPEMKeyFile, tlsCertificateKeyFilePassword: "qwerty"}, false);
|
||||||
@ -642,7 +642,7 @@ public:
|
|||||||
std::shared_ptr<SSLManagerInterface> manager = nullptr;
|
std::shared_ptr<SSLManagerInterface> manager = nullptr;
|
||||||
if (SSLManagerCoordinator::get() &&
|
if (SSLManagerCoordinator::get() &&
|
||||||
(manager = SSLManagerCoordinator::get()->getSSLManager())) {
|
(manager = SSLManagerCoordinator::get()->getSSLManager())) {
|
||||||
_loadTlsCertificates(manager->getSSLConfiguration());
|
_loadTlsCertificates(manager->getSSLConfiguration(), *manager);
|
||||||
}
|
}
|
||||||
_prunerService.start(_svcCtx, _pool);
|
_prunerService.start(_svcCtx, _pool);
|
||||||
}
|
}
|
||||||
@ -657,9 +657,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MONGO_CONFIG_SSL
|
#ifdef MONGO_CONFIG_SSL
|
||||||
Status rotateCertificates(const SSLConfiguration& sslConfig) try {
|
Status rotateCertificates(const SSLConfiguration& sslConfig,
|
||||||
|
const SSLManagerInterface& sslManager) try {
|
||||||
LOGV2_DEBUG(9886801, 3, "Rotating certificates used for creating gRPC channels");
|
LOGV2_DEBUG(9886801, 3, "Rotating certificates used for creating gRPC channels");
|
||||||
_loadTlsCertificates(sslConfig);
|
_loadTlsCertificates(sslConfig, sslManager);
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
} catch (const DBException& ex) {
|
} catch (const DBException& ex) {
|
||||||
return ex.toStatus();
|
return ex.toStatus();
|
||||||
@ -714,7 +715,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MONGO_CONFIG_SSL
|
#ifdef MONGO_CONFIG_SSL
|
||||||
void _loadTlsCertificates(const SSLConfiguration& sslConfig) {
|
void _loadTlsCertificates(const SSLConfiguration& sslConfig,
|
||||||
|
const SSLManagerInterface& manager) {
|
||||||
auto cache = [&]() -> boost::optional<TLSCache> {
|
auto cache = [&]() -> boost::optional<TLSCache> {
|
||||||
if (!_options.tlsCAFile && !_options.tlsCertificateKeyFile) {
|
if (!_options.tlsCAFile && !_options.tlsCertificateKeyFile) {
|
||||||
return boost::none;
|
return boost::none;
|
||||||
@ -722,7 +724,20 @@ private:
|
|||||||
|
|
||||||
std::vector<::grpc::experimental::IdentityKeyCertPair> certKeyPairs;
|
std::vector<::grpc::experimental::IdentityKeyCertPair> certKeyPairs;
|
||||||
if (_options.tlsCertificateKeyFile) {
|
if (_options.tlsCertificateKeyFile) {
|
||||||
auto sslPair = util::parsePEMKeyFile(_options.tlsCertificateKeyFile.get());
|
::grpc::SslServerCredentialsOptions::PemKeyCertPair sslPair;
|
||||||
|
|
||||||
|
auto certificateKeyFileContents =
|
||||||
|
uassertStatusOK(ssl_util::readPEMFile(_options.tlsCertificateKeyFile.get()));
|
||||||
|
sslPair.cert_chain = certificateKeyFileContents;
|
||||||
|
auto swDecrypted =
|
||||||
|
manager.decryptPEMKey(certificateKeyFileContents,
|
||||||
|
_options.tlsCertificatePassword.value_or(StringData{}));
|
||||||
|
if (swDecrypted == ErrorCodes::NotImplemented) {
|
||||||
|
sslPair.private_key = certificateKeyFileContents;
|
||||||
|
} else {
|
||||||
|
sslPair.private_key = uassertStatusOK(std::move(swDecrypted));
|
||||||
|
}
|
||||||
|
|
||||||
certKeyPairs.push_back(
|
certKeyPairs.push_back(
|
||||||
{std::move(sslPair.private_key), std::move(sslPair.cert_chain)});
|
{std::move(sslPair.private_key), std::move(sslPair.cert_chain)});
|
||||||
}
|
}
|
||||||
@ -843,8 +858,9 @@ void GRPCClient::appendStats(GRPCConnectionStats& stats) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef MONGO_CONFIG_SSL
|
#ifdef MONGO_CONFIG_SSL
|
||||||
Status GRPCClient::rotateCertificates(const SSLConfiguration& config) {
|
Status GRPCClient::rotateCertificates(const SSLConfiguration& config,
|
||||||
return static_cast<StubFactoryImpl&>(*_stubFactory).rotateCertificates(config);
|
const SSLManagerInterface& sslManager) {
|
||||||
|
return static_cast<StubFactoryImpl&>(*_stubFactory).rotateCertificates(config, sslManager);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -90,7 +90,8 @@ public:
|
|||||||
virtual void appendStats(GRPCConnectionStats& stats) const = 0;
|
virtual void appendStats(GRPCConnectionStats& stats) const = 0;
|
||||||
|
|
||||||
#ifdef MONGO_CONFIG_SSL
|
#ifdef MONGO_CONFIG_SSL
|
||||||
virtual Status rotateCertificates(const SSLConfiguration& sslConfig) = 0;
|
virtual Status rotateCertificates(const SSLConfiguration& sslConfig,
|
||||||
|
const SSLManagerInterface& sslManager) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct ConnectOptions {
|
struct ConnectOptions {
|
||||||
@ -270,6 +271,7 @@ public:
|
|||||||
struct Options {
|
struct Options {
|
||||||
boost::optional<StringData> tlsCAFile;
|
boost::optional<StringData> tlsCAFile;
|
||||||
boost::optional<StringData> tlsCertificateKeyFile;
|
boost::optional<StringData> tlsCertificateKeyFile;
|
||||||
|
boost::optional<StringData> tlsCertificatePassword;
|
||||||
bool tlsAllowInvalidCertificates = false;
|
bool tlsAllowInvalidCertificates = false;
|
||||||
bool tlsAllowInvalidHostnames = false;
|
bool tlsAllowInvalidHostnames = false;
|
||||||
};
|
};
|
||||||
@ -283,7 +285,8 @@ public:
|
|||||||
void shutdown() override;
|
void shutdown() override;
|
||||||
void appendStats(GRPCConnectionStats& stats) const override;
|
void appendStats(GRPCConnectionStats& stats) const override;
|
||||||
#ifdef MONGO_CONFIG_SSL
|
#ifdef MONGO_CONFIG_SSL
|
||||||
Status rotateCertificates(const SSLConfiguration& sslConfig) override;
|
Status rotateCertificates(const SSLConfiguration& sslConfig,
|
||||||
|
const SSLManagerInterface& sslManager) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void dropConnections(const Status& status) override;
|
void dropConnections(const Status& status) override;
|
||||||
|
|||||||
@ -256,6 +256,65 @@ TEST_F(GRPCClientTest, GRPCClientConnectWithInvalidCertificate) {
|
|||||||
CommandServiceTestFixtures::makeEchoHandler(), clientThreadBody, std::move(options));
|
CommandServiceTestFixtures::makeEchoHandler(), clientThreadBody, std::move(options));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(GRPCClientTest, GRPCClientConnectWithEncryptedCertificate) {
|
||||||
|
auto options = CommandServiceTestFixtures::makeServerOptions();
|
||||||
|
|
||||||
|
auto clientThreadBody = [&](auto& server, auto& monitor) {
|
||||||
|
GRPCClient::Options options;
|
||||||
|
options.tlsCAFile = "jstests/libs/ca.pem";
|
||||||
|
options.tlsCertificateKeyFile = "jstests/libs/client_password_protected.pem";
|
||||||
|
options.tlsCertificatePassword = "qwerty";
|
||||||
|
|
||||||
|
auto client = makeClient(std::move(options));
|
||||||
|
client->start();
|
||||||
|
|
||||||
|
auto session = client
|
||||||
|
->connect(server.getListeningAddresses().at(0),
|
||||||
|
getReactor(),
|
||||||
|
CommandServiceTestFixtures::kDefaultConnectTimeout,
|
||||||
|
{})
|
||||||
|
.get();
|
||||||
|
assertEchoSucceeds(*session);
|
||||||
|
ASSERT_OK(session->finish());
|
||||||
|
};
|
||||||
|
|
||||||
|
CommandServiceTestFixtures::runWithServer(
|
||||||
|
CommandServiceTestFixtures::makeEchoHandler(), clientThreadBody, std::move(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GRPCClientTest, GRPCClientConnectWithIncorrectCertificatePasswordShouldFail) {
|
||||||
|
auto options = CommandServiceTestFixtures::makeServerOptions();
|
||||||
|
|
||||||
|
auto clientThreadBody = [&](auto& server, auto& monitor) {
|
||||||
|
GRPCClient::Options options;
|
||||||
|
options.tlsCAFile = "jstests/libs/ca.pem";
|
||||||
|
options.tlsCertificateKeyFile = "jstests/libs/client_password_protected.pem";
|
||||||
|
options.tlsCertificatePassword = "wrong!";
|
||||||
|
|
||||||
|
auto client = makeClient(std::move(options));
|
||||||
|
ASSERT_THROWS_CODE(client->start(), DBException, ErrorCodes::InvalidSSLConfiguration);
|
||||||
|
};
|
||||||
|
|
||||||
|
CommandServiceTestFixtures::runWithServer(
|
||||||
|
CommandServiceTestFixtures::makeEchoHandler(), clientThreadBody, std::move(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GRPCClientTest, GRPCClientConnectMissingCertificatePasswordShouldFail) {
|
||||||
|
auto options = CommandServiceTestFixtures::makeServerOptions();
|
||||||
|
|
||||||
|
auto clientThreadBody = [&](auto& server, auto& monitor) {
|
||||||
|
GRPCClient::Options options;
|
||||||
|
options.tlsCAFile = "jstests/libs/ca.pem";
|
||||||
|
options.tlsCertificateKeyFile = "jstests/libs/client_password_protected.pem";
|
||||||
|
|
||||||
|
auto client = makeClient(std::move(options));
|
||||||
|
ASSERT_THROWS_CODE(client->start(), DBException, ErrorCodes::InvalidSSLConfiguration);
|
||||||
|
};
|
||||||
|
|
||||||
|
CommandServiceTestFixtures::runWithServer(
|
||||||
|
CommandServiceTestFixtures::makeEchoHandler(), clientThreadBody, std::move(options));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(GRPCClientTest, GRPCClientConnectNoClientCertificate) {
|
TEST_F(GRPCClientTest, GRPCClientConnectNoClientCertificate) {
|
||||||
auto options = CommandServiceTestFixtures::makeServerOptions();
|
auto options = CommandServiceTestFixtures::makeServerOptions();
|
||||||
options.tlsAllowConnectionsWithoutCertificates = true;
|
options.tlsAllowConnectionsWithoutCertificates = true;
|
||||||
|
|||||||
@ -203,8 +203,10 @@ Status GRPCTransportLayerImpl::setup() {
|
|||||||
}
|
}
|
||||||
if (!sslGlobalParams.sslClusterFile.empty()) {
|
if (!sslGlobalParams.sslClusterFile.empty()) {
|
||||||
_clientOptions.tlsCertificateKeyFile = sslGlobalParams.sslClusterFile;
|
_clientOptions.tlsCertificateKeyFile = sslGlobalParams.sslClusterFile;
|
||||||
|
_clientOptions.tlsCertificatePassword = sslGlobalParams.sslClusterPassword;
|
||||||
} else if (!sslGlobalParams.sslPEMKeyFile.empty()) {
|
} else if (!sslGlobalParams.sslPEMKeyFile.empty()) {
|
||||||
_clientOptions.tlsCertificateKeyFile = sslGlobalParams.sslPEMKeyFile;
|
_clientOptions.tlsCertificateKeyFile = sslGlobalParams.sslPEMKeyFile;
|
||||||
|
_clientOptions.tlsCertificatePassword = sslGlobalParams.sslPEMKeyPassword;
|
||||||
}
|
}
|
||||||
_clientOptions.tlsAllowInvalidHostnames = sslGlobalParams.sslAllowInvalidHostnames;
|
_clientOptions.tlsAllowInvalidHostnames = sslGlobalParams.sslAllowInvalidHostnames;
|
||||||
_clientOptions.tlsAllowInvalidCertificates =
|
_clientOptions.tlsAllowInvalidCertificates =
|
||||||
@ -444,7 +446,8 @@ Status GRPCTransportLayerImpl::rotateCertificates(std::shared_ptr<SSLManagerInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_defaultClient) {
|
if (_defaultClient) {
|
||||||
if (auto status = _defaultClient->rotateCertificates(manager->getSSLConfiguration());
|
if (auto status =
|
||||||
|
_defaultClient->rotateCertificates(manager->getSSLConfiguration(), *manager);
|
||||||
!status.isOK()) {
|
!status.isOK()) {
|
||||||
LOGV2_DEBUG(
|
LOGV2_DEBUG(
|
||||||
9886803, 1, "Failed to rotate egress gRPC TLS certificates", "error"_attr = status);
|
9886803, 1, "Failed to rotate egress gRPC TLS certificates", "error"_attr = status);
|
||||||
@ -456,7 +459,7 @@ Status GRPCTransportLayerImpl::rotateCertificates(std::shared_ptr<SSLManagerInte
|
|||||||
std::lock_guard lk(_mutex);
|
std::lock_guard lk(_mutex);
|
||||||
for (auto&& clientEntry : _clients) {
|
for (auto&& clientEntry : _clients) {
|
||||||
if (auto c = clientEntry.client.lock()) {
|
if (auto c = clientEntry.client.lock()) {
|
||||||
if (auto status = c->rotateCertificates(manager->getSSLConfiguration());
|
if (auto status = c->rotateCertificates(manager->getSSLConfiguration(), *manager);
|
||||||
!status.isOK()) {
|
!status.isOK()) {
|
||||||
LOGV2_DEBUG(10026100,
|
LOGV2_DEBUG(10026100,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@ -280,6 +280,31 @@ TEST_F(GRPCTransportLayerTest, setupIngressWithoutTLSShouldFail) {
|
|||||||
ASSERT_EQ(ErrorCodes::InvalidOptions, tl->setup());
|
ASSERT_EQ(ErrorCodes::InvalidOptions, tl->setup());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(GRPCTransportLayerTest, startupEgressWithClusterPassword) {
|
||||||
|
sslGlobalParams.sslClusterPassword = "qwerty";
|
||||||
|
sslGlobalParams.sslClusterFile = "jstests/libs/password_protected.pem";
|
||||||
|
createAndStartupTL(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GRPCTransportLayerTest, startupEgressWithPEMKeyPassword) {
|
||||||
|
sslGlobalParams.sslPEMKeyPassword = "qwerty";
|
||||||
|
sslGlobalParams.sslPEMKeyFile = "jstests/libs/password_protected.pem";
|
||||||
|
createAndStartupTL(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GRPCTransportLayerTest, startupEgressWithIncorrectSSLPasswordShouldFail) {
|
||||||
|
sslGlobalParams.sslPEMKeyPassword = "wrong!";
|
||||||
|
sslGlobalParams.sslPEMKeyFile = "jstests/libs/password_protected.pem";
|
||||||
|
|
||||||
|
auto options = CommandServiceTestFixtures::makeTLOptions();
|
||||||
|
options.enableIngress = false;
|
||||||
|
options.enableEgress = true;
|
||||||
|
auto tl = makeTL(makeNoopRPCHandler(), std::move(options));
|
||||||
|
ASSERT_OK(tl->setup());
|
||||||
|
ASSERT_EQ(ErrorCodes::InvalidSSLConfiguration, tl->start());
|
||||||
|
tl->shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
using GRPCTransportLayerTestDeathTest = GRPCTransportLayerTest;
|
using GRPCTransportLayerTestDeathTest = GRPCTransportLayerTest;
|
||||||
DEATH_TEST_F(GRPCTransportLayerTestDeathTest,
|
DEATH_TEST_F(GRPCTransportLayerTestDeathTest,
|
||||||
setupWithPortConflictShouldFail,
|
setupWithPortConflictShouldFail,
|
||||||
@ -929,7 +954,9 @@ TEST_F(RotateCertificatesGRPCTransportLayerTest,
|
|||||||
SSLConfiguration newConfig{};
|
SSLConfiguration newConfig{};
|
||||||
newConfig.serverCertificateExpirationDate =
|
newConfig.serverCertificateExpirationDate =
|
||||||
Date_t::fromDurationSinceEpoch(Milliseconds(1234));
|
Date_t::fromDurationSinceEpoch(Milliseconds(1234));
|
||||||
ASSERT_EQ(client->rotateCertificates(newConfig), ErrorCodes::InvalidSSLConfiguration);
|
ASSERT_EQ(client->rotateCertificates(newConfig,
|
||||||
|
*(SSLManagerCoordinator::get()->getSSLManager())),
|
||||||
|
ErrorCodes::InvalidSSLConfiguration);
|
||||||
|
|
||||||
// Make sure we can still connect with the initial certs used before the bad
|
// Make sure we can still connect with the initial certs used before the bad
|
||||||
// rotation.
|
// rotation.
|
||||||
@ -999,7 +1026,8 @@ TEST_F(RotateCertificatesGRPCTransportLayerTest, ClientUsesOldCertsUntilRotate)
|
|||||||
|
|
||||||
SSLConfiguration newConfig{};
|
SSLConfiguration newConfig{};
|
||||||
newConfig.serverCertificateExpirationDate = Date_t::fromMillisSinceEpoch(1234);
|
newConfig.serverCertificateExpirationDate = Date_t::fromMillisSinceEpoch(1234);
|
||||||
ASSERT_OK(client->rotateCertificates(newConfig));
|
ASSERT_OK(client->rotateCertificates(newConfig,
|
||||||
|
*(SSLManagerCoordinator::get()->getSSLManager())));
|
||||||
auto swSession =
|
auto swSession =
|
||||||
client
|
client
|
||||||
->connect(addr, reactor, CommandServiceTestFixtures::kDefaultConnectTimeout, {})
|
->connect(addr, reactor, CommandServiceTestFixtures::kDefaultConnectTimeout, {})
|
||||||
|
|||||||
@ -61,7 +61,8 @@ public:
|
|||||||
MONGO_UNIMPLEMENTED;
|
MONGO_UNIMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status rotateCertificates(const SSLConfiguration& sslConfig) override {
|
Status rotateCertificates(const SSLConfiguration& sslConfig,
|
||||||
|
const SSLManagerInterface& sslManager) override {
|
||||||
MONGO_UNIMPLEMENTED;
|
MONGO_UNIMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,6 +55,7 @@
|
|||||||
#include "mongo/util/modules.h"
|
#include "mongo/util/modules.h"
|
||||||
#include "mongo/util/net/hostandport.h"
|
#include "mongo/util/net/hostandport.h"
|
||||||
#include "mongo/util/net/socket_utils.h"
|
#include "mongo/util/net/socket_utils.h"
|
||||||
|
#include "mongo/util/net/ssl_manager.h"
|
||||||
#include "mongo/util/net/ssl_util.h"
|
#include "mongo/util/net/ssl_util.h"
|
||||||
#include "mongo/util/scopeguard.h"
|
#include "mongo/util/scopeguard.h"
|
||||||
#include "mongo/util/uuid.h"
|
#include "mongo/util/uuid.h"
|
||||||
@ -181,6 +182,7 @@ public:
|
|||||||
static constexpr auto kMaxThreads = 100;
|
static constexpr auto kMaxThreads = 100;
|
||||||
static constexpr auto kServerCertificateKeyFile = "jstests/libs/server_SAN.pem";
|
static constexpr auto kServerCertificateKeyFile = "jstests/libs/server_SAN.pem";
|
||||||
static constexpr auto kClientCertificateKeyFile = "jstests/libs/client.pem";
|
static constexpr auto kClientCertificateKeyFile = "jstests/libs/client.pem";
|
||||||
|
static constexpr auto kClientCertificatePassword = "";
|
||||||
static constexpr auto kClientSelfSignedCertificateKeyFile =
|
static constexpr auto kClientSelfSignedCertificateKeyFile =
|
||||||
"jstests/libs/client-self-signed.pem";
|
"jstests/libs/client-self-signed.pem";
|
||||||
static constexpr auto kCAFile = "jstests/libs/ca.pem";
|
static constexpr auto kCAFile = "jstests/libs/ca.pem";
|
||||||
@ -426,6 +428,7 @@ public:
|
|||||||
options.emplace();
|
options.emplace();
|
||||||
options->tlsCAFile = kCAFile;
|
options->tlsCAFile = kCAFile;
|
||||||
options->tlsCertificateKeyFile = kClientCertificateKeyFile;
|
options->tlsCertificateKeyFile = kClientCertificateKeyFile;
|
||||||
|
options->tlsCertificatePassword = kClientCertificatePassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
::grpc::SslCredentialsOptions sslOps;
|
::grpc::SslCredentialsOptions sslOps;
|
||||||
|
|||||||
@ -297,7 +297,7 @@ inline std::unique_ptr<TempCertificatesDir> copyCertsToTempDir(std::string caFil
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RAII type that caches the sslGlobalParams sslCAFile, sslPEMKeyFile, and sslMode on construction,
|
* RAII type that caches the included sslGlobalParams on construction,
|
||||||
* and restores them to the cached values on destruction.
|
* and restores them to the cached values on destruction.
|
||||||
*/
|
*/
|
||||||
class SSLGlobalParamsGuard {
|
class SSLGlobalParamsGuard {
|
||||||
@ -305,18 +305,27 @@ public:
|
|||||||
SSLGlobalParamsGuard() {
|
SSLGlobalParamsGuard() {
|
||||||
_sslCAFile = sslGlobalParams.sslCAFile;
|
_sslCAFile = sslGlobalParams.sslCAFile;
|
||||||
_sslPEMKeyFile = sslGlobalParams.sslPEMKeyFile;
|
_sslPEMKeyFile = sslGlobalParams.sslPEMKeyFile;
|
||||||
|
_sslPEMKeyPassword = sslGlobalParams.sslPEMKeyPassword;
|
||||||
|
_sslClusterFile = sslGlobalParams.sslClusterFile;
|
||||||
|
_sslClusterPassword = sslGlobalParams.sslClusterPassword;
|
||||||
_sslMode = sslGlobalParams.sslMode.load();
|
_sslMode = sslGlobalParams.sslMode.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
~SSLGlobalParamsGuard() {
|
~SSLGlobalParamsGuard() {
|
||||||
sslGlobalParams.sslCAFile = _sslCAFile;
|
sslGlobalParams.sslCAFile = _sslCAFile;
|
||||||
sslGlobalParams.sslPEMKeyFile = _sslPEMKeyFile;
|
sslGlobalParams.sslPEMKeyFile = _sslPEMKeyFile;
|
||||||
|
sslGlobalParams.sslPEMKeyPassword = _sslPEMKeyPassword;
|
||||||
|
sslGlobalParams.sslClusterFile = _sslClusterFile;
|
||||||
|
sslGlobalParams.sslClusterPassword = _sslClusterPassword;
|
||||||
sslGlobalParams.sslMode.store(_sslMode);
|
sslGlobalParams.sslMode.store(_sslMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _sslCAFile;
|
std::string _sslCAFile;
|
||||||
std::string _sslPEMKeyFile;
|
std::string _sslPEMKeyFile;
|
||||||
|
std::string _sslPEMKeyPassword;
|
||||||
|
std::string _sslClusterFile;
|
||||||
|
std::string _sslClusterPassword;
|
||||||
int _sslMode;
|
int _sslMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -405,6 +405,17 @@ public:
|
|||||||
* SSL connecctions.
|
* SSL connecctions.
|
||||||
*/
|
*/
|
||||||
virtual SSLInformationToLog getSSLInformationToLog() const = 0;
|
virtual SSLInformationToLog getSSLInformationToLog() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt the raw contents of a PEM key file which was encrypted with `password`. Only
|
||||||
|
* implemented for the OpenSSL variant; other implementations return NotImplemented.
|
||||||
|
* TODO SERVER-126149: Replace/remove this function.
|
||||||
|
*/
|
||||||
|
virtual StatusWith<std::string> decryptPEMKey(StringData pemContents,
|
||||||
|
StringData password) const {
|
||||||
|
return Status(ErrorCodes::NotImplemented,
|
||||||
|
"decryptPEMKey is not supported on this platform");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -161,6 +161,9 @@ constexpr std::uint8_t ffdhe3072_g = 0x02;
|
|||||||
|
|
||||||
using UniqueBIO = std::unique_ptr<BIO, OpenSSLDeleter<decltype(::BIO_free), ::BIO_free>>;
|
using UniqueBIO = std::unique_ptr<BIO, OpenSSLDeleter<decltype(::BIO_free), ::BIO_free>>;
|
||||||
|
|
||||||
|
using UniqueEVP_PKEY =
|
||||||
|
std::unique_ptr<EVP_PKEY, OpenSSLDeleter<decltype(::EVP_PKEY_free), ::EVP_PKEY_free>>;
|
||||||
|
|
||||||
#ifdef MONGO_CONFIG_HAVE_SSL_EC_KEY_NEW
|
#ifdef MONGO_CONFIG_HAVE_SSL_EC_KEY_NEW
|
||||||
using UniqueEC_KEY =
|
using UniqueEC_KEY =
|
||||||
std::unique_ptr<EC_KEY, OpenSSLDeleter<decltype(::EC_KEY_free), ::EC_KEY_free>>;
|
std::unique_ptr<EC_KEY, OpenSSLDeleter<decltype(::EC_KEY_free), ::EC_KEY_free>>;
|
||||||
@ -1327,6 +1330,8 @@ public:
|
|||||||
|
|
||||||
SSLInformationToLog getSSLInformationToLog() const final;
|
SSLInformationToLog getSSLInformationToLog() const final;
|
||||||
|
|
||||||
|
StatusWith<std::string> decryptPEMKey(StringData pemContents, StringData password) const final;
|
||||||
|
|
||||||
std::shared_ptr<OCSPStaplingContext> getOcspStaplingContext() {
|
std::shared_ptr<OCSPStaplingContext> getOcspStaplingContext() {
|
||||||
std::lock_guard<std::mutex> guard(_sharedResponseMutex);
|
std::lock_guard<std::mutex> guard(_sharedResponseMutex);
|
||||||
return _ocspStaplingContext;
|
return _ocspStaplingContext;
|
||||||
@ -3817,4 +3822,46 @@ SSLInformationToLog SSLManagerOpenSSL::getSSLInformationToLog() const {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusWith<std::string> SSLManagerOpenSSL::decryptPEMKey(StringData pemContents,
|
||||||
|
StringData password) const {
|
||||||
|
str::uassertNoEmbeddedNulBytes(password);
|
||||||
|
|
||||||
|
UniqueBIO inBIO(::BIO_new_mem_buf(pemContents.data(), pemContents.size()));
|
||||||
|
if (!inBIO) {
|
||||||
|
return Status(ErrorCodes::InvalidSSLConfiguration,
|
||||||
|
fmt::format("Failed to allocate inBIO object. error: {}",
|
||||||
|
getSSLErrorMessage(ERR_get_error())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If `cb` is NULL, `PEM_read_bio_PrivateKey` interprets `u` as a NUL-terminated string
|
||||||
|
// containing the password. Calling `toString()` is necessary as `password` does not have to be
|
||||||
|
// NUL-terminated.
|
||||||
|
std::string passwordStr{password};
|
||||||
|
void* userdata = static_cast<void*>(passwordStr.data());
|
||||||
|
UniqueEVP_PKEY pkey(::PEM_read_bio_PrivateKey(inBIO.get(), nullptr, nullptr, userdata));
|
||||||
|
if (!pkey) {
|
||||||
|
return Status(
|
||||||
|
ErrorCodes::InvalidSSLConfiguration,
|
||||||
|
fmt::format("Failed to read PEM key: {}", getSSLErrorMessage(ERR_get_error())));
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueBIO outBIO(BIO_new(BIO_s_mem()));
|
||||||
|
if (!outBIO) {
|
||||||
|
return Status(ErrorCodes::InvalidSSLConfiguration,
|
||||||
|
fmt::format("Failed to allocate outBIO object. error: {}",
|
||||||
|
getSSLErrorMessage(ERR_get_error())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PEM_write_bio_PrivateKey(outBIO.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr) !=
|
||||||
|
1) {
|
||||||
|
return Status(ErrorCodes::InvalidSSLConfiguration,
|
||||||
|
fmt::format("Failed to serialize decrypted PEM key: {}",
|
||||||
|
getSSLErrorMessage(ERR_get_error())));
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data = nullptr;
|
||||||
|
long len = BIO_get_mem_data(outBIO.get(), &data);
|
||||||
|
return std::string(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mongo
|
} // namespace mongo
|
||||||
|
|||||||
@ -1150,6 +1150,84 @@ TEST(SSLManager, WindowsReusableBufferOps) {
|
|||||||
|
|
||||||
#endif // MONGO_CONFIG_SSL_PROVIDER == MONGO_CONFIG_SSL_PROVIDER_WINDOWS
|
#endif // MONGO_CONFIG_SSL_PROVIDER == MONGO_CONFIG_SSL_PROVIDER_WINDOWS
|
||||||
|
|
||||||
|
// Test decryptPEMKey
|
||||||
|
#if MONGO_CONFIG_SSL_PROVIDER == MONGO_CONFIG_SSL_PROVIDER_OPENSSL
|
||||||
|
TEST(SSLManager, OpenSSLDecryptBadPEMKey) {
|
||||||
|
SSLParams params;
|
||||||
|
params.sslMode.store(::mongo::sslGlobalParams.SSLMode_requireSSL);
|
||||||
|
params.sslPEMKeyFile = "jstests/libs/server.pem";
|
||||||
|
params.sslCAFile = "jstests/libs/ca.pem";
|
||||||
|
|
||||||
|
std::shared_ptr<SSLManagerInterface> manager =
|
||||||
|
SSLManagerInterface::create(params, true /* isSSLServer */);
|
||||||
|
|
||||||
|
auto sw = manager->decryptPEMKey("badPEMContents"_sd, "password"_sd);
|
||||||
|
ASSERT_NOT_OK(sw.getStatus());
|
||||||
|
ASSERT_EQ(sw.getStatus().code(), ErrorCodes::InvalidSSLConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SSLManager, OpenSSLDecryptPEMKeyEmbeddedNullInPasswordNotAllowed) {
|
||||||
|
SSLParams params;
|
||||||
|
params.sslMode.store(::mongo::sslGlobalParams.SSLMode_requireSSL);
|
||||||
|
params.sslPEMKeyFile = "jstests/libs/server.pem";
|
||||||
|
params.sslCAFile = "jstests/libs/ca.pem";
|
||||||
|
|
||||||
|
std::shared_ptr<SSLManagerInterface> manager =
|
||||||
|
SSLManagerInterface::create(params, true /* isSSLServer */);
|
||||||
|
|
||||||
|
ASSERT_THROWS_CODE(
|
||||||
|
manager->decryptPEMKey("whatever"_sd, "password\0extrastuff"_sd), DBException, 9527900);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SSLManager, OpenSSLDecryptPEMKeyPasswordWithoutNulTermination) {
|
||||||
|
SSLParams params;
|
||||||
|
params.sslMode.store(::mongo::sslGlobalParams.SSLMode_requireSSL);
|
||||||
|
params.sslPEMKeyFile = "jstests/libs/server.pem";
|
||||||
|
params.sslCAFile = "jstests/libs/ca.pem";
|
||||||
|
|
||||||
|
auto pemContents = loadFile("jstests/libs/password_protected.pem");
|
||||||
|
auto fullStr = "qwerty_extra_bytes"_sd;
|
||||||
|
StringData password = fullStr.substr(0, 6); // no null termination
|
||||||
|
|
||||||
|
std::shared_ptr<SSLManagerInterface> manager =
|
||||||
|
SSLManagerInterface::create(params, true /* isSSLServer */);
|
||||||
|
|
||||||
|
ASSERT_OK(manager->decryptPEMKey(pemContents, password));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SSLManager, OpenSSLDecryptPEMKeyEmptyPasswordShouldFail) {
|
||||||
|
SSLParams params;
|
||||||
|
params.sslMode.store(::mongo::sslGlobalParams.SSLMode_requireSSL);
|
||||||
|
params.sslPEMKeyFile = "jstests/libs/server.pem";
|
||||||
|
params.sslCAFile = "jstests/libs/ca.pem";
|
||||||
|
|
||||||
|
auto pemContents = loadFile("jstests/libs/password_protected.pem");
|
||||||
|
auto password = ""_sd;
|
||||||
|
|
||||||
|
std::shared_ptr<SSLManagerInterface> manager =
|
||||||
|
SSLManagerInterface::create(params, true /* isSSLServer */);
|
||||||
|
|
||||||
|
auto sw = manager->decryptPEMKey(pemContents, password);
|
||||||
|
ASSERT_NOT_OK(sw.getStatus());
|
||||||
|
ASSERT_EQ(sw.getStatus().code(), ErrorCodes::InvalidSSLConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
TEST(SSLManager, NonOpenSSLDecryptPEMKeyNotImplemented) {
|
||||||
|
SSLParams params;
|
||||||
|
params.sslMode.store(::mongo::sslGlobalParams.SSLMode_requireSSL);
|
||||||
|
params.sslPEMKeyFile = "jstests/libs/server.pem";
|
||||||
|
params.sslCAFile = "jstests/libs/ca.pem";
|
||||||
|
|
||||||
|
std::shared_ptr<SSLManagerInterface> manager =
|
||||||
|
SSLManagerInterface::create(params, true /* isSSLServer */);
|
||||||
|
|
||||||
|
auto sw = manager->decryptPEMKey("pemContents"_sd, "password"_sd);
|
||||||
|
ASSERT_NOT_OK(sw.getStatus());
|
||||||
|
ASSERT_EQ(sw.getStatus().code(), ErrorCodes::NotImplemented);
|
||||||
|
}
|
||||||
|
#endif // MONGO_CONFIG_SSL_PROVIDER == MONGO_CONFIG_SSL_PROVIDER_OPENSSL
|
||||||
|
|
||||||
#ifdef MONGO_CONFIG_SSL
|
#ifdef MONGO_CONFIG_SSL
|
||||||
|
|
||||||
TEST(SSLManager, CheckCertificateInTransientManager) {
|
TEST(SSLManager, CheckCertificateInTransientManager) {
|
||||||
|
|||||||
@ -531,6 +531,34 @@ certs_def = json.encode({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "client_password_protected.pem",
|
||||||
|
"description": "Server cerificate using an encrypted private key.",
|
||||||
|
"Subject": {
|
||||||
|
"OU": "KernelUser",
|
||||||
|
"CN": "client",
|
||||||
|
},
|
||||||
|
"keyfile": "pkcs1_encrypted_key.pem",
|
||||||
|
"passphrase": "qwerty",
|
||||||
|
"Issuer": "ca.pem",
|
||||||
|
"extensions": {
|
||||||
|
"basicConstraints": {
|
||||||
|
"CA": False,
|
||||||
|
},
|
||||||
|
"subjectKeyIdentifier": "hash",
|
||||||
|
"keyUsage": [
|
||||||
|
"digitalSignature",
|
||||||
|
"keyEncipherment",
|
||||||
|
],
|
||||||
|
"extendedKeyUsage": [
|
||||||
|
"clientAuth",
|
||||||
|
],
|
||||||
|
"subjectAltName": {
|
||||||
|
"DNS": "localhost",
|
||||||
|
"IP": "127.0.0.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "password_protected.pem",
|
"name": "password_protected.pem",
|
||||||
"description": "Server cerificate using an encrypted private key.",
|
"description": "Server cerificate using an encrypted private key.",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user