mirror of https://github.com/mongodb/mongo
321 lines
11 KiB
JavaScript
321 lines
11 KiB
JavaScript
/**
|
|
* This test does full rollovers of the X509 auth for cluster membership using configurable
|
|
* attributes and extensions. It ensures that it is possible to change the attributes and/or
|
|
* extensions used to determine cluster membership while rotating cluster certificates.
|
|
*
|
|
* @tags: [requires_persistence, requires_replication, requires_fcv_70]
|
|
*/
|
|
|
|
import {ReplSetTest} from "jstests/libs/replsettest.js";
|
|
import {determineSSLProvider} from "jstests/ssl/libs/ssl_helpers.js";
|
|
|
|
if (determineSSLProvider() !== "openssl") {
|
|
print("Skipping test, tlsClusterAuthX509 options are only available with OpenSSL");
|
|
quit();
|
|
}
|
|
|
|
/**
|
|
* This is the path of the original certificate and the CA cert used in all configurations.
|
|
*/
|
|
// Subject: C=US, ST=New York, L=New York, O=MongoDB, OU=Kernel, CN=server
|
|
const originalDNAttributes = "O=MongoDB, OU=Kernel";
|
|
const originalCert = "jstests/libs/server.pem";
|
|
const originalCACert = "jstests/libs/ca.pem";
|
|
const defaultPolicyClusterAuthX509Override = {
|
|
attributes: originalDNAttributes,
|
|
};
|
|
/**
|
|
* This is the tlsClusterAuthX509Attributes and path of the certificate which is rolled over to
|
|
* after switching to custom attributes.
|
|
*/
|
|
// Subject: C=US, ST=New York, L=New York City, CN=server, title=foo
|
|
const fooTitleDNAttributes = "C=US, ST=New York, L=New York City, title=foo";
|
|
const fooTitleDNCert = "jstests/libs/server_title_foo_no_o_ou_dc.pem";
|
|
const fooTitleClusterAuthX509Override = {
|
|
attributes: fooTitleDNAttributes,
|
|
};
|
|
/**
|
|
* This is the DN of the certificate which is rolled over to after switching the custom attribute
|
|
* value.
|
|
*/
|
|
// Subject: C=US, ST=New York, L=New York City, O=MongoDB, OU=Kernel, CN=server, title=bar
|
|
const barTitleDNAttributes = "C=US, ST=New York, L=New York City, title=bar";
|
|
const barTitleDNCert = "jstests/libs/server_title_bar.pem";
|
|
const barTitleClusterAuthX509Override = {
|
|
attributes: barTitleDNAttributes,
|
|
};
|
|
/**
|
|
* This is the path of the certificate containing the cluster membership extension set to 'foo'.
|
|
*/
|
|
const fooExtensionCert = "jstests/ssl/libs/cluster-member-foo.pem";
|
|
const fooExtensionClusterAuthX509Override = {
|
|
extensionValue: "foo",
|
|
};
|
|
/**
|
|
* This is the path of the certificate containing the cluster membership extension set to 'bar'.
|
|
*/
|
|
const barExtensionCert = "jstests/ssl/libs/cluster-member-bar.pem";
|
|
const barExtensionClusterAuthX509Override = {
|
|
extensionValue: "bar",
|
|
};
|
|
|
|
const rst = new ReplSetTest({
|
|
nodes: 3,
|
|
waitForKeys: false,
|
|
nodeOptions: {
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: originalCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
},
|
|
});
|
|
rst.startSet();
|
|
rst.initiate(
|
|
Object.extend(rst.getReplSetConfig(), {
|
|
writeConcernMajorityJournalDefault: true,
|
|
}),
|
|
null,
|
|
{initiateWithDefaultElectionTimeout: true, allNodesAuthorizedToRunRSGetStatus: false},
|
|
);
|
|
|
|
// Create a user to login as when auth is enabled later
|
|
rst.getPrimary()
|
|
.getDB("admin")
|
|
.createUser({user: "root", pwd: "root", roles: ["root"]}, {w: 3});
|
|
rst.nodes.forEach((node) => {
|
|
assert(node.getDB("admin").auth("root", "root"));
|
|
});
|
|
|
|
// Future connections should authenticate immediately on connecting so that replSet actions succeed.
|
|
const originalAwaitConnection = MongoRunner.awaitConnection;
|
|
MongoRunner.awaitConnection = function (args) {
|
|
const conn = originalAwaitConnection(args);
|
|
assert(conn.getDB("admin").auth("root", "root"));
|
|
return conn;
|
|
};
|
|
|
|
// This will rollover the cluster to a new config in a rolling fashion. It will return when
|
|
// there is a primary and we are able to write to it.
|
|
function rolloverConfig(newConfig) {
|
|
function restart(node) {
|
|
const nodeId = rst.getNodeId(node);
|
|
rst.stop(nodeId);
|
|
const configId = "n" + nodeId;
|
|
rst.nodeOptions[configId] = newConfig;
|
|
rst.start(nodeId, {remember: false}, true, true);
|
|
rst.awaitSecondaryNodes();
|
|
}
|
|
|
|
rst.nodes.forEach(function (node) {
|
|
restart(node);
|
|
});
|
|
|
|
assert.soon(() => {
|
|
let primary = rst.getPrimary();
|
|
assert.commandWorked(primary.getDB("admin").runCommand({hello: 1}));
|
|
assert.commandWorked(primary.getDB("test").a.insert({a: 1, str: "TESTTESTTEST"}));
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
// Scenario 1: From no tlsClusterAuthX509 to tlsClusterAuthX509Attributes.
|
|
jsTestLog("Transitioning from DC/O/OU only to DC/O/OU and custom subject DN attributes");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: originalCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509Attributes: fooTitleDNAttributes,
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(defaultPolicyClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Rotating cluster member certificates to use subject DNs with custom attributes");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: fooTitleDNCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509Attributes: fooTitleDNAttributes,
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(defaultPolicyClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Removing DC/O/OU policy from override");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: fooTitleDNCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509Attributes: fooTitleDNAttributes,
|
|
});
|
|
|
|
jsTestLog("SUCCESS - certificate rotation from DC/O/OU certs to custom subject DN attribute-based certs");
|
|
|
|
// Scenario 2: From tlsClusterAuthX509Attributes: 'title=foo' to tlsClusterAuthX509Attributes:
|
|
// 'title=bar'
|
|
jsTestLog(
|
|
"Transitioning from custom subject DN attributes with 'title=foo' to custom subject DN attributes with 'title=bar'",
|
|
);
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: fooTitleDNCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509Attributes: barTitleDNAttributes,
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(fooTitleClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Rotating cluster member certificates to use certificates with 'title=bar'");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: barTitleDNCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509Attributes: barTitleDNAttributes,
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(fooTitleClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Removing 'title=foo' attribute from override");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: barTitleDNCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509Attributes: barTitleDNAttributes,
|
|
});
|
|
|
|
jsTestLog("SUCCESS - certificate rotation from one set of custom subject DN attributes to another");
|
|
|
|
// Scenario 3: From tlsClusterAuthX509Attributes to tlsClusterAuthX509ExtensionValue.
|
|
jsTestLog("Transitioning from custom attributes to certificate extension");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: barTitleDNCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509ExtensionValue: "foo",
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(barTitleClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Rotating cluster member certificates to use certificates with extension set to 'foo'");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: fooExtensionCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509ExtensionValue: "foo",
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(barTitleClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Removing 'title=bar' attribute from override");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: fooExtensionCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509ExtensionValue: "foo",
|
|
});
|
|
|
|
jsTestLog("SUCCESS - certificate rotation from custom subject DN attributes to custom extension value");
|
|
|
|
// Scenario 4: From tlsClusterAuthX509ExtensionValue: 'foo' to tlsClusterAuthX509ExtensionValue:
|
|
// 'bar'
|
|
jsTestLog("Transitioning from one custom extension value to another");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: fooExtensionCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509ExtensionValue: "bar",
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(fooExtensionClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Rotating cluster member certificates to use certificates with extension set to 'bar'");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: barExtensionCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509ExtensionValue: "bar",
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(fooExtensionClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Removing 'foo' extension from override");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: barExtensionCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
tlsClusterAuthX509ExtensionValue: "bar",
|
|
});
|
|
|
|
jsTestLog("SUCCESS - certificate rotation from one custom extension value to another");
|
|
|
|
// Scenario 5: From tlsClusterAuthExtensionValue back to default.
|
|
jsTestLog("Transitioning custom extension value to custom subject DN attributes");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: fooExtensionCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(barExtensionClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
jsTestLog("Rotating cluster member certificates to use certificates with DC/O/OU");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: originalCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
setParameter: {
|
|
tlsClusterAuthX509Override: tojson(barExtensionClusterAuthX509Override),
|
|
},
|
|
});
|
|
|
|
// This works because the original cert meets the default DC/O/OU criteria.
|
|
jsTestLog("Removing 'bar' extension from override and the custom attributes");
|
|
rolloverConfig({
|
|
tlsMode: "preferTLS",
|
|
clusterAuthMode: "x509",
|
|
tlsCertificateKeyFile: originalCert,
|
|
tlsCAFile: originalCACert,
|
|
tlsAllowInvalidHostnames: "",
|
|
});
|
|
|
|
jsTestLog("SUCCESS - certificate rotation from extension value back to default");
|
|
|
|
rst.stopSet();
|