summaryrefslogtreecommitdiff
path: root/src/mongo/util
diff options
context:
space:
mode:
authorVarun Ravichandran <varun.ravichandran@mongodb.com>2023-03-29 05:44:21 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-04-04 07:05:50 +0000
commit39f7c9035ab26b15b95625e016da51be946374a2 (patch)
tree5039e540d07025fd6701f977c2411aa08c81e29d /src/mongo/util
parent11a5a961e255e9c07b1068bd702d57daa15ec33e (diff)
downloadmongo-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/SConscript1
-rw-r--r--src/mongo/util/net/ssl_manager.cpp173
-rw-r--r--src/mongo/util/net/ssl_manager.h8
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp1
-rw-r--r--src/mongo/util/net/ssl_manager_test.cpp100
-rw-r--r--src/mongo/util/net/ssl_options.h1
-rw-r--r--src/mongo/util/net/ssl_options_server.cpp11
-rw-r--r--src/mongo/util/net/ssl_options_server.idl26
-rw-r--r--src/mongo/util/net/ssl_types.h18
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