diff options
author | Cheahuychou Mao <mao.cheahuychou@gmail.com> | 2020-11-23 06:40:22 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-01-05 00:39:04 +0000 |
commit | fee631bc61d2fc85bd727967cfb4f2f243395461 (patch) | |
tree | e7ef493d74477b00b93f242e6ea973877d1a4306 /jstests | |
parent | e2f5ada302c5541dc7af96391583e327820a5f5e (diff) | |
download | mongo-fee631bc61d2fc85bd727967cfb4f2f243395461.tar.gz |
SERVER-52708 Make tenant migration donor take certificate as string in donorStartMigration command
Diffstat (limited to 'jstests')
-rw-r--r-- | jstests/replsets/libs/tenant_migration_test.js | 14 | ||||
-rw-r--r-- | jstests/replsets/libs/tenant_migration_util.js | 42 | ||||
-rw-r--r-- | jstests/replsets/tenant_migration_filters_tenant_id.js | 2 | ||||
-rw-r--r-- | jstests/replsets/tenant_migration_invalid_inputs.js | 14 | ||||
-rw-r--r-- | jstests/replsets/tenant_migration_logs_and_profiling.js | 98 | ||||
-rw-r--r-- | jstests/replsets/tenant_migration_no_failover.js | 2 | ||||
-rw-r--r-- | jstests/replsets/tenant_migration_x509.js | 226 |
7 files changed, 386 insertions, 12 deletions
diff --git a/jstests/replsets/libs/tenant_migration_test.js b/jstests/replsets/libs/tenant_migration_test.js index a17c39867a4..64b6d839faa 100644 --- a/jstests/replsets/libs/tenant_migration_test.js +++ b/jstests/replsets/libs/tenant_migration_test.js @@ -5,8 +5,7 @@ "use strict"; load("jstests/aggregation/extras/utils.js"); -load("jstests/replsets/rslib.js"); -load('jstests/libs/fail_point_util.js'); +load("jstests/replsets/libs/tenant_migration_util.js"); /** * This fixture allows the user to optionally pass in a custom ReplSetTest for the donor and @@ -24,6 +23,8 @@ function TenantMigrationTest( const donorPassedIn = (donorRst !== undefined); const recipientPassedIn = (recipientRst !== undefined); + const migrationCertificates = TenantMigrationUtil.makeMigrationCertificatesForTest(); + donorRst = donorPassedIn ? donorRst : performSetUp(true /* isDonor */); recipientRst = recipientPassedIn ? recipientRst : performSetUp(false /* isDonor */); @@ -153,6 +154,8 @@ function TenantMigrationTest( tenantId, recipientConnectionString = recipientRst.getURL(), readPreference = {mode: "primary"}, + donorCertificateForRecipient = migrationCertificates.donorCertificateForRecipient, + recipientCertificateForDonor = migrationCertificates.recipientCertificateForDonor, }, waitForMigrationToComplete, retryOnRetryableErrors) { @@ -162,6 +165,8 @@ function TenantMigrationTest( migrationId: UUID(migrationIdString), recipientConnectionString, readPreference, + donorCertificateForRecipient, + recipientCertificateForDonor }; let donorPrimary = this.getDonorPrimary(); @@ -345,8 +350,9 @@ function TenantMigrationTest( /** * Verifies that the documents on the recipient primary are correct. */ - this.verifyReceipientDB = function(tenantId, dbName, collName, data = loadDummyData()) { - const shouldMigrate = this.isNamespaceForTenant(tenantId, dbName); + this.verifyRecipientDB = function( + tenantId, dbName, collName, migrationCommitted = true, data = loadDummyData()) { + const shouldMigrate = migrationCommitted && this.isNamespaceForTenant(tenantId, dbName); jsTestLog(`Verifying that data in collection ${collName} of DB ${dbName} was ${ (shouldMigrate ? "" : "not")} migrated to the recipient`); diff --git a/jstests/replsets/libs/tenant_migration_util.js b/jstests/replsets/libs/tenant_migration_util.js index e6d38691516..c4e52e26ca2 100644 --- a/jstests/replsets/libs/tenant_migration_util.js +++ b/jstests/replsets/libs/tenant_migration_util.js @@ -2,8 +2,6 @@ * Utilities for testing tenant migrations. */ var TenantMigrationUtil = (function() { - load("jstests/replsets/libs/tenant_migration_test.js"); - /** * Returns whether tenant migration commands are supported. */ @@ -14,6 +12,30 @@ var TenantMigrationUtil = (function() { } /** + * Returns an object containing the certificate and private key extracted from the given + * pem file. + */ + function getCertificateAndPrivateKey(pemFile) { + const lines = cat(pemFile); + const certificate = + lines.match(new RegExp("-*BEGIN CERTIFICATE-*\\n(.*\\n)*-*END CERTIFICATE-*\\n"))[0]; + const privateKey = + lines.match(new RegExp("-*BEGIN PRIVATE KEY-*\\n(.*\\n)*-*END PRIVATE KEY-*\\n"))[0]; + return {certificate, privateKey}; + } + + /** + * Returns an object containing the donor and recipient's certificate and private key for + * tenant migration testing. + */ + function makeMigrationCertificatesForTest() { + return { + donorCertificateForRecipient: getCertificateAndPrivateKey("jstests/libs/client.pem"), + recipientCertificateForDonor: getCertificateAndPrivateKey("jstests/libs/client.pem") + }; + } + + /** * Runs the donorStartMigration command with the given migration options * until the migration commits or aborts, or until the command fails. Returns the last command * response. @@ -26,12 +48,19 @@ var TenantMigrationUtil = (function() { * fixture. */ function runMigrationAsync(migrationOpts, donorRstArgs, retryOnRetryableErrors = false) { + load("jstests/replsets/libs/tenant_migration_util.js"); + + const migrationCertificates = TenantMigrationUtil.makeMigrationCertificatesForTest(); const cmdObj = { donorStartMigration: 1, migrationId: UUID(migrationOpts.migrationIdString), recipientConnectionString: migrationOpts.recipientConnString, tenantId: migrationOpts.tenantId, readPreference: migrationOpts.readPreference || {mode: "primary"}, + donorCertificateForRecipient: migrationOpts.donorCertificateForRecipient || + migrationCertificates.donorCertificateForRecipient, + recipientCertificateForDonor: migrationOpts.recipientCertificateForDonor || + migrationCertificates.recipientCertificateForDonor }; const donorRst = new ReplSetTest({rstArgs: donorRstArgs}); @@ -118,5 +147,12 @@ var TenantMigrationUtil = (function() { return donorRstArgs; } - return {runMigrationAsync, forgetMigrationAsync, createRstArgs, isFeatureFlagEnabled}; + return { + runMigrationAsync, + forgetMigrationAsync, + createRstArgs, + isFeatureFlagEnabled, + getCertificateAndPrivateKey, + makeMigrationCertificatesForTest, + }; })(); diff --git a/jstests/replsets/tenant_migration_filters_tenant_id.js b/jstests/replsets/tenant_migration_filters_tenant_id.js index 41c9ca9074e..a17ef1e7f7f 100644 --- a/jstests/replsets/tenant_migration_filters_tenant_id.js +++ b/jstests/replsets/tenant_migration_filters_tenant_id.js @@ -40,7 +40,7 @@ const runTest = (baseTenantId, dbName, shouldMatch) => { // verifyRecipientDB calls isNamespaceForTenant() to determine if the data should have been // migrated, so we can directly call it here. - tenantMigrationTest.verifyReceipientDB(baseTenantId, dbName, collName); + tenantMigrationTest.verifyRecipientDB(baseTenantId, dbName, collName); tenantMigrationTest.forgetMigration(migrationOpts.migrationIdString); }; diff --git a/jstests/replsets/tenant_migration_invalid_inputs.js b/jstests/replsets/tenant_migration_invalid_inputs.js index 59e7e8c4f25..31745b68cfb 100644 --- a/jstests/replsets/tenant_migration_invalid_inputs.js +++ b/jstests/replsets/tenant_migration_invalid_inputs.js @@ -10,6 +10,7 @@ (function() { "use strict"; +load("jstests/replsets/libs/tenant_migration_test.js"); load("jstests/replsets/libs/tenant_migration_util.js"); const tenantMigrationTest = @@ -27,6 +28,7 @@ const tenantId = "testTenantId"; const readPreference = { mode: 'primary' }; +const migrationCertificates = TenantMigrationUtil.makeMigrationCertificatesForTest(); jsTestLog("Testing 'donorStartMigration' command provided with invalid options."); @@ -38,7 +40,9 @@ unsupportedtenantIds.forEach((invalidTenantId) => { migrationId: UUID(), recipientConnectionString: tenantMigrationTest.getRecipientRst().getURL(), tenantId: invalidTenantId, - readPreference: readPreference + readPreference: readPreference, + donorCertificateForRecipient: migrationCertificates.donorCertificateForRecipient, + recipientCertificateForDonor: migrationCertificates.recipientCertificateForDonor, }), ErrorCodes.BadValue); }); @@ -49,7 +53,9 @@ assert.commandFailedWithCode(donorPrimary.adminCommand({ migrationId: UUID(), recipientConnectionString: tenantMigrationTest.getDonorRst().getURL(), tenantId: tenantId, - readPreference: readPreference + readPreference: readPreference, + donorCertificateForRecipient: migrationCertificates.donorCertificateForRecipient, + recipientCertificateForDonor: migrationCertificates.recipientCertificateForDonor, }), ErrorCodes.BadValue); @@ -70,7 +76,9 @@ assert.commandFailedWithCode(donorPrimary.adminCommand({ migrationId: UUID(), recipientConnectionString: recipientPrimary.host, tenantId: tenantId, - readPreference: readPreference + readPreference: readPreference, + donorCertificateForRecipient: migrationCertificates.donorCertificateForRecipient, + recipientCertificateForDonor: migrationCertificates.recipientCertificateForDonor, }), ErrorCodes.BadValue); diff --git a/jstests/replsets/tenant_migration_logs_and_profiling.js b/jstests/replsets/tenant_migration_logs_and_profiling.js new file mode 100644 index 00000000000..470d916d255 --- /dev/null +++ b/jstests/replsets/tenant_migration_logs_and_profiling.js @@ -0,0 +1,98 @@ +/** + * Tests that migration certificates do not show up in the logs or system.profile collections. + * + * @tags: [requires_fcv_47, requires_majority_read_concern, incompatible_with_eft] + */ + +(function() { +"use strict"; + +load("jstests/replsets/libs/tenant_migration_test.js"); +load("jstests/libs/uuid_util.js"); + +function assertNoCertificateOrPrivateKeyLogsForCmd(conn, cmdName) { + assert(checkLog.checkContainsOnce(conn, new RegExp(`Slow query.*${cmdName}`)), + "did not find slow query logs for the command"); + assert(!checkLog.checkContainsOnce(conn, /BEGIN CERTIFICATE.*END CERTIFICATE/), + "found certificate in the logs"); + assert(!checkLog.checkContainsOnce(conn, /BEGIN PRIVATE KEY.*END PRIVATE KEY/), + "found private key in the logs"); +} + +function assertNoCertificateOrPrivateKeyFields(doc) { + for (let k of Object.keys(doc)) { + let v = doc[k]; + if (typeof v === "string") { + assert(!v.match(/BEGIN CERTIFICATE(.*\n.*)*END CERTIFICATE/m), + `found certificate field`); + assert(!v.match(/BEGIN PRIVATE KEY(.*\n.*)*END PRIVATE KEY/m), + `found private key field`); + } else if (Array.isArray(v)) { + v.forEach((item) => { + if (typeof item === "object" && item !== null) { + assertNoCertificateOrPrivateKeyFields(v); + } + }); + } else if (typeof v === "object" && v !== null) { + assertNoCertificateOrPrivateKeyFields(v); + } + } +} + +const tenantMigrationTest = new TenantMigrationTest({name: jsTestName()}); +if (!tenantMigrationTest.isFeatureFlagEnabled()) { + jsTestLog("Skipping test because the tenant migrations feature flag is disabled"); + return; +} + +const donorPrimary = tenantMigrationTest.getDonorPrimary(); + +// Verify that migration certificates are not logged as part of slow query logging. +(() => { + const donorDefaultSlowMs = + assert.commandWorked(donorPrimary.adminCommand({profile: 0, slowms: 0})).slowms; + + const migrationOpts = { + migrationIdString: extractUUIDFromObject(UUID()), + tenantId: "slowCommands", + }; + + const stateRes = assert.commandWorked(tenantMigrationTest.runMigration(migrationOpts)); + assert.eq(stateRes.state, TenantMigrationTest.State.kCommitted); + + assertNoCertificateOrPrivateKeyLogsForCmd(donorPrimary, "donorStartMigration"); + + assert.commandWorked(tenantMigrationTest.forgetMigration(migrationOpts.migrationIdString)); + + assertNoCertificateOrPrivateKeyLogsForCmd(donorPrimary, "donorForgetMigration"); + + assert.commandWorked(donorPrimary.adminCommand({profile: 0, slowms: donorDefaultSlowMs})); +})(); + +// Verify that migration certificates do not show up in system.profile collections. +(() => { + const donorAdminDB = donorPrimary.getDB("config"); + + donorAdminDB.setProfilingLevel(2); + + const migrationOpts = { + migrationIdString: extractUUIDFromObject(UUID()), + tenantId: "profiler", + }; + + const stateRes = assert.commandWorked(tenantMigrationTest.runMigration(migrationOpts)); + assert.eq(stateRes.state, TenantMigrationTest.State.kCommitted); + + donorAdminDB.system.profile.find({ns: TenantMigrationTest.kConfigDonorsNS}).forEach(doc => { + assertNoCertificateOrPrivateKeyFields(doc); + }); + + assert.commandWorked(tenantMigrationTest.forgetMigration(migrationOpts.migrationIdString)); + + donorAdminDB.system.profile.find({ns: TenantMigrationTest.kConfigDonorsNS}).forEach(doc => { + assertNoCertificateOrPrivateKeyFields(doc); + }); +})(); + +tenantMigrationTest.stop(); +})(); diff --git a/jstests/replsets/tenant_migration_no_failover.js b/jstests/replsets/tenant_migration_no_failover.js index 0cd54d018cb..73c7be69339 100644 --- a/jstests/replsets/tenant_migration_no_failover.js +++ b/jstests/replsets/tenant_migration_no_failover.js @@ -40,7 +40,7 @@ assert.eq(stateRes.state, TenantMigrationTest.State.kCommitted); for (const db of [...tenantDBs, ...nonTenantDBs]) { for (const coll of collNames) { - tenantMigrationTest.verifyReceipientDB(tenantId, db, coll); + tenantMigrationTest.verifyRecipientDB(tenantId, db, coll); } } diff --git a/jstests/replsets/tenant_migration_x509.js b/jstests/replsets/tenant_migration_x509.js new file mode 100644 index 00000000000..9645b476cef --- /dev/null +++ b/jstests/replsets/tenant_migration_x509.js @@ -0,0 +1,226 @@ +/** + * Tests that the tenant migration donor authenticates as client to recipient using the + * migration-specific x.509 certificate, and vice versa. + * + * @tags: [requires_fcv_47, requires_majority_read_concern, incompatible_with_eft] + */ + +(function() { +"use strict"; + +load("jstests/replsets/libs/tenant_migration_test.js"); +load("jstests/replsets/libs/tenant_migration_util.js"); +load("jstests/libs/uuid_util.js"); + +function makeTestNs(tenantId) { + return {dbName: tenantId + "_testDb", collName: "testColl"}; +} + +const tenantMigrationTest = new TenantMigrationTest({name: jsTestName()}); +if (!tenantMigrationTest.isFeatureFlagEnabled()) { + jsTestLog("Skipping test because the tenant migrations feature flag is disabled"); + return; +} + +const kDonorCertificateAndPrivateKey = + TenantMigrationUtil.getCertificateAndPrivateKey("jstests/libs/client.pem"); +const kRecipientCertificateAndPrivateKey = + TenantMigrationUtil.getCertificateAndPrivateKey("jstests/libs/client.pem"); + +(() => { + jsTest.log("Test valid donor and recipient certificates"); + const migrationId = UUID(); + const tenantId = "validCertificates"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: kDonorCertificateAndPrivateKey, + recipientCertificateForDonor: kRecipientCertificateAndPrivateKey, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + const stateRes = assert.commandWorked(tenantMigrationTest.runMigration(migrationOpts)); + assert.eq(stateRes.state, TenantMigrationTest.State.kCommitted); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, true /* migrationCommitted */); +})(); + +(() => { + jsTest.log("Test invalid donor certificate, no header and trailer"); + const migrationId = UUID(); + const tenantId = "invalidDonorCertificateNoHeaderAndTrailer"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: { + certificate: "invalidCertificate", + privateKey: kDonorCertificateAndPrivateKey.privateKey + }, + recipientCertificateForDonor: kRecipientCertificateAndPrivateKey, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + assert.commandFailedWithCode(tenantMigrationTest.runMigration(migrationOpts), + ErrorCodes.InvalidSSLConfiguration); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, false /* migrationCommitted */); +})(); + +(() => { + jsTest.log("Test invalid donor certificate, use private key as certificate"); + const migrationId = UUID(); + const tenantId = "invalidDonorCertificatePrivateKeyAsCertificate"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: { + certificate: kDonorCertificateAndPrivateKey.privateKey, + privateKey: kDonorCertificateAndPrivateKey.privateKey + }, + recipientCertificateForDonor: kRecipientCertificateAndPrivateKey, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + assert.commandFailedWithCode(tenantMigrationTest.runMigration(migrationOpts), + ErrorCodes.InvalidSSLConfiguration); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, false /* migrationCommitted */); +})(); + +(() => { + jsTest.log("Test invalid donor private key, no header and trailer"); + const migrationId = UUID(); + const tenantId = "invalidDonorPrivateKeyNoHeaderAndTrailer"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: { + certificate: kDonorCertificateAndPrivateKey.certificate, + privateKey: "invalidCertificate" + }, + recipientCertificateForDonor: kRecipientCertificateAndPrivateKey, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + assert.commandFailedWithCode(tenantMigrationTest.runMigration(migrationOpts), + ErrorCodes.InvalidSSLConfiguration); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, false /* migrationCommitted */); +})(); + +(() => { + jsTest.log("Test invalid donor private key, use certificate as private key"); + const migrationId = UUID(); + const tenantId = "invalidDonorPrivateKeyCertificateAsPrivateKey"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: { + certificate: kDonorCertificateAndPrivateKey.certificate, + privateKey: kDonorCertificateAndPrivateKey.certificate + }, + recipientCertificateForDonor: kRecipientCertificateAndPrivateKey, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + assert.commandFailedWithCode(tenantMigrationTest.runMigration(migrationOpts), + ErrorCodes.InvalidSSLConfiguration); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, false /* migrationCommitted */); +})(); + +(() => { + jsTest.log("Test invalid recipient certificate, no header and trailer"); + const migrationId = UUID(); + const tenantId = "invalidRecipientCertificateNoHeaderAndTrailer"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: kDonorCertificateAndPrivateKey, + recipientCertificateForDonor: { + certificate: "invalidCertificate", + privateKey: kRecipientCertificateAndPrivateKey.privateKey + }, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + assert.commandFailedWithCode(tenantMigrationTest.runMigration(migrationOpts), + ErrorCodes.InvalidSSLConfiguration); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, false /* migrationCommitted */); +})(); + +(() => { + jsTest.log("Test invalid recipient certificate, use private key as certificate"); + const migrationId = UUID(); + const tenantId = "invalidRecipientCertificatePrivateKeyAsCertificate"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: kDonorCertificateAndPrivateKey, + recipientCertificateForDonor: { + certificate: kRecipientCertificateAndPrivateKey.privateKey, + privateKey: kRecipientCertificateAndPrivateKey.privateKey + }, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + assert.commandFailedWithCode(tenantMigrationTest.runMigration(migrationOpts), + ErrorCodes.InvalidSSLConfiguration); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, false /* migrationCommitted */); +})(); + +(() => { + jsTest.log("Test invalid recipient private key, no header and trailer"); + const migrationId = UUID(); + const tenantId = "invalidRecipientPrivateKeyNoHeaderAndTrailer"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: kDonorCertificateAndPrivateKey, + recipientCertificateForDonor: { + certificate: kRecipientCertificateAndPrivateKey.certificate, + privateKey: "invalidCertificate" + }, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + assert.commandFailedWithCode(tenantMigrationTest.runMigration(migrationOpts), + ErrorCodes.InvalidSSLConfiguration); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, false /* migrationCommitted */); +})(); + +(() => { + jsTest.log("Test invalid recipient private key, use certificate as private key"); + const migrationId = UUID(); + const tenantId = "invalidRecipientPrivateKeyCertificateAsPrivateKey"; + const migrationOpts = { + migrationIdString: extractUUIDFromObject(migrationId), + tenantId: tenantId, + donorCertificateForRecipient: kDonorCertificateAndPrivateKey, + recipientCertificateForDonor: { + certificate: kRecipientCertificateAndPrivateKey.certificate, + privateKey: kRecipientCertificateAndPrivateKey.certificate + }, + }; + const {dbName, collName} = makeTestNs(tenantId); + + tenantMigrationTest.insertDonorDB(dbName, collName); + assert.commandFailedWithCode(tenantMigrationTest.runMigration(migrationOpts), + ErrorCodes.InvalidSSLConfiguration); + tenantMigrationTest.verifyRecipientDB( + tenantId, dbName, collName, false /* migrationCommitted */); +})(); + +tenantMigrationTest.stop(); +})(); |