mongo/jstests/ssl/tlsCATrusts.js

221 lines
7.5 KiB
JavaScript

// Test restricting role authorization via X509 extensions.
import {requireSSLProvider} from "jstests/ssl/libs/ssl_helpers.js";
requireSSLProvider("openssl", function () {
const SERVER_CERT = "jstests/libs/server.pem";
const COMBINED_CA_CERT = "jstests/ssl/x509/root-and-trusted-ca.pem";
const CA_HASH = cat("jstests/libs/ca.pem.digest.sha256");
const TRUSTED_CA_HASH = cat("jstests/libs/trusted-ca.pem.digest.sha256");
// Common suffix, keep the lines short.
const RDN_SUFFIX = ",O=MongoDB,L=New York City,ST=New York,C=US";
const USERS = [];
const CLIENT = {
cert: "jstests/libs/client.pem",
roles: [],
};
USERS.push("CN=client,OU=KernelUser");
const CLIENT_ROLES = {
cert: "jstests/libs/client_roles.pem",
roles: [
{role: "backup", db: "admin"},
{role: "readAnyDatabase", db: "admin"},
],
};
USERS.push("CN=Kernel Client Peer Role,OU=Kernel Users");
const TRUSTED_CLIENT_TESTDB_ROLES = {
cert: "jstests/ssl/x509/trusted-client-testdb-roles.pem",
roles: [
{role: "role1", db: "testDB"},
{role: "role2", db: "testDB"},
],
};
USERS.push("CN=Trusted Kernel Test Client With Roles,OU=Kernel Users");
function test(tlsCATrusts, success, failure) {
const options = {
auth: "",
tlsMode: "requireTLS",
tlsCertificateKeyFile: SERVER_CERT,
tlsCAFile: COMBINED_CA_CERT,
};
if (tlsCATrusts !== null) {
options.setParameter = {
tlsCATrusts: tojson(tlsCATrusts),
};
}
const mongod = MongoRunner.runMongod(options);
const admin = mongod.getDB("admin");
admin.createUser({user: "admin", pwd: "pwd", roles: ["root"]});
admin.auth({user: "admin", pwd: "pwd"});
const external = mongod.getDB("$external");
USERS.forEach((u) => external.createUser({user: u + RDN_SUFFIX, roles: []}));
const testDB = mongod.getDB("test");
testDB.createRole({role: "role1", privileges: [], roles: []});
testDB.createRole({role: "role2", privileges: [], roles: []});
// Sorting JS arrays of objects with arbitrary order is... complex.
const serverTrusts = assert.commandWorked(admin.runCommand({getParameter: 1, tlsCATrusts: 1})).tlsCATrusts;
function sortAndNormalizeRoles(roles) {
return roles
.map((r) => r.role + "." + r.db)
.sort()
.join("/");
}
function sortAndNormalizeTrusts(trusts) {
if (trusts === null) {
return "(unconfigured)";
}
return trusts.map((t) => t.sha256 + "/" + sortAndNormalizeRoles(t.roles)).sort();
}
assert.eq(sortAndNormalizeTrusts(tlsCATrusts), sortAndNormalizeTrusts(serverTrusts));
function impl(user, expect) {
const snRoles = tojson(sortAndNormalizeRoles(user.roles));
const uri = "mongodb://localhost:" + mongod.port + "/admin";
const script =
tojson(sortAndNormalizeRoles) +
'assert(db.getSiblingDB("$external").auth({mechanism: "MONGODB-X509"}));' +
"const status = assert.commandWorked(db.runCommand({connectionStatus: 1}));" +
"const roles = status.authInfo.authenticatedUserRoles;" +
"assert.eq(" +
snRoles +
", sortAndNormalizeRoles(roles));";
const mongo = runMongoProgram(
"mongo",
"--tls",
"--tlsCertificateKeyFile",
user.cert,
"--tlsCAFile",
COMBINED_CA_CERT,
uri,
"--eval",
script,
);
expect(mongo, 0);
}
success.forEach((u) => impl(u, assert.eq));
failure.forEach((u) => impl(u, assert.neq));
MongoRunner.stopMongod(mongod);
}
// Positive tests.
const unconfigured = null;
test(unconfigured, [CLIENT, CLIENT_ROLES, TRUSTED_CLIENT_TESTDB_ROLES], []);
const allRoles = [
{sha256: CA_HASH, roles: [{role: "", db: ""}]},
{sha256: TRUSTED_CA_HASH, roles: [{role: "", db: ""}]},
];
test(allRoles, [CLIENT, CLIENT_ROLES, TRUSTED_CLIENT_TESTDB_ROLES], []);
const allRolesOnAdmin = [{sha256: CA_HASH, roles: [{role: "", db: "admin"}]}];
test(allRolesOnAdmin, [CLIENT, CLIENT_ROLES], [TRUSTED_CLIENT_TESTDB_ROLES]);
const specificRolesOnAnyDB = [
{
sha256: CA_HASH,
roles: [
{role: "backup", db: ""},
{role: "readAnyDatabase", db: ""},
],
},
];
test(specificRolesOnAnyDB, [CLIENT, CLIENT_ROLES], [TRUSTED_CLIENT_TESTDB_ROLES]);
const exactRoles = [
{
sha256: CA_HASH,
roles: [
{role: "backup", db: "admin"},
{role: "readAnyDatabase", db: "admin"},
],
},
];
test(exactRoles, [CLIENT, CLIENT_ROLES], [TRUSTED_CLIENT_TESTDB_ROLES]);
const extraRoles = [
{
sha256: CA_HASH,
roles: [
{role: "backup", db: "admin"},
{role: "readAnyDatabase", db: "admin"},
{role: "readWrite", db: "admin"},
],
},
];
test(extraRoles, [CLIENT, CLIENT_ROLES], [TRUSTED_CLIENT_TESTDB_ROLES]);
const similarRoles = [
{
sha256: CA_HASH,
roles: [
{role: "backup", db: "test"},
{role: "readAnyDatabase", db: ""},
{role: "backup", db: "admin"},
],
},
{
sha256: TRUSTED_CA_HASH,
roles: [
{role: "role1", db: "admin"},
{role: "role2", db: "testDB"},
{role: "role1", db: "testDB"},
],
},
];
test(similarRoles, [CLIENT, CLIENT_ROLES, TRUSTED_CLIENT_TESTDB_ROLES], []);
const withUntrusted = [
{sha256: CA_HASH, roles: [{role: "", db: ""}]},
{sha256: TRUSTED_CA_HASH, roles: []},
];
test(withUntrusted, [CLIENT, CLIENT_ROLES], [TRUSTED_CLIENT_TESTDB_ROLES]);
const customRoles = [
{
sha256: TRUSTED_CA_HASH,
roles: [
{role: "role1", db: "testDB"},
{role: "role2", db: "testDB"},
],
},
];
test(customRoles, [CLIENT, TRUSTED_CLIENT_TESTDB_ROLES], [CLIENT_ROLES]);
// Negative tests. CLIENT_CERT is okay because it doesn't ask for roles.
const noTrustedCAs = [];
test(noTrustedCAs, [CLIENT], [CLIENT_ROLES, TRUSTED_CLIENT_TESTDB_ROLES]);
const noRoles = [{sha256: CA_HASH, roles: []}];
test(noRoles, [CLIENT], [CLIENT_ROLES, TRUSTED_CLIENT_TESTDB_ROLES]);
const insufficientRoles1 = [
{sha256: CA_HASH, roles: [{role: "backup", db: ""}]},
{sha256: TRUSTED_CA_HASH, roles: [{role: "role1", db: "testDB"}]},
];
test(insufficientRoles1, [CLIENT], [CLIENT_ROLES, TRUSTED_CLIENT_TESTDB_ROLES]);
const insufficientRoles2 = [
{sha256: CA_HASH, roles: [{role: "readWriteAnyDatabase", db: ""}]},
{sha256: TRUSTED_CA_HASH, roles: [{role: "role2", db: "testDB"}]},
];
test(insufficientRoles2, [CLIENT], [CLIENT_ROLES, TRUSTED_CLIENT_TESTDB_ROLES]);
const withTrusted = [
{sha256: CA_HASH, roles: []},
{sha256: TRUSTED_CA_HASH, roles: [{role: "", db: ""}]},
];
test(withTrusted, [CLIENT, TRUSTED_CLIENT_TESTDB_ROLES], [CLIENT_ROLES]);
});