diff options
author | Varun Ravichandran <varun.ravichandran@mongodb.com> | 2023-03-29 05:44:21 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-04-04 07:05:50 +0000 |
commit | 39f7c9035ab26b15b95625e016da51be946374a2 (patch) | |
tree | 5039e540d07025fd6701f977c2411aa08c81e29d /src/mongo/util | |
parent | 11a5a961e255e9c07b1068bd702d57daa15ec33e (diff) | |
download | mongo-39f7c9035ab26b15b95625e016da51be946374a2.tar.gz |
SERVER-74989: Create configuration option to specify X.509 subject DN attributes for intracluster auth
Diffstat (limited to 'src/mongo/util')
-rw-r--r-- | src/mongo/util/net/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.cpp | 173 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager.h | 8 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager_openssl.cpp | 1 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_manager_test.cpp | 100 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_options.h | 1 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_options_server.cpp | 11 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_options_server.idl | 26 | ||||
-rw-r--r-- | src/mongo/util/net/ssl_types.h | 18 |
9 files changed, 256 insertions, 83 deletions
diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript index 09b609aadb3..3d217c36ed4 100644 --- a/src/mongo/util/net/SConscript +++ b/src/mongo/util/net/SConscript @@ -263,6 +263,7 @@ if get_option('ssl') == 'on': 'network', 'ssl_manager', 'ssl_options_server', + 'ssl_types', ], ) diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp index 74c2f2c8748..de5370235eb 100644 --- a/src/mongo/util/net/ssl_manager.cpp +++ b/src/mongo/util/net/ssl_manager.cpp @@ -52,6 +52,7 @@ #include "mongo/util/icu.h" #include "mongo/util/net/ssl_options.h" #include "mongo/util/net/ssl_parameters_gen.h" +#include "mongo/util/net/ssl_types.h" #include "mongo/util/str.h" #include "mongo/util/synchronized_value.h" #include "mongo/util/text.h" @@ -278,57 +279,24 @@ std::pair<std::string, RFC4514Parser::ValueTerminator> RFC4514Parser::extractVal const auto getTLSVersionCounts = ServiceContext::declareDecoration<TLSVersionCounts>(); - -void canonicalizeClusterDN(std::vector<std::string>* dn) { - // remove all RDNs we don't care about - for (size_t i = 0; i < dn->size(); i++) { - std::string& comp = dn->at(i); - boost::algorithm::trim(comp); - if (!str::startsWith(comp.c_str(), "DC=") && // - !str::startsWith(comp.c_str(), "O=") && // - !str::startsWith(comp.c_str(), "OU=")) { - dn->erase(dn->begin() + i); - i--; - } - } - std::stable_sort(dn->begin(), dn->end()); -} - constexpr StringData kOID_DC = "0.9.2342.19200300.100.1.25"_sd; constexpr StringData kOID_O = "2.5.4.10"_sd; constexpr StringData kOID_OU = "2.5.4.11"_sd; -std::vector<SSLX509Name::Entry> canonicalizeClusterDN( - const std::vector<std::vector<SSLX509Name::Entry>>& entries) { - std::vector<SSLX509Name::Entry> ret; - - for (const auto& rdn : entries) { - for (const auto& entry : rdn) { - if ((entry.oid != kOID_DC) && (entry.oid != kOID_O) && (entry.oid != kOID_OU)) { - continue; - } - ret.push_back(entry); - } - } - std::stable_sort(ret.begin(), ret.end()); - return ret; -} - -struct DNValue { - explicit DNValue(SSLX509Name dn) - : fullDN(std::move(dn)), canonicalized(canonicalizeClusterDN(fullDN.entries())) {} - - SSLX509Name fullDN; - std::vector<SSLX509Name::Entry> canonicalized; +static const stdx::unordered_set<std::string> defaultMatchingAttributes = { + kOID_DC.toString(), + kOID_O.toString(), + kOID_OU.toString(), }; -synchronized_value<boost::optional<DNValue>> clusterMemberOverride; -boost::optional<std::vector<SSLX509Name::Entry>> getClusterMemberDNOverrideParameter() { - auto guarded_value = clusterMemberOverride.synchronize(); + +synchronized_value<boost::optional<SSLX509Name>> clusterAuthDNOverride; +boost::optional<SSLX509Name> getClusterAuthDNOverrideParameter() { + auto guarded_value = clusterAuthDNOverride.synchronize(); auto& value = *guarded_value; if (!value) { return boost::none; } - return value->canonicalized; + return value; } } // namespace @@ -410,36 +378,50 @@ void ClusterMemberDNOverride::append(OperationContext* opCtx, BSONObjBuilder* b, StringData name, const boost::optional<TenantId>&) { - auto value = clusterMemberOverride.get(); + auto value = clusterAuthDNOverride.get(); if (value) { - b->append(name, value->fullDN.toString()); + b->append(name, value->toString()); } } Status ClusterMemberDNOverride::setFromString(StringData str, const boost::optional<TenantId>&) { if (str.empty()) { - *clusterMemberOverride = boost::none; + *clusterAuthDNOverride = boost::none; return Status::OK(); } - auto swDN = parseDN(str); - if (!swDN.isOK()) { - return swDN.getStatus(); + auto swFullDN = parseDN(str); + if (!swFullDN.isOK()) { + return swFullDN.getStatus(); } - auto dn = std::move(swDN.getValue()); - auto status = dn.normalizeStrings(); + auto fullDN = std::move(swFullDN.getValue()); + auto status = fullDN.normalizeStrings(); if (!status.isOK()) { return status; } - DNValue val(std::move(dn)); - if (val.canonicalized.empty()) { - return {ErrorCodes::BadValue, - "Cluster member DN's must contain at least one O, OU, or DC component"}; + *clusterAuthDNOverride = {std::move(fullDN)}; + return Status::OK(); +} + +SSLX509Name filterClusterDN(const SSLX509Name& fullClusterDN, + const stdx::unordered_set<std::string>& filteredAttributes) { + std::vector<std::vector<SSLX509Name::Entry>> ret; + + for (const auto& rdn : fullClusterDN.entries()) { + std::vector<SSLX509Name::Entry> filteredRdn; + for (const auto& entry : rdn) { + if (filteredAttributes.contains(entry.oid)) { + filteredRdn.push_back(entry); + } + } + + if (!filteredRdn.empty()) { + ret.push_back(filteredRdn); + } } - *clusterMemberOverride = {std::move(val)}; - return Status::OK(); + return SSLX509Name(ret); } StatusWith<SSLX509Name> parseDN(StringData sd) try { @@ -690,6 +672,13 @@ Status SSLX509Name::normalizeStrings() { return Status::OK(); } +bool SSLX509Name::contains(const SSLX509Name& other) const { + return std::all_of( + other.entries().begin(), other.entries().end(), [this](const auto& attribute) { + return std::find(_entries.begin(), _entries.end(), attribute) != _entries.end(); + }); +} + StatusWith<std::string> SSLX509Name::getOID(StringData oid) const { for (const auto& rdn : _entries) { for (const auto& entry : rdn) { @@ -727,10 +716,39 @@ Status SSLConfiguration::setServerSubjectName(SSLX509Name name) { return status; } _serverSubjectName = std::move(name); - _canonicalServerSubjectName = canonicalizeClusterDN(_serverSubjectName.entries()); return Status::OK(); } +Status SSLConfiguration::setClusterAuthX509Attributes() try { + uassert( + ErrorCodes::InvalidSSLConfiguration, + "tlsClusterAuthX509Attributes and tlsX509ClusterAuthDNOverride cannot both be set at once", + sslGlobalParams.clusterAuthX509Attributes.empty() || + getClusterAuthDNOverrideParameter() == boost::none); + + if (!sslGlobalParams.clusterAuthX509Attributes.empty()) { + auto attributesAsDN = uassertStatusOK(parseDN(sslGlobalParams.clusterAuthX509Attributes)); + uassertStatusOK(attributesAsDN.normalizeStrings()); + + // The server's outgoing certificate subject DN and incoming certificate subject DN should + // match the criteria being used to determine other cluster member nodes. + uassert(ErrorCodes::InvalidSSLConfiguration, + "The server's outgoing certificate's DN does not contain the attributes specified " + "in tlsClusterAuthX509Attributes", + _serverSubjectName.contains(attributesAsDN)); + uassert(ErrorCodes::InvalidSSLConfiguration, + "The server's incoming certificate's DN does not contain the attributes specified " + "in tlsClusterAuthX509Attributes", + clientSubjectName.contains(attributesAsDN)); + + _clusterAuthX509Attributes = std::move(attributesAsDN); + } + + return Status::OK(); +} catch (const DBException& ex) { + return ex.toStatus(); +} + /** * The behavior of isClusterMember() is subtly different when passed * an SSLX509Name versus a StringData. @@ -743,21 +761,35 @@ Status SSLConfiguration::setServerSubjectName(SSLX509Name name) { * the server's distinguished name. */ bool SSLConfiguration::isClusterMember(SSLX509Name subject) const { - if (!subject.normalizeStrings().isOK()) { + if (auto status = subject.normalizeStrings(); !status.isOK()) { + LOGV2_WARNING(23220, "Unable to normalize client subject name", "error"_attr = status); return false; } - auto client = canonicalizeClusterDN(subject.entries()); - if (client.empty()) { - return false; + // If tlsClusterAuthX509Attributes have been specified, then subject is a cluster member as long + // as it contains all of the entries in _clusterAuthX509Attributes. + if (_clusterAuthX509Attributes) { + return subject.contains(*_clusterAuthX509Attributes); } - if (client == _canonicalServerSubjectName) { + // If not specified, check DC, O, and OU (the default matching attributes) from + // the server DN. + auto defaultFilteredSubjectDN = filterClusterDN(_serverSubjectName, defaultMatchingAttributes); + if (subject.contains(defaultFilteredSubjectDN)) { return true; } - auto altClusterDN = getClusterMemberDNOverrideParameter(); - return (altClusterDN && (client == *altClusterDN)); + // If the certificate did not match DC, O, and OU from the server DN, then the only way that it + // could still be accepted as a cluster member is if it contains the DC, O, and/or OU in the + // tlsClusterAuthDNOverride DN. + auto altClusterDN = getClusterAuthDNOverrideParameter(); + if (altClusterDN) { + auto defaultFilteredAltClusterDN = + filterClusterDN(*altClusterDN, defaultMatchingAttributes); + return subject.contains(defaultFilteredAltClusterDN); + } + + return false; } bool SSLConfiguration::isClusterMember(StringData subjectName) const { @@ -769,19 +801,8 @@ bool SSLConfiguration::isClusterMember(StringData subjectName) const { "error"_attr = swClient.getStatus()); return false; } - auto& client = swClient.getValue(); - auto status = client.normalizeStrings(); - if (!status.isOK()) { - LOGV2_WARNING(23220, - "Unable to normalize client subject name: {error}", - "Unable to normalize client subject name", - "error"_attr = status); - return false; - } - - auto canonicalClient = canonicalizeClusterDN(client.entries()); - return !canonicalClient.empty() && (canonicalClient == _canonicalServerSubjectName); + return isClusterMember(swClient.getValue()); } void SSLConfiguration::getServerStatusBSON(BSONObjBuilder* security) const { diff --git a/src/mongo/util/net/ssl_manager.h b/src/mongo/util/net/ssl_manager.h index 28b218530d5..6c8c98a91cf 100644 --- a/src/mongo/util/net/ssl_manager.h +++ b/src/mongo/util/net/ssl_manager.h @@ -446,6 +446,14 @@ std::string removeFQDNRoot(std::string name); std::string escapeRfc2253(StringData str); /** + * Generates a new SSLX509Name containing only the attributes requested in filteredAttributes. + * Note that multi-valued RDNs will be preserved if any of the attributes in the RDN are specified + * in filteredAttributes. + */ +SSLX509Name filterClusterDN(const SSLX509Name& fullClusterDN, + const stdx::unordered_set<std::string>& filterAttributes); + +/** * Parse a DN from a string per RFC 4514 */ StatusWith<SSLX509Name> parseDN(StringData str); diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp index de847d52da0..64bb5751af0 100644 --- a/src/mongo/util/net/ssl_manager_openssl.cpp +++ b/src/mongo/util/net/ssl_manager_openssl.cpp @@ -1815,6 +1815,7 @@ SSLManagerOpenSSL::SSLManagerOpenSSL(const SSLParams& params, << params.sslPEMKeyFile); uassertStatusOK(_sslConfiguration.setServerSubjectName(std::move(serverSubjectName))); + uassertStatusOK(_sslConfiguration.setClusterAuthX509Attributes()); CertificateExpirationMonitor::get()->updateExpirationDeadline( _sslConfiguration.serverCertificateExpirationDate); diff --git a/src/mongo/util/net/ssl_manager_test.cpp b/src/mongo/util/net/ssl_manager_test.cpp index 8f6fef22362..a65b1db53e8 100644 --- a/src/mongo/util/net/ssl_manager_test.cpp +++ b/src/mongo/util/net/ssl_manager_test.cpp @@ -42,6 +42,7 @@ #include "mongo/logv2/log.h" #include "mongo/unittest/unittest.h" +#include "mongo/util/net/ssl_types.h" #if MONGO_CONFIG_SSL_PROVIDER == MONGO_CONFIG_SSL_PROVIDER_OPENSSL #include "mongo/util/net/dh_openssl.h" @@ -424,6 +425,105 @@ FlattenedX509Name flattenX509Name(const SSLX509Name& name) { return ret; } +TEST(SSLManager, FilterClusterDN) { + static const stdx::unordered_set<std::string> defaultMatchingAttributes = { + "0.9.2342.19200300.100.1.25", // DC + "2.5.4.10", // O + "2.5.4.11", // OU + }; + std::vector<std::pair<std::string, std::string>> tests = { + // Single-valued RDNs. + {"CN=server,OU=Kernel,O=MongoDB,DC=example,L=New York City,ST=New York,C=US", + "OU=Kernel,O=MongoDB,DC=example"}, + // Multi-valued RDN. + {"CN=server+OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US", "OU=Kernel,O=MongoDB"}, + // Multiple DC attributes. + {"CN=server,OU=Kernel,O=MongoDB,DC=example,DC=net,L=New York City,ST=New York,C=US", + "OU=Kernel,O=MongoDB,DC=example,DC=net"}, + }; + + for (const auto& test : tests) { + LOGV2(7498900, "Testing DN: ", "test_first"_attr = test.first); + auto swUnfilteredDN = parseDN(test.first); + auto swExpectedFilteredDN = parseDN(test.second); + + ASSERT_OK(swUnfilteredDN.getStatus()); + ASSERT_OK(swExpectedFilteredDN.getStatus()); + ASSERT_OK(swUnfilteredDN.getValue().normalizeStrings()); + ASSERT_OK(swExpectedFilteredDN.getValue().normalizeStrings()); + + auto actualFilteredDN = + filterClusterDN(swUnfilteredDN.getValue(), defaultMatchingAttributes); + ASSERT_TRUE(actualFilteredDN == swExpectedFilteredDN.getValue()); + } +}; + +TEST(SSLManager, DNContains) { + // Checks if the second RDN is contained by the first (order does not matter). + // The bool is the expected value. + std::vector<std::tuple<std::string, std::string, bool>> tests = { + // Single-valued RDNs positive case. + {"CN=server,OU=Kernel,O=MongoDB,DC=example,L=New York City,ST=New York,C=US", + "CN=server,L=New York City,ST=New York,C=US", + true}, + // Single-valued RDNs mismatched value. + {"CN=server,OU=Kernel,O=MongoDB,DC=example,L=New York City,ST=New York,C=US", + "CN=server,L=Yonkers,ST=New York,C=US", + false}, + // Single-valued RDNs missing attribute. + {"CN=server,OU=Kernel,O=MongoDB,DC=example,ST=New York,C=US", + "CN=server,L=Yonkers,ST=New York,C=US", + false}, + // Multi-valued RDN negative case (attribute value mismatch). + {"CN=server,OU=Kernel,O=MongoDB,L=New York City+ST=New York,C=US", + "CN=server,L=Yonkers+ST=New York", + false}, + // Multi-valued RDN negative case (matching attributes in single-value RDNs, first RDN needs + // to be filtered beforehand). + {"CN=server,OU=Kernel,O=MongoDB,L=New York City+ST=New York,C=US", + "CN=server,L=New York City", + false}, + // Multi-valued RDN negative case (input DN has attributes in single-value RDNs while match + // expects multi-valued RDN). + {"CN=server,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US", + "CN=server,L=New York City+ST=New York", + false}, + // Multi-valued RDN positive case (full multi-valued RDN present in second). + {"CN=server,OU=Kernel,O=MongoDB,L=New York City+ST=New York,C=US", + "CN=server,L=New York City+ST=New York", + true}, + // Multiple attributes positive case (order should not matter). + {"CN=server,OU=Kernel,O=MongoDB,DC=net,DC=example,L=New York City,ST=New York,C=US", + "OU=Kernel,O=MongoDB,DC=example,DC=net", + true}, + // Multiple attributes positive case (missing in second, but should not matter). + {"CN=server,OU=Kernel,O=MongoDB,DC=example,DC=net,L=New York City,ST=New York,C=US", + "OU=Kernel,O=MongoDB,DC=example", + true}, + // Multiple attributes negative case (missing in first). + {"CN=server,OU=Kernel,O=MongoDB,DC=example,L=New York City,ST=New York,C=US", + "OU=Kernel,O=MongoDB,DC=example,DC=net", + false}, + }; + + for (const auto& test : tests) { + LOGV2(7498901, "Testing DN: ", "test_first"_attr = std::get<0>(test)); + auto swExternalDN = parseDN(std::get<0>(test)); + auto swMatchPatternDN = parseDN(std::get<1>(test)); + + ASSERT_OK(swExternalDN.getStatus()); + ASSERT_OK(swMatchPatternDN.getStatus()); + + auto externalDN = swExternalDN.getValue(); + auto matchPatternDN = swMatchPatternDN.getValue(); + + ASSERT_OK(externalDN.normalizeStrings()); + ASSERT_OK(matchPatternDN.normalizeStrings()); + + ASSERT_EQ(externalDN.contains(matchPatternDN), std::get<2>(test)); + } +}; + TEST(SSLManager, DNParsingAndNormalization) { std::vector<std::pair<std::string, FlattenedX509Name>> tests = { // Basic DN parsing diff --git a/src/mongo/util/net/ssl_options.h b/src/mongo/util/net/ssl_options.h index 41bb82c66ea..2f50b564074 100644 --- a/src/mongo/util/net/ssl_options.h +++ b/src/mongo/util/net/ssl_options.h @@ -73,6 +73,7 @@ struct SSLParams { std::string sslCipherConfig; // --tlsCipherConfig std::string sslCipherSuiteConfig; // --tlsCipherSuiteConfig std::string clusterAuthX509ExtensionValue; // --tlsClusterAuthX509ExtensionValue + std::string clusterAuthX509Attributes; // --tlsClusterAuthX509Attributes boost::optional<TLSCATrusts> tlsCATrusts; // --setParameter tlsCATrusts diff --git a/src/mongo/util/net/ssl_options_server.cpp b/src/mongo/util/net/ssl_options_server.cpp index 2f1e6f15179..a85acc3ff0c 100644 --- a/src/mongo/util/net/ssl_options_server.cpp +++ b/src/mongo/util/net/ssl_options_server.cpp @@ -254,6 +254,17 @@ MONGO_STARTUP_OPTIONS_POST(SSLServerOptions)(InitializerContext*) { params["net.tls.clusterAuthX509.extensionValue"].as<std::string>(); } + if (params.count("net.tls.clusterAuthX509.attributes")) { + uassert(ErrorCodes::BadValue, + "Unknown configuration option 'net.tls.clusterAuthX509.attributes'", + gFeatureFlagConfigurableX509ClusterAuthn.isEnabledAndIgnoreFCV()); + uassert(ErrorCodes::BadValue, + "Cannot set clusterAuthX509.attributes when clusterAuthMode does not allow X.509", + clusterAuthMode.allowsX509()); + sslGlobalParams.clusterAuthX509Attributes = + params["net.tls.clusterAuthX509.attributes"].as<std::string>(); + } + if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_allowSSL) { // allowSSL and x509 is valid only when we are transitioning to auth. if (clusterAuthMode.sendsX509() && !serverGlobalParams.transitionToAuth) { diff --git a/src/mongo/util/net/ssl_options_server.idl b/src/mongo/util/net/ssl_options_server.idl index 0311f3e74da..be97af32294 100644 --- a/src/mongo/util/net/ssl_options_server.idl +++ b/src/mongo/util/net/ssl_options_server.idl @@ -63,7 +63,10 @@ configs: hidden: true "net.tls.certificateKeyFile": - description: "Certificate and key file for TLS" + description: >- + Certificate and key file for TLS. Certificate is presented in response to inbound connections + always. Certificate is also presented for outbound connections if tlsClusterFile is not + specified. short_name: tlsCertificateKeyFile deprecated_name: "net.ssl.PEMKeyFile" deprecated_short_name: sslPEMKeyFile @@ -79,7 +82,9 @@ configs: redact: true "net.tls.clusterFile": - description: "Key file for internal TLS authentication" + description: >- + Certificate and key file for internal TLS authentication. Certificate is presented on outbound + connections if specified. short_name: tlsClusterFile deprecated_name: "net.ssl.clusterFile" deprecated_short_name: sslClusterFile @@ -95,7 +100,10 @@ configs: redact: true "net.tls.CAFile": - description: "Certificate Authority file for TLS" + description: >- + Certificate Authority file for TLS. Used to verify remote certificates presented in response + to outbound connections. Also used to verify remote certificates from inbound connections if + tlsClusterCAFile is not specified. short_name: tlsCAFile deprecated_name: "net.ssl.CAFile" deprecated_short_name: sslCAFile @@ -187,5 +195,13 @@ configs: for OID 1.3.6.1.4.1.34601.2.1.2 which contains the specified value. short_name: tlsClusterAuthX509ExtensionValue arg_vartype: String - # // TODO SERVER-74989 X.509 Subject Name Matching - # conflicts: "net.tls.clusterAuthX509.attributes" + conflicts: "net.tls.clusterAuthX509.attributes" + + "net.tls.clusterAuthX509.attributes": + description: >- + If specified, clients performing X.509 authentication must present a certificate with a + subject name with the exact attributes and values provided in this config option to be + treated as peer cluster nodes. + short_name: tlsClusterAuthX509Attributes + arg_vartype: String + conflicts: "net.tls.clusterAuthX509.extensionValue" diff --git a/src/mongo/util/net/ssl_types.h b/src/mongo/util/net/ssl_types.h index 6f859ee01aa..ed41e242ebb 100644 --- a/src/mongo/util/net/ssl_types.h +++ b/src/mongo/util/net/ssl_types.h @@ -34,6 +34,7 @@ #include "mongo/bson/util/builder.h" #include "mongo/db/auth/role_name.h" #include "mongo/stdx/unordered_set.h" +#include "mongo/util/synchronized_value.h" namespace mongo { @@ -65,7 +66,7 @@ public: explicit SSLX509Name(std::vector<std::vector<Entry>> entries) : _entries(std::move(entries)) {} /** - * Retreive the first instance of the value for a given OID in this name. + * Retrieve the first instance of the value for a given OID in this name. * Returns ErrorCodes::KeyNotFound if the OID does not exist. */ StatusWith<std::string> getOID(StringData oid) const; @@ -97,6 +98,12 @@ public: */ Status normalizeStrings(); + /** + * A SSLX509Name is said to contain another SSLX509Name if it contains all of the other + * SSLX509Name's entries. + */ + bool contains(const SSLX509Name& other) const; + private: std::vector<std::vector<Entry>> _entries; }; @@ -115,6 +122,11 @@ public: bool isClusterMember(SSLX509Name subjectName) const; void getServerStatusBSON(BSONObjBuilder*) const; Status setServerSubjectName(SSLX509Name name); + Status setClusterAuthX509Attributes(); + + const boost::optional<SSLX509Name>& getClusterAuthX509Attributes() { + return _clusterAuthX509Attributes; + } const SSLX509Name& serverSubjectName() const { return _serverSubjectName; @@ -126,7 +138,9 @@ public: private: SSLX509Name _serverSubjectName; - std::vector<SSLX509Name::Entry> _canonicalServerSubjectName; + + // DN provided via tlsClusterAuthX509.attributes. + boost::optional<SSLX509Name> _clusterAuthX509Attributes; }; } // namespace mongo |