summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Reams <jbreams@mongodb.com>2019-06-07 11:07:10 -0400
committerJonathan Reams <jbreams@mongodb.com>2019-06-11 14:12:51 -0400
commit85ec26ff72f4029c52c40fab796ad53533828e60 (patch)
tree634e1951d8fd846e902d940dfeeb3897231ab855
parent9ad8cdbded0dc00b17dca8344608920507f60e8d (diff)
downloadmongo-85ec26ff72f4029c52c40fab796ad53533828e60.tar.gz
SERVER-41069 Add option to disable embedded roles from X509 certificates
-rw-r--r--jstests/ssl/ssl_x509_roles.js130
-rw-r--r--src/mongo/db/auth/authorization_manager_global_parameters.idl7
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.cpp5
-rw-r--r--src/mongo/db/commands/authentication_commands.cpp6
4 files changed, 99 insertions, 49 deletions
diff --git a/jstests/ssl/ssl_x509_roles.js b/jstests/ssl/ssl_x509_roles.js
index ac355414787..5a872263baa 100644
--- a/jstests/ssl/ssl_x509_roles.js
+++ b/jstests/ssl/ssl_x509_roles.js
@@ -12,12 +12,20 @@ load('jstests/ssl/libs/ssl_helpers.js');
const CLIENT_UTF8_CERT = "jstests/libs/client_utf8.pem";
const CLIENT_EMAIL_CERT = "jstests/libs/client_email.pem";
const CLIENT_TITLE_CERT = "jstests/libs/client_title.pem";
+ const CLIENT_CERT_NO_ROLES = "jstests/libs/client.pem";
const CLIENT_USER =
"C=US,ST=New York,L=New York City,O=MongoDB,OU=Kernel Users,CN=Kernel Client Peer Role";
- function authAndTest(port) {
- const mongo = runMongoProgram("mongo",
+ const CLIENT_USER_NO_ROLES =
+ "CN=client,OU=KernelUser,O=MongoDB,L=New York City,ST=New York,C=US";
+ const smokeScript =
+ 'assert(db.getSiblingDB(\'$external\').auth({ mechanism: \'MONGODB-X509\' }));';
+
+ function authAndTest(port, expectSuccess) {
+ // First we run the shell with the "smoke" user that has no embedded roles to verify
+ // that X509 auth works overall.
+ const smoke = runMongoProgram("mongo",
"--host",
"localhost",
"--port",
@@ -26,13 +34,13 @@ load('jstests/ssl/libs/ssl_helpers.js');
"--sslCAFile",
CA_CERT,
"--sslPEMKeyFile",
- CLIENT_CERT,
- "jstests/ssl/libs/ssl_x509_role_auth.js");
-
- // runMongoProgram returns 0 on success
- assert.eq(0, mongo, "Connection attempt failed");
+ CLIENT_CERT_NO_ROLES,
+ "--eval",
+ smokeScript);
+ assert.eq(smoke, 0, "Could not auth with smoke user");
- const escaped = runMongoProgram("mongo",
+ const runTest = function(cert, script) {
+ const res = runMongoProgram("mongo",
"--host",
"localhost",
"--port",
@@ -41,55 +49,63 @@ load('jstests/ssl/libs/ssl_helpers.js');
"--sslCAFile",
CA_CERT,
"--sslPEMKeyFile",
- CLIENT_ESCAPE_CERT,
- "jstests/ssl/libs/ssl_x509_role_auth_escape.js");
-
- // runMongoProgram returns 0 on success
- assert.eq(0, escaped, "Connection attempt failed");
-
- const utf8 = runMongoProgram("mongo",
- "--host",
- "localhost",
- "--port",
- port,
- "--ssl",
- "--sslCAFile",
- CA_CERT,
- "--sslPEMKeyFile",
- CLIENT_UTF8_CERT,
- "jstests/ssl/libs/ssl_x509_role_auth_utf8.js");
-
- // runMongoProgram returns 0 on success
- assert.eq(0, utf8, "Connection attempt failed");
-
- const email = runMongoProgram("mongo",
- "--host",
- "localhost",
- "--port",
- port,
- "--ssl",
- "--sslCAFile",
- CA_CERT,
- "--sslPEMKeyFile",
- CLIENT_EMAIL_CERT,
- "jstests/ssl/libs/ssl_x509_role_auth_email.js");
+ cert,
+ script);
+
+ let expectExitCode = 0;
+ if (!expectSuccess) {
+ if (_isWindows()) {
+ expectExitCode = -3;
+ } else {
+ expectExitCode = 253;
+ }
+ }
+
+ assert.eq(expectExitCode, res, "Connection attempt failed");
+ };
- // runMongoProgram returns 0 on success
- assert.eq(0, email, "Connection attempt failed");
+ // Then we assert success or failure with each of the X509 certs with embedded roles.
+ runTest(CLIENT_CERT, "jstests/ssl/libs/ssl_x509_role_auth.js");
+ runTest(CLIENT_ESCAPE_CERT, "jstests/ssl/libs/ssl_x509_role_auth_escape.js");
+ runTest(CLIENT_UTF8_CERT, "jstests/ssl/libs/ssl_x509_role_auth_utf8.js");
+ runTest(CLIENT_EMAIL_CERT, "jstests/ssl/libs/ssl_x509_role_auth_email.js");
}
+ const prepConn = function(conn) {
+ const admin = conn.getDB('admin');
+ admin.createUser({user: "admin", pwd: "admin", roles: ["root"]});
+ assert(admin.auth('admin', 'admin'));
+
+ const external = conn.getDB('$external');
+ external.createUser(
+ {user: CLIENT_USER_NO_ROLES, roles: [{'role': 'readWrite', 'db': 'test'}]});
+ };
+
const x509_options = {sslMode: "requireSSL", sslPEMKeyFile: SERVER_CERT, sslCAFile: CA_CERT};
print("1. Testing x.509 auth to mongod");
{
let mongo = MongoRunner.runMongod(Object.merge(x509_options, {auth: ""}));
+ prepConn(mongo);
+
+ authAndTest(mongo.port, true);
+
+ MongoRunner.stopMongod(mongo);
+ }
- authAndTest(mongo.port);
+ jsTestLog("2. Testing disabling x.509 auth with roles");
+ {
+ const mongo = MongoRunner.runMongod(Object.merge(
+ x509_options, {auth: "", setParameter: "allowRolesFromX509Certificates=false"}));
+
+ prepConn(mongo);
+
+ authAndTest(mongo.port, false);
MongoRunner.stopMongod(mongo);
}
- print("2. Testing x.509 auth to mongos");
+ print("3. Testing x.509 auth to mongos");
{
// TODO: Remove 'shardAsReplicaSet: false' when SERVER-32672 is fixed.
let st = new ShardingTest({
@@ -105,7 +121,31 @@ load('jstests/ssl/libs/ssl_helpers.js');
}
});
- authAndTest(st.s0.port);
+ prepConn(st.s0);
+ authAndTest(st.s0.port, true);
+ st.stop();
+ }
+
+ print("4. Testing x.509 auth to mongos with x509 roles disabled");
+ {
+ const localOptions =
+ Object.merge(x509_options, {setParameter: "allowRolesFromX509Certificates=false"});
+ // TODO: Remove 'shardAsReplicaSet: false' when SERVER-32672 is fixed.
+ let st = new ShardingTest({
+ shards: 1,
+ mongos: 1,
+ other: {
+ keyFile: 'jstests/libs/key1',
+ configOptions: localOptions,
+ mongosOptions: localOptions,
+ shardOptions: localOptions,
+ useHostname: false,
+ shardAsReplicaSet: false
+ }
+ });
+
+ prepConn(st.s0);
+ authAndTest(st.s0.port, false);
st.stop();
}
}());
diff --git a/src/mongo/db/auth/authorization_manager_global_parameters.idl b/src/mongo/db/auth/authorization_manager_global_parameters.idl
index b682bcd148d..ba9cbdccc3c 100644
--- a/src/mongo/db/auth/authorization_manager_global_parameters.idl
+++ b/src/mongo/db/auth/authorization_manager_global_parameters.idl
@@ -43,3 +43,10 @@ server_parameters:
cpp_vartype: bool
cpp_varname: gStartupAuthSchemaValidation
default: true
+ allowRolesFromX509Certificates:
+ description:
+ Whether to allow roles contained in X509 certificates if X509 authentication is enabled
+ set_at: startup
+ cpp_vartype: bool
+ cpp_varname: allowRolesFromX509Certificates
+ default: true
diff --git a/src/mongo/db/auth/authz_manager_external_state.cpp b/src/mongo/db/auth/authz_manager_external_state.cpp
index 107110adb47..8093e0fb8b3 100644
--- a/src/mongo/db/auth/authz_manager_external_state.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state.cpp
@@ -30,6 +30,7 @@
#include "mongo/platform/basic.h"
#include "mongo/config.h"
+#include "mongo/db/auth/authorization_manager_global_parameters_gen.h"
#include "mongo/db/auth/authz_manager_external_state.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/operation_context.h"
@@ -49,6 +50,10 @@ bool AuthzManagerExternalState::shouldUseRolesFromConnection(OperationContext* o
return false;
}
+ if (!allowRolesFromX509Certificates) {
+ return false;
+ }
+
auto& sslPeerInfo = SSLPeerInfo::forSession(opCtx->getClient()->session());
return sslPeerInfo.subjectName.toString() == userName.getUser() &&
userName.getDB() == "$external" && !sslPeerInfo.roles.empty();
diff --git a/src/mongo/db/commands/authentication_commands.cpp b/src/mongo/db/commands/authentication_commands.cpp
index 22b6a1326a4..5115bc9293d 100644
--- a/src/mongo/db/commands/authentication_commands.cpp
+++ b/src/mongo/db/commands/authentication_commands.cpp
@@ -66,9 +66,7 @@ namespace mongo {
namespace {
static bool _isX509AuthDisabled;
-static const char _nonceAuthenticationDisabledMessage[] =
- "Challenge-response authentication using getnonce and authenticate commands is disabled.";
-static const char _x509AuthenticationDisabledMessage[] = "x.509 authentication is disabled.";
+static constexpr auto kX509AuthenticationDisabledMessage = "x.509 authentication is disabled."_sd;
#ifdef MONGO_CONFIG_SSL
Status _authenticateX509(OperationContext* opCtx, const UserName& user, const BSONObj& cmdObj) {
@@ -126,7 +124,7 @@ Status _authenticateX509(OperationContext* opCtx, const UserName& user, const BS
// Handle normal client authentication, only applies to client-server connections
else {
if (_isX509AuthDisabled) {
- return Status(ErrorCodes::BadValue, _x509AuthenticationDisabledMessage);
+ return Status(ErrorCodes::BadValue, kX509AuthenticationDisabledMessage);
}
Status status = authorizationSession->addAndAuthorizeUser(opCtx, user);
if (!status.isOK()) {