// 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]); });