diff options
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/audit.h | 2 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_commands.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/commands.cpp | 24 | ||||
-rw-r--r-- | src/mongo/db/commands.h | 4 | ||||
-rw-r--r-- | src/mongo/db/commands/tenant_migration_donor_cmds.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/commands/tenant_migration_donor_cmds.idl | 10 | ||||
-rw-r--r-- | src/mongo/db/commands/user_management_commands.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/repl/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/repl/tenant_migration_donor_service.cpp | 66 | ||||
-rw-r--r-- | src/mongo/db/repl/tenant_migration_pem_payload.idl | 50 | ||||
-rw-r--r-- | src/mongo/db/repl/tenant_migration_state_machine.idl | 11 | ||||
-rw-r--r-- | src/mongo/db/repl/tenant_migration_util.h | 13 |
12 files changed, 151 insertions, 60 deletions
diff --git a/src/mongo/db/audit.h b/src/mongo/db/audit.h index 364ac43cae7..c01619ed580 100644 --- a/src/mongo/db/audit.h +++ b/src/mongo/db/audit.h @@ -62,7 +62,7 @@ namespace audit { class CommandInterface { public: virtual ~CommandInterface() = default; - virtual StringData sensitiveFieldName() const = 0; + virtual std::set<StringData> sensitiveFieldNames() const = 0; virtual void snipForLogging(mutablebson::Document* cmdObj) const = 0; virtual StringData getName() const = 0; virtual NamespaceString ns() const = 0; diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp index f0b3783cb1f..e5e00a6b160 100644 --- a/src/mongo/db/auth/sasl_commands.cpp +++ b/src/mongo/db/auth/sasl_commands.cpp @@ -67,6 +67,8 @@ const bool autoAuthorizeDefault = true; class CmdSaslStart : public BasicCommand { public: + static constexpr StringData kPayloadField = "payload"_sd; + CmdSaslStart(); virtual ~CmdSaslStart(); @@ -78,8 +80,8 @@ public: const BSONObj&, std::vector<Privilege>*) const {} - StringData sensitiveFieldName() const final { - return "payload"_sd; + std::set<StringData> sensitiveFieldNames() const final { + return {kPayloadField}; } virtual bool run(OperationContext* opCtx, @@ -101,6 +103,8 @@ public: class CmdSaslContinue : public BasicCommand { public: + static constexpr StringData kPayloadField = "payload"_sd; + CmdSaslContinue(); virtual ~CmdSaslContinue(); @@ -111,8 +115,8 @@ public: const BSONObj&, std::vector<Privilege>*) const {} - StringData sensitiveFieldName() const final { - return "payload"_sd; + std::set<StringData> sensitiveFieldNames() const final { + return {kPayloadField}; } virtual bool run(OperationContext* opCtx, diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 025def0f609..6b279d1ebfe 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -214,11 +214,11 @@ void CommandHelpers::auditLogAuthEvent(OperationContext* opCtx, } } - StringData sensitiveFieldName() const override { + std::set<StringData> sensitiveFieldNames() const override { if (_invocation) { - return _invocation->definition()->sensitiveFieldName(); + return _invocation->definition()->sensitiveFieldNames(); } - return StringData{}; + return {}; } StringData getName() const override { @@ -898,15 +898,15 @@ private: Command::~Command() = default; void Command::snipForLogging(mutablebson::Document* cmdObj) const { - StringData sensitiveField = sensitiveFieldName(); - if (!sensitiveField.empty()) { - - for (mutablebson::Element pwdElement = - mutablebson::findFirstChildNamed(cmdObj->root(), sensitiveField); - pwdElement.ok(); - pwdElement = - mutablebson::findElementNamed(pwdElement.rightSibling(), sensitiveField)) { - uassertStatusOK(pwdElement.setValueString("xxx")); + auto sensitiveFields = sensitiveFieldNames(); + if (!sensitiveFields.empty()) { + for (auto& sensitiveField : sensitiveFields) { + for (mutablebson::Element element = + mutablebson::findFirstChildNamed(cmdObj->root(), sensitiveField); + element.ok(); + element = mutablebson::findElementNamed(element.rightSibling(), sensitiveField)) { + uassertStatusOK(element.setValueString("xxx")); + } } } } diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 140d7bccee4..dfb4cdba633 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -484,8 +484,8 @@ public: * The default snipForLogging shall remove these field names. Auditing shall not * include these fields in audit outputs. */ - virtual StringData sensitiveFieldName() const { - return StringData{}; + virtual std::set<StringData> sensitiveFieldNames() const { + return {}; } /** diff --git a/src/mongo/db/commands/tenant_migration_donor_cmds.cpp b/src/mongo/db/commands/tenant_migration_donor_cmds.cpp index a4a838277b3..eacf88bde84 100644 --- a/src/mongo/db/commands/tenant_migration_donor_cmds.cpp +++ b/src/mongo/db/commands/tenant_migration_donor_cmds.cpp @@ -44,6 +44,11 @@ public: using Request = DonorStartMigration; using Response = DonorStartMigrationResponse; + std::set<StringData> sensitiveFieldNames() const final { + return {Request::kDonorCertificateForRecipientFieldName, + Request::kRecipientCertificateForDonorFieldName}; + } + class Invocation : public InvocationBase { public: @@ -61,7 +66,9 @@ public: TenantMigrationDonorDocument(requestBody.getMigrationId(), requestBody.getRecipientConnectionString().toString(), requestBody.getReadPreference(), - requestBody.getTenantId().toString()) + requestBody.getTenantId().toString(), + requestBody.getDonorCertificateForRecipient(), + requestBody.getRecipientCertificateForDonor()) .toBSON(); auto donorService = diff --git a/src/mongo/db/commands/tenant_migration_donor_cmds.idl b/src/mongo/db/commands/tenant_migration_donor_cmds.idl index c26b4848b26..ed211a408a4 100644 --- a/src/mongo/db/commands/tenant_migration_donor_cmds.idl +++ b/src/mongo/db/commands/tenant_migration_donor_cmds.idl @@ -74,6 +74,16 @@ commands: readPreference: description: "The read preference settings that the donor will pass on to the recipient." type: readPreference + donorCertificateForRecipient: + description: >- + The SSL certificate and private key that the donor should use to authenticate to the + recipient. + type: TenantMigrationPEMPayload + recipientCertificateForDonor: + description: >- + The SSL certificate and private key that the recipient should use to authenticate to the + donor. + type: TenantMigrationPEMPayload donorForgetMigration: description: "Parser for the 'donorForgetMigration' command." diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp index c19b3ab9efc..f88e75fc929 100644 --- a/src/mongo/db/commands/user_management_commands.cpp +++ b/src/mongo/db/commands/user_management_commands.cpp @@ -968,8 +968,8 @@ class CmdCreateUser : public CmdUMCTyped<CreateUserCommand, void> { public: static constexpr StringData kPwdField = "pwd"_sd; - StringData sensitiveFieldName() const final { - return kPwdField; + std::set<StringData> sensitiveFieldNames() const final { + return {kPwdField}; } } cmdCreateUser; @@ -1077,8 +1077,8 @@ class CmdUpdateUser : public CmdUMCTyped<UpdateUserCommand, void> { public: static constexpr StringData kPwdField = "pwd"_sd; - StringData sensitiveFieldName() const final { - return kPwdField; + std::set<StringData> sensitiveFieldNames() const final { + return {kPwdField}; } } cmdUpdateUser; diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index e3e5c2a4e11..7b9492f4ebf 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -1269,6 +1269,7 @@ env.Library( env.Library( target='tenant_migration_state_machine_idl', source=[ + 'tenant_migration_pem_payload.idl', 'tenant_migration_state_machine.idl', ], LIBDEPS=[ @@ -1276,6 +1277,7 @@ env.Library( '$BUILD_DIR/mongo/client/connection_string', '$BUILD_DIR/mongo/client/read_preference', '$BUILD_DIR/mongo/idl/idl_parser', + '$BUILD_DIR/mongo/util/net/ssl_manager', 'optime', 'repl_coordinator_interface', ], diff --git a/src/mongo/db/repl/tenant_migration_donor_service.cpp b/src/mongo/db/repl/tenant_migration_donor_service.cpp index 039d622f908..981fe7aada8 100644 --- a/src/mongo/db/repl/tenant_migration_donor_service.cpp +++ b/src/mongo/db/repl/tenant_migration_donor_service.cpp @@ -221,24 +221,21 @@ ExecutorFuture<repl::OpTime> TenantMigrationDonorService::Instance::_insertState return AsyncTry([this, self = shared_from_this()] { auto opCtxHolder = cc().makeOperationContext(); auto opCtx = opCtxHolder.get(); - DBDirectClient client(opCtx); - auto commandResponse = client.runCommand([&] { - write_ops::Update updateOp(_stateDocumentsNS); - auto updateModification = - write_ops::UpdateModification::parseFromClassicUpdate(_stateDoc.toBSON()); - write_ops::UpdateOpEntry updateEntry( - BSON(TenantMigrationDonorDocument::kIdFieldName << _stateDoc.getId()), - updateModification); - updateEntry.setMulti(false); - updateEntry.setUpsert(true); - updateOp.setUpdates({updateEntry}); + AutoGetCollection collection(opCtx, _stateDocumentsNS, MODE_IX); - return updateOp.serialize({}); - }()); + writeConflictRetry( + opCtx, "tenantMigrationInsertStateDoc", _stateDocumentsNS.ns(), [&] { + const auto filter = + BSON(TenantMigrationDonorDocument::kIdFieldName << _stateDoc.getId()); + const auto updateMod = BSON("$setOnInsert" << _stateDoc.toBSON()); + auto updateResult = Helpers::upsert( + opCtx, _stateDocumentsNS.ns(), filter, updateMod, /*fromMigrate=*/false); - const auto commandReply = commandResponse->getCommandReply(); - uassertStatusOK(getStatusFromWriteCommandReply(commandReply)); + // '$setOnInsert' update operator can never modify an existing on-disk state + // doc. + invariant(!updateResult.numDocsModified); + }); return repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(); }) @@ -348,31 +345,28 @@ ExecutorFuture<repl::OpTime> TenantMigrationDonorService::Instance::_updateState ExecutorFuture<repl::OpTime> TenantMigrationDonorService::Instance::_markStateDocumentAsGarbageCollectable( std::shared_ptr<executor::ScopedTaskExecutor> executor, const CancelationToken& token) { + _stateDoc.setExpireAt(_serviceContext->getFastClockSource()->now() + + Milliseconds{repl::tenantMigrationGarbageCollectionDelayMS.load()}); + return AsyncTry([this, self = shared_from_this()] { auto opCtxHolder = cc().makeOperationContext(); auto opCtx = opCtxHolder.get(); - DBDirectClient client(opCtx); - _stateDoc.setExpireAt( - _serviceContext->getFastClockSource()->now() + - Milliseconds{repl::tenantMigrationGarbageCollectionDelayMS.load()}); - - auto commandResponse = client.runCommand([&] { - write_ops::Update updateOp(_stateDocumentsNS); - auto updateModification = - write_ops::UpdateModification::parseFromClassicUpdate(_stateDoc.toBSON()); - write_ops::UpdateOpEntry updateEntry( - BSON(TenantMigrationDonorDocument::kIdFieldName << _stateDoc.getId()), - updateModification); - updateEntry.setMulti(false); - updateEntry.setUpsert(false); - updateOp.setUpdates({updateEntry}); - - return updateOp.serialize({}); - }()); - - const auto commandReply = commandResponse->getCommandReply(); - uassertStatusOK(getStatusFromWriteCommandReply(commandReply)); + AutoGetCollection collection(opCtx, _stateDocumentsNS, MODE_IX); + + writeConflictRetry( + opCtx, + "tenantMigrationDonorMarkStateDocAsGarbageCollectable", + _stateDocumentsNS.ns(), + [&] { + const auto filter = + BSON(TenantMigrationDonorDocument::kIdFieldName << _stateDoc.getId()); + const auto updateMod = _stateDoc.toBSON(); + auto updateResult = Helpers::upsert( + opCtx, _stateDocumentsNS.ns(), filter, updateMod, /*fromMigrate=*/false); + + invariant(updateResult.numDocsModified == 1); + }); return repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp(); }) diff --git a/src/mongo/db/repl/tenant_migration_pem_payload.idl b/src/mongo/db/repl/tenant_migration_pem_payload.idl new file mode 100644 index 00000000000..2e4aa666c62 --- /dev/null +++ b/src/mongo/db/repl/tenant_migration_pem_payload.idl @@ -0,0 +1,50 @@ +# Copyright (C) 2020-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# As a special exception, the copyright holders give permission to link the +# code of portions of this program with the OpenSSL library under certain +# conditions as described in each individual source file and distribute +# linked combinations including the program with the OpenSSL library. You +# must comply with the Server Side Public License in all respects for +# all of the code used other than as permitted herein. If you modify file(s) +# with this exception, you may extend this exception to your version of the +# file(s), but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. If you delete this +# exception statement from all source files in the program, then also delete +# it in the license file. +# +global: + cpp_namespace: "mongo" + cpp_includes: + - "mongo/db/repl/tenant_migration_util.h" + +imports: + - "mongo/idl/basic_types.idl" + +structs: + TenantMigrationPEMPayload: + description: "Contains SSL certificate and private key PEM blobs for a replica set." + strict: true + fields: + certificate: + type: string + description: "Certificate PEM blob." + validator: + callback: "validateCertificatePEMPayload" + privateKey: + type: string + description: "Private key PEM blob." + validator: + callback: "validatePrivateKeyPEMPayload" diff --git a/src/mongo/db/repl/tenant_migration_state_machine.idl b/src/mongo/db/repl/tenant_migration_state_machine.idl index 2f444f9f9f2..ea808921f2a 100644 --- a/src/mongo/db/repl/tenant_migration_state_machine.idl +++ b/src/mongo/db/repl/tenant_migration_state_machine.idl @@ -34,6 +34,7 @@ global: imports: - "mongo/client/read_preference_setting.idl" - "mongo/db/repl/replication_types.idl" + - "mongo/db/repl/tenant_migration_pem_payload.idl" - "mongo/idl/basic_types.idl" enums: @@ -106,6 +107,16 @@ structs: The wall-clock time at which the state machine document should be removed by the TTL monitor. optional: true + donorCertificateForRecipient: + description: >- + The SSL certificate and private key that the donor should use to authenticate + to the recipient. + type: TenantMigrationPEMPayload + recipientCertificateForDonor: + description: >- + The SSL certificate and private key that the recipient should use to + authenticate to the donor. + type: TenantMigrationPEMPayload tenantMigrationRecipientDocument: description: "Represents an in-progress tenant migration on the migration recipient." diff --git a/src/mongo/db/repl/tenant_migration_util.h b/src/mongo/db/repl/tenant_migration_util.h index 1e5d1a5000f..1fd136fecc4 100644 --- a/src/mongo/db/repl/tenant_migration_util.h +++ b/src/mongo/db/repl/tenant_migration_util.h @@ -34,6 +34,7 @@ #include "mongo/bson/timestamp.h" #include "mongo/client/mongo_uri.h" #include "mongo/db/repl/replication_coordinator.h" +#include "mongo/util/net/ssl_util.h" #include "mongo/util/str.h" namespace mongo { @@ -98,4 +99,16 @@ inline Status validateConnectionString(const StringData& donorOrRecipientConnect return Status::OK(); } +inline Status validateCertificatePEMPayload(const StringData& payload) { + auto swBlob = + ssl_util::findPEMBlob(payload, "CERTIFICATE"_sd, 0 /* position */, false /* allowEmpty */); + return swBlob.getStatus().withContext("Invalid certificate field"); +} + +inline Status validatePrivateKeyPEMPayload(const StringData& payload) { + auto swBlob = + ssl_util::findPEMBlob(payload, "PRIVATE KEY"_sd, 0 /* position */, false /* allowEmpty */); + return swBlob.getStatus().withContext("Invalid private key field"); +} + } // namespace mongo |