mongo/jstests/ssl_linear/ssl_cert_selector.js

162 lines
6.0 KiB
JavaScript

/**
* Tests that the server can load its keys and certificates from the certificate store
* through a selector, and that the correct keys are used for ingress/egress connections.
*/
import {getPython3Binary} from "jstests/libs/python.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {
requireSSLProvider,
TRUSTED_CA_CERT,
TRUSTED_CLIENT_CERT,
TRUSTED_SERVER_CERT,
} from "jstests/ssl/libs/ssl_helpers.js";
const clientThumbprint = cat("jstests/libs/trusted-client.pem.digest.sha1");
const serverThumbprint = cat("jstests/libs/trusted-server.pem.digest.sha1");
const clusterServerThumbprint = cat("jstests/libs/trusted-cluster-server.pem.digest.sha1");
const CLIENT = "CN=Trusted Kernel Test Client,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US";
const SERVER = "CN=Trusted Kernel Test Server,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US";
const CLUSTER_SERVER = "CN=Trusted Kernel Test Cluster Server,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US";
const testCases = [
// Configure server with only a certificateSelector - we expect this to be used instead of
// the --tlsCertificateKeyFile by the server for both ingress (server) and egress (client)
// traffic for both cluster and other communication
//
{
selector: `thumbprint=${clusterServerThumbprint}`,
expectIngressKeyUsed: CLUSTER_SERVER,
expectEgressKeyUsed: CLUSTER_SERVER,
},
{
selector: `thumbprint=${serverThumbprint}`,
clusterSelector: `thumbprint=${clientThumbprint}`,
expectIngressKeyUsed: SERVER,
expectEgressKeyUsed: CLIENT,
},
{
keyFile: TRUSTED_SERVER_CERT,
clusterSelector: `thumbprint=${clientThumbprint}`,
expectIngressKeyUsed: SERVER,
expectEgressKeyUsed: CLIENT,
},
{
selector: `thumbprint=${serverThumbprint}`,
clusterFile: TRUSTED_CLIENT_CERT,
expectIngressKeyUsed: SERVER,
expectEgressKeyUsed: CLIENT,
},
{
selector: "subject=Trusted Kernel Test Server",
clusterSelector: "subject=Trusted Kernel Test Client",
expectIngressKeyUsed: SERVER,
expectEgressKeyUsed: CLIENT,
},
];
function testServerSelectorKeyUsage(testCase) {
jsTestLog(`Running testServerSelectorKeyUsage with test case: ${tojson(testCase)}`);
// Start a replica set with one mongod configured with the test case key file parameters
// and a system CA store containing trusted-ca.pem.
const rst = new ReplSetTest({nodes: 1});
rst.startSet({
tlsMode: "requireTLS",
tlsCertificateKeyFile: testCase.keyFile,
tlsCertificateSelector: testCase.selector,
tlsClusterFile: testCase.clusterFile,
tlsClusterCertificateSelector: testCase.clusterSelector,
tlsAllowInvalidHostnames: "",
tlsAllowConnectionsWithoutCertificates: "",
waitForConnect: true,
setParameter: {tlsUseSystemCA: true},
});
rst.initiate();
rst.awaitReplication();
let conn = rst.getPrimary();
jsTestLog("Testing server uses correct key on ingress");
assert.soon(function () {
return (
runMongoProgram(
"mongo",
"--tls",
"--tlsAllowInvalidHostnames",
"--tlsCAFile",
TRUSTED_CA_CERT,
"--tlsCertificateKeyFile",
TRUSTED_CLIENT_CERT,
"--port",
conn.port,
"--eval",
"quit()",
) === 0
);
}, "mongo did not initialize properly");
assert.soon(
() => {
const log = rawMongoProgramOutput(".*");
return log.search(testCase.expectIngressKeyUsed) !== -1;
},
`logfile did not contain expected peer certificate info: ${testCase.expectIngressKeyUsed}.\n` +
"Log File Contents\n==============================\n" +
rawMongoProgramOutput(".*") +
"\n==============================\n",
);
jsTestLog("Testing server uses correct key on egress");
// Add new node to test the other node's egress key
let otherNode = rst.add({
tlsMode: "requireTLS",
tlsCertificateKeyFile: TRUSTED_SERVER_CERT,
tlsCAFile: TRUSTED_CA_CERT,
tlsAllowInvalidHostnames: "",
setParameter: {tlsWithholdClientCertificate: true},
waitForConnect: true,
});
jsTestLog("Reinitiating replica set with one additional node");
rst.reInitiate();
rst.awaitSecondaryNodes();
assert.commandWorked(otherNode.adminCommand({clearLog: "global"}));
// Verify node 1 can now connect to node 2
jsTestLog("Forcing egress connection with replSetTestEgress...");
assert.commandWorked(conn.adminCommand({replSetTestEgress: 1}));
checkLog.containsRelaxedJson(otherNode, 6723802, {peerSubjectName: testCase.expectEgressKeyUsed});
jsTestLog("Stopping the replica set...");
rst.stopSet();
}
requireSSLProvider("windows", function () {
if (_isWindows()) {
assert.eq(0, runProgram(getPython3Binary(), "jstests/ssl_linear/windows_castore_cleanup.py"));
// SChannel backed follows Windows rules and only trusts Root in LocalMachine
runProgram("certutil.exe", "-addstore", "-f", "Root", TRUSTED_CA_CERT);
// Import a pfx file since it contains both a cert and private key and is easy to import
// via command line.
const importPfx = function (pfxFile) {
return runProgram("certutil.exe", "-importpfx", "-f", "-p", "qwerty", pfxFile);
};
assert.eq(0, importPfx("jstests\\libs\\trusted-client.pfx"));
assert.eq(0, importPfx("jstests\\libs\\trusted-server.pfx"));
assert.eq(0, importPfx("jstests\\libs\\trusted-cluster-server.pfx"));
}
try {
testCases.forEach((test) => testServerSelectorKeyUsage(test));
} finally {
if (_isWindows()) {
const trusted_ca_thumbprint = cat("jstests/libs/trusted-ca.pem.digest.sha1");
runProgram("certutil.exe", "-delstore", "-f", "Root", trusted_ca_thumbprint);
}
}
});