diff options
author | mathisbessamdb <mathis.bessa@mongodb.com> | 2022-12-20 23:02:26 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-12-21 00:22:27 +0000 |
commit | 7399dc8f481fb056ea36085548da39868c4217b9 (patch) | |
tree | 6cc11d306ee94911ec06d134f48bb9d60a2ae16d | |
parent | d1500aed6baa47ca511692c5ca326321c80cd50d (diff) | |
download | mongo-7399dc8f481fb056ea36085548da39868c4217b9.tar.gz |
SERVER-71861 Allow killOp with security token
-rw-r--r-- | jstests/core/count10.js | 2 | ||||
-rw-r--r-- | jstests/core/count_plan_summary.js | 2 | ||||
-rw-r--r-- | jstests/core/currentop_shell.js | 2 | ||||
-rw-r--r-- | jstests/core/illegal_cmd_namespace.js | 2 | ||||
-rw-r--r-- | jstests/core/killop_drop_collection.js | 2 | ||||
-rw-r--r-- | jstests/core/mr_killop.js | 2 | ||||
-rw-r--r-- | jstests/core/shellkillop.js | 2 | ||||
-rw-r--r-- | jstests/core/txns/transactions_profiling_with_drops.js | 2 | ||||
-rw-r--r-- | jstests/libs/override_methods/inject_security_token.js | 2 | ||||
-rw-r--r-- | jstests/serverless/native_tenant_data_isolation_kill_op.js | 107 | ||||
-rw-r--r-- | src/mongo/db/commands/kill_op.cpp | 4 |
11 files changed, 116 insertions, 13 deletions
diff --git a/jstests/core/count10.js b/jstests/core/count10.js index 7f5e2957abe..c981e45a9b8 100644 --- a/jstests/core/count10.js +++ b/jstests/core/count10.js @@ -1,8 +1,6 @@ // Test that interrupting a count returns an error code. // -// The test runs commands that are not allowed with security token: killOp. // @tags: [ -// not_allowed_with_security_token, // # This test attempts to perform a count command and find it using the currentOp command. The // # former operation may be routed to a secondary in the replica set, whereas the latter must be // # routed to the primary. diff --git a/jstests/core/count_plan_summary.js b/jstests/core/count_plan_summary.js index 9db379a1a0a..f38e6f00a11 100644 --- a/jstests/core/count_plan_summary.js +++ b/jstests/core/count_plan_summary.js @@ -1,8 +1,6 @@ // Test that the plan summary string appears in db.currentOp() for count operations. SERVER-14064. // -// The test runs commands that are not allowed with security token: killOp. // @tags: [ -// not_allowed_with_security_token, // # This test attempts to perform a find command and find it using the currentOp command. The // # former operation may be routed to a secondary in the replica set, whereas the latter must be // # routed to the primary. diff --git a/jstests/core/currentop_shell.js b/jstests/core/currentop_shell.js index 365e1ab3344..72f236658b4 100644 --- a/jstests/core/currentop_shell.js +++ b/jstests/core/currentop_shell.js @@ -2,7 +2,7 @@ * Tests that the shell helper db.currentOpCursor isn't constrained by the legacy currentOp server * command - ie. the result set isn't limited to 16MB and long operations aren't truncated. * - * The test runs commands that are not allowed with security token: getLog, killOp. + * The test runs commands that are not allowed with security token: getLog. * @tags: [ * not_allowed_with_security_token, * uses_parallel_shell, diff --git a/jstests/core/illegal_cmd_namespace.js b/jstests/core/illegal_cmd_namespace.js index f3075337abe..3dc26c4e67a 100644 --- a/jstests/core/illegal_cmd_namespace.js +++ b/jstests/core/illegal_cmd_namespace.js @@ -2,9 +2,7 @@ * Test that an attempt to run a query over a $cmd namespace is not treated specially by the shell, * but is rejected by the server. * - * The test runs commands that are not allowed with security token: killop. * @tags: [ - * not_allowed_with_security_token, * assumes_unsharded_collection, * ] */ diff --git a/jstests/core/killop_drop_collection.js b/jstests/core/killop_drop_collection.js index b8a4597712a..17e0154fbfb 100644 --- a/jstests/core/killop_drop_collection.js +++ b/jstests/core/killop_drop_collection.js @@ -4,7 +4,7 @@ * This test confirms that killOp won't interrupt a collection drop, and that the drop occurs * successfully. * - * The test runs commands that are not allowed with security token: fsyncUnlock, killOp. + * The test runs commands that are not allowed with security token: fsyncUnlock. * @tags: [ * not_allowed_with_security_token, * assumes_superuser_permissions, diff --git a/jstests/core/mr_killop.js b/jstests/core/mr_killop.js index 8ed4d1cc680..de91c72c7af 100644 --- a/jstests/core/mr_killop.js +++ b/jstests/core/mr_killop.js @@ -1,5 +1,5 @@ // Test killop applied to m/r operations and child ops of m/r operations. -// The test runs commands that are not allowed with security token: killOp, mapreduce. +// The test runs commands that are not allowed with security token: mapreduce. // @tags: [ // not_allowed_with_security_token, // # mapReduce does not support afterClusterTime. diff --git a/jstests/core/shellkillop.js b/jstests/core/shellkillop.js index 78e757c5726..0ac2ad681ed 100644 --- a/jstests/core/shellkillop.js +++ b/jstests/core/shellkillop.js @@ -1,5 +1,3 @@ -// The test runs commands that are not allowed with security token: killop. -// @tags: [not_allowed_with_security_token] baseName = "jstests_shellkillop"; // 'retry' should be set to true in contexts where an exception should cause the test to be retried diff --git a/jstests/core/txns/transactions_profiling_with_drops.js b/jstests/core/txns/transactions_profiling_with_drops.js index d71328b701f..821070fd51d 100644 --- a/jstests/core/txns/transactions_profiling_with_drops.js +++ b/jstests/core/txns/transactions_profiling_with_drops.js @@ -1,5 +1,5 @@ // Tests that locks acquisitions for profiling in a transaction have a 0-second timeout. -// The test runs commands that are not allowed with security token: endSession, killOp, profile. +// The test runs commands that are not allowed with security token: endSession, profile. // @tags: [ // not_allowed_with_security_token,uses_transactions, uses_parallel_shell] (function() { diff --git a/jstests/libs/override_methods/inject_security_token.js b/jstests/libs/override_methods/inject_security_token.js index dcce73b4e5b..b7ef386f330 100644 --- a/jstests/libs/override_methods/inject_security_token.js +++ b/jstests/libs/override_methods/inject_security_token.js @@ -148,7 +148,7 @@ const kCmdsAllowedWithSecurityToken = new Set([ // `connectionStatus`, // `connPoolStats`, // `top`, - // `killop`, + `killop`, // `endSessions`, ]); diff --git a/jstests/serverless/native_tenant_data_isolation_kill_op.js b/jstests/serverless/native_tenant_data_isolation_kill_op.js new file mode 100644 index 00000000000..445a19a15d6 --- /dev/null +++ b/jstests/serverless/native_tenant_data_isolation_kill_op.js @@ -0,0 +1,107 @@ +// Test that we can create and auth a tenant (user) using a security token. +// Then create an op (insert) for that tenant, find it using `currentOp` and kill it using `killOp`. + +(function() { +"use strict"; + +load('jstests/aggregation/extras/utils.js'); // For arrayEq() +load("jstests/libs/fail_point_util.js"); // For configureFailPoint() +load("jstests/libs/parallel_shell_helpers.js"); // For funWithArgs() + +function killCurrentOpTest() { + function operationToKillFunc(securityToken, dbName, colName) { + db.getMongo()._setSecurityToken(securityToken); + const insertCmdObj = {insert: colName, documents: [{_id: 0}]}; + assert.commandWorked(db.getSiblingDB(dbName).runCommand(insertCmdObj)); + } + + const rst = new ReplSetTest({ + nodes: 3, + nodeOptions: + {auth: '', setParameter: {multitenancySupport: true, featureFlagSecurityToken: true}} + }); + rst.startSet({keyFile: 'jstests/libs/key1'}); + rst.initiate(); + + const primary = rst.getPrimary(); + const adminDb = primary.getDB('admin'); + + // Prepare a user for testing pass tenant via $tenant. + // Must be authenticated as a user with ActionType::useTenant in order to use $tenant. + assert.commandWorked(adminDb.runCommand({createUser: 'admin', pwd: 'pwd', roles: ['root']})); + assert(adminDb.auth('admin', 'pwd')); + + const kTenant = ObjectId(); + const kOtherTenant = ObjectId(); + const kDbName = 'myDb'; + const kCollName = "currOpColl"; + + // Create a user for kTenant and its security token. + const securityToken = + _createSecurityToken({user: "userTenant1", db: '$external', tenant: kTenant}); + assert.commandWorked(primary.getDB('$external').runCommand({ + createUser: "userTenant1", + '$tenant': kTenant, + roles: + [{role: 'dbAdminAnyDatabase', db: 'admin'}, {role: 'readWriteAnyDatabase', db: 'admin'}] + })); + + // Create a different tenant to test that one tenant can't see or kill other tenant's op. + const securityTokenOtherTenant = + _createSecurityToken({user: "userTenant2", db: '$external', tenant: kOtherTenant}); + assert.commandWorked(primary.getDB('$external').runCommand({ + createUser: "userTenant2", + '$tenant': kOtherTenant, + roles: + [{role: 'dbAdminAnyDatabase', db: 'admin'}, {role: 'readWriteAnyDatabase', db: 'admin'}] + })); + + const tokenConn = new Mongo(primary.host); + tokenConn._setSecurityToken(securityToken); + + // test that the current tenant can see and kill his current op. + { + const findCurrentOpCmd = { + aggregate: 1, + pipeline: [{$currentOp: {allUsers: false}}, {$match: {op: "insert"}}], + cursor: {} + }; + + const createCollFP = configureFailPoint(primary, "hangBeforeLoggingCreateCollection"); + + const parallelShell = startParallelShell( + funWithArgs(operationToKillFunc, securityToken, kDbName, kCollName), primary.port); + + createCollFP.wait(); + + // find the current insert op that's being blocked due to the failpoint. + let findCurrentOpRes = tokenConn.getDB("admin").runCommand(findCurrentOpCmd); + assert.eq(findCurrentOpRes.cursor.firstBatch.length, 1, tojson(findCurrentOpRes)); + const opIdToKill = findCurrentOpRes.cursor.firstBatch[0].opid; + + // Try to kill the op with a different tenant / security token. Fails due to Unauthorized. + tokenConn._setSecurityToken(securityTokenOtherTenant); + assert.commandFailedWithCode( + tokenConn.getDB("admin").runCommand({killOp: 1, op: opIdToKill}), + ErrorCodes.Unauthorized); + + // Try to kill the op with a the same tenant / security token. Succeeds. + tokenConn._setSecurityToken(securityToken); + assert.commandWorked(tokenConn.getDB("admin").runCommand({killOp: 1, op: opIdToKill})); + + createCollFP.off(); + + // the current op was killed therefor the thread will throw an exception and wil return + // code 252. + const exitCode = parallelShell({checkExitSuccess: false}); + assert.neq(0, exitCode, "Expected shell to exit with failure due to operation kill"); + + // we should no longer have an operation for that tenant. + findCurrentOpRes = tokenConn.getDB("admin").runCommand(findCurrentOpCmd); + assert.eq(findCurrentOpRes.cursor.firstBatch.length, 0, tojson(findCurrentOpRes)); + } + + rst.stopSet(); +} +killCurrentOpTest(); +})(); diff --git a/src/mongo/db/commands/kill_op.cpp b/src/mongo/db/commands/kill_op.cpp index 633b10ef325..b31f45242ff 100644 --- a/src/mongo/db/commands/kill_op.cpp +++ b/src/mongo/db/commands/kill_op.cpp @@ -67,6 +67,10 @@ public: // killOp always reports success once past the auth check. return true; } + + bool allowedWithSecurityToken() const final { + return true; + } } killOpCmd; } // namespace mongo |