summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/libs/cluster_title_foo.pem54
-rw-r--r--jstests/libs/cluster_title_foo.pem.digest.sha11
-rw-r--r--jstests/libs/cluster_title_foo.pem.digest.sha2561
-rw-r--r--jstests/libs/cluster_title_foo_no_o_ou_dc.pem53
-rw-r--r--jstests/libs/cluster_title_foo_no_o_ou_dc.pem.digest.sha11
-rw-r--r--jstests/libs/cluster_title_foo_no_o_ou_dc.pem.digest.sha2561
-rw-r--r--jstests/libs/server_title_bar.pem53
-rw-r--r--jstests/libs/server_title_bar.pem.digest.sha11
-rw-r--r--jstests/libs/server_title_bar.pem.digest.sha2561
-rw-r--r--jstests/libs/server_title_foo.pem53
-rw-r--r--jstests/libs/server_title_foo.pem.digest.sha11
-rw-r--r--jstests/libs/server_title_foo.pem.digest.sha2561
-rw-r--r--jstests/libs/server_title_foo_no_o_ou_dc.pem53
-rw-r--r--jstests/libs/server_title_foo_no_o_ou_dc.pem.digest.sha11
-rw-r--r--jstests/libs/server_title_foo_no_o_ou_dc.pem.digest.sha2561
-rw-r--r--jstests/ssl/x509/certs.yml75
-rw-r--r--jstests/sslSpecial/cluster_auth_x509_subject_attributes.js222
-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
26 files changed, 829 insertions, 83 deletions
diff --git a/jstests/libs/cluster_title_foo.pem b/jstests/libs/cluster_title_foo.pem
new file mode 100644
index 00000000000..51c5a1b0e11
--- /dev/null
+++ b/jstests/libs/cluster_title_foo.pem
@@ -0,0 +1,54 @@
+# Autogenerated file, do not edit.
+# Generate using jstests/ssl/x509/mkcert.py --config jstests/ssl/x509/certs.yml cluster_title_foo.pem
+#
+# Alternate certificate for intracluster auth including the title attribute set to foo.
+-----BEGIN CERTIFICATE-----
+MIIDjzCCAnegAwIBAgIEWd0RDTANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV
+UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO
+BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs
+IFRlc3QgQ0EwHhcNMjMwMzIyMDIzODIyWhcNMjUwNjIzMDIzODIyWjB/MQswCQYD
+VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENp
+dHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEUMBIGA1UEAwwL
+Y2x1c3RlcnRlc3QxDDAKBgNVBAwMA2ZvbzCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKtYXLqpGhTggA2fItGZDqGwmPmUWpMpazBZ1vMxyvWeLQvso9Pk
+Ubz+zXT0MP+XtjteoqUwNcfRViSiv2wiIttBD3VlGH3dGJXSnQaMMjE1MORkkjHJ
+qeSZZA75QEpfyRhx7Tc+JEIwQx1Ptrrt1k9rQv58x1N8zN27Eqsqw3f9dq4XjpCs
+XRBcOOSjVyHRKli5j1wxFLDNxBtr5+i5LfmWOgPY/KSQtE0cRqFXTxajHuMaRUtl
+z9QMKRKc2uN3E7fA1Fa8IboT4mhG6mY9xO2rMf0cV4ZuMa3LimwG4KnTnii8cz8g
+fXPDENvdI4/Wm6YuUlQRlfu6v77Mb0UEfW8CAwEAAaMeMBwwGgYDVR0RBBMwEYIJ
+bG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCdZZaJZN0X1htNTL1I
+/ENBqZ5NYXyQi24yMJbRz+hVTFaR0gaecAG5A388YbcjmO1yLZcpzI4oHDSInc0Z
+1kS8Zsriqfkh4ZQsWeHV9LImclecpYK2l0VB6YOpTOS2f75+PEaRSEYiYWEwERrk
+q5IVodd59c5Mn8GUUrJVlVpNiwX1w0J9+qiUtmuQqrORpt6hbV1DGwXrMQgpprys
+tZiEOxRUEyGTTtMoxktsGbT0o6Z+YAQRl90UVB7rPCpzwuJECFi0JXH28cIfncnr
+8HVnEhxsPg4HHQmb5Ykq/gxNbAWSQAey3fP2NKosGnRKDJbd1ivyvvQNWya3DLIW
+dbnN
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrWFy6qRoU4IAN
+nyLRmQ6hsJj5lFqTKWswWdbzMcr1ni0L7KPT5FG8/s109DD/l7Y7XqKlMDXH0VYk
+or9sIiLbQQ91ZRh93RiV0p0GjDIxNTDkZJIxyankmWQO+UBKX8kYce03PiRCMEMd
+T7a67dZPa0L+fMdTfMzduxKrKsN3/XauF46QrF0QXDjko1ch0SpYuY9cMRSwzcQb
+a+fouS35ljoD2PykkLRNHEahV08Wox7jGkVLZc/UDCkSnNrjdxO3wNRWvCG6E+Jo
+RupmPcTtqzH9HFeGbjGty4psBuCp054ovHM/IH1zwxDb3SOP1pumLlJUEZX7ur++
+zG9FBH1vAgMBAAECggEAIQ63NTwS0BxQGFCvgwiojgFoQh6hKus+xuFOWzUsFx8h
+Sb+qC+Ns8a2nLf0+xtEaU3H6pywZ9CcrG35auB4N44c12Exc4Uuaxq0Ppoe910iP
+2kCdBAYIRRZi+5CTGsZIIfM49QOEM1DkYe9TLdVdF412K2sfebgGPnEtNODXPXrU
+P0iLqxXRJmiWMZoxzbxNATMS8LkUG4gjfDeuGJZD1QFoun6hbCT4W6B2CgVnSpM1
+8Njys39V55wjAsfaKm4gpzeNRj5V0iw/G++G43uCVUQntTR/kzMABsfYFIWmfuOq
+E50VteYwzlxskQZxqAPcw/7QmZNCANIfEEWFw3hmAQKBgQDgCiYlquODGUTcDydF
+jzmZ3nnpacvBQ9KVO8IwpOg0v63EGXokvhVBigUszYTnqdlnihTJxkccXeCr21G7
+pL1tqq8qKQga46MH1B4DF7xYftwwYewEzXYIfYfMkJaxPw3Q9xrWU0y2pbHUY7zg
+0odpV5IFkhyRpig3vdS15gqjQQKBgQDDydQx1XniYh72lX89Od5fXnfubzJJiG5J
+GzSP0Z4GMusEX6cV4VTjZydDunv22nmHUj2yVtXIyFST1VJ5A2/OmSwjN8Dm/91E
+/fTaSa3Eh/H5EzUV6EtuZXnIdYWBM/tQfQwViA2gph2mIMLUD7kxVo5G6y6wL+kw
+kgDqWRnkrwKBgQC0v1thXkoo9VT5mPwdAVz+R1/hsSniZR5aqZiUeCaij9XX9Jn3
+VKd/daORLsm/wOcVwm/dDatHNnHRFKMPGOx+soqZH/ta/jYEVdxUsGySlN595jJs
++Xn1hZjur+PzYaR65zDuosusO2eJq2GxnAgFM9IpzmRgGUYvGmamzc3dQQKBgHrB
+2iTgx4oUoXtUIrI9zVqYfbPmzm3id9uojh06fc0/MbHNU5LZdIMcUzcY/s65Dwe0
+nfBql6JLURRb5VjwubKcwVrXg0CS3qZ6YIJZPfWCk0nrLBavTlRKlcAFR47KC+Hc
+da4uXvUCEobt9ZpGvYPc1FpM7ToU4C3O7XoCIcULAoGAD7W2C2tiHepHUlbLCiEt
+fHoyoWVc1v1xPRdw/lNHVkopHyxB7Zg8nf2ei9kv+6ECdqmNk6qiYVtFMd+gxK3e
+G5sgEZ2GazACraR9snz+iBOyYm+CoKJd1YzeyuFIs3hdq0++QQAm9XDaTu6C8HEM
+bkhlGRJcQyaN32bPtRXkymY=
+-----END PRIVATE KEY-----
diff --git a/jstests/libs/cluster_title_foo.pem.digest.sha1 b/jstests/libs/cluster_title_foo.pem.digest.sha1
new file mode 100644
index 00000000000..2e6c630d0f4
--- /dev/null
+++ b/jstests/libs/cluster_title_foo.pem.digest.sha1
@@ -0,0 +1 @@
+AAA79606BF68AE2AFA2A0F37F4DCD09FFCFD8295 \ No newline at end of file
diff --git a/jstests/libs/cluster_title_foo.pem.digest.sha256 b/jstests/libs/cluster_title_foo.pem.digest.sha256
new file mode 100644
index 00000000000..21bcc294a52
--- /dev/null
+++ b/jstests/libs/cluster_title_foo.pem.digest.sha256
@@ -0,0 +1 @@
+63EF60AFA384EAE126790C8CE5EE438F5956C77378D8997AD1644DBCC310F3DB \ No newline at end of file
diff --git a/jstests/libs/cluster_title_foo_no_o_ou_dc.pem b/jstests/libs/cluster_title_foo_no_o_ou_dc.pem
new file mode 100644
index 00000000000..b3dff850c96
--- /dev/null
+++ b/jstests/libs/cluster_title_foo_no_o_ou_dc.pem
@@ -0,0 +1,53 @@
+# Autogenerated file, do not edit.
+# Generate using jstests/ssl/x509/mkcert.py --config jstests/ssl/x509/certs.yml cluster_title_foo_no_o_ou_dc.pem
+#
+# Alternate certificate for intracluster auth including the title attribute set to foo without O, OU, or DC.
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIER0TcWzANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV
+UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO
+BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs
+IFRlc3QgQ0EwHhcNMjMwMzIyMDIzODUyWhcNMjUwNjIzMDIzODUyWjBcMRQwEgYD
+VQQDDAtjbHVzdGVydGVzdDEMMAoGA1UEDAwDZm9vMQswCQYDVQQGEwJVUzERMA8G
+A1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwpF7T1FoPnfAHotkAv5NRotQekIebAqHW
+ohdeQiqmJoIMJ58qZOaTaNm+HMRiPo6/PYuKqup7w9nkbBO6xRK6+N8nn3IsrOVl
+MOuERahyCcjEBRStZL/QiDSOK7FzBwLsnx/wSgNWisOzi840h0+OLmtpEK4kjxgg
+sbH5GFEObfWX4OgHtjdf4MTn/EZkyb643MQT6aD8/qQ0/Ai0ptKCbuCfycondK6U
+Tzps9vA61gXy/KwPhYfs9BVeWQAP7XHZvv7Lqgg5yciEC+qBwR3/pCYUhNqECndj
+VY0Uffp/uH5snBRNfS1+/p9jIo+t0nq3UXVjfz+Fl1Uwndp8wtaNAgMBAAGjHjAc
+MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEA
+E4q9YQX7PJ3IwibFNDpmwnb/mDHXQyhYdJsh7eRmdrF60TEMiXdYV+NMpVf/j1qV
+bXiV7TskcNkqIK+88wbgDeR0gen+MhAyHCSe5B7QwRsuQ+8elsN84urmu9fddSKw
+XycjivcqpqTGSyndWy5FAIfJ2SepZswgUofKcYOju36y6Ai5UBCQA1lNTwQHpQ8L
+nZbf/mcqtQ0Op9y+UaT8r+L/ju9rNTVw96fDq4oJNXHZQgFUKZrv73RsJJaj8v5X
+w0rYEQn0i3hIlap9clp4dXqFeqwrRxa5nI838p6DvjyMzBagMC6RVEHHI/JHAfzF
+yy9y0ma7HQ32Lg5XspPrGw==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCwpF7T1FoPnfAH
+otkAv5NRotQekIebAqHWohdeQiqmJoIMJ58qZOaTaNm+HMRiPo6/PYuKqup7w9nk
+bBO6xRK6+N8nn3IsrOVlMOuERahyCcjEBRStZL/QiDSOK7FzBwLsnx/wSgNWisOz
+i840h0+OLmtpEK4kjxggsbH5GFEObfWX4OgHtjdf4MTn/EZkyb643MQT6aD8/qQ0
+/Ai0ptKCbuCfycondK6UTzps9vA61gXy/KwPhYfs9BVeWQAP7XHZvv7Lqgg5yciE
+C+qBwR3/pCYUhNqECndjVY0Uffp/uH5snBRNfS1+/p9jIo+t0nq3UXVjfz+Fl1Uw
+ndp8wtaNAgMBAAECggEABZWsydW04zmDFTq40aU86x/SxQScxPHYXAjT5E8DOi2N
+fwThq111TMPL3o7aRqDjsngnqUKuFyuh/+7K0OTaKr8jjwUjfvYYapKZX500LibR
+CiF+/dxplBY6UyRef9yA4ypEwDwWzu2kMlEBO/frM/uTucalOtKrWJ1FmzKBnYse
+8H9zLyKbc96xk6IiFYlBqe6O6JT6mZtBHwz59zVmuJ7eP0V8Se8ZTA1MEE3P+ORR
+/9xLURQc0hvfDFwSnM/gKAuwB3tpnJsEUmRCX0WBBCEiEJ+FaQ5yAihmfRv9AH8c
+dFR/7XuKEMN5jetR4khjB2eBY26SXRzTQ8qE9fujoQKBgQDonTDu6EAa0yoySjQ+
+q3KW2Ir4Egqw3kJfBQ3ZBjtsRvbBsl0S0rEfq2EfgusKvIz9sVDLEGhrOnn8zhKM
+CWkaikZORwniRtGUpMdsbw7UfUHaSDi/12kqD7vKXs3bJWrQsRVl3yHMYaDHWAUF
+L9q9rvD7AD12bFMH8cBnGjmuFwKBgQDCZp+0G8fUlVgACwmMNnToOT/mzQEjsUlG
+4ReS/o889pPvtpm+Ul5XK1Pl1gvcwfSo2hkzXBht95Sj5t7L4qBkK2naoN1LgbfX
+R/fLuMQLCYgUOs3UbOUfyOy1LfgEHINuDHVaK7RkiWhuHE3/a+VKZvOnffL8Copu
+xo1LyUHK+wKBgD8Rh5fu/pqHUHSMK/gl8g62LY+vDJkB2gr7StLh3rCv2O2Rl6yn
+1YBZrh6mF2Y00yFhtx8nlrgkBbkmgl7XmliozwEgP6zLOL3No4hh4Cp6v6UYWdKh
+7BCMbYUkCTp2vaxRpxSU2AwbGEWUNuA+JlexnALiAMgf/K81u834jVUHAoGBAIBP
+K+m8zFBLoiGlJ1AcQV1lLAAyHyZnxW2688xZqEEcntgBNcigpRPzzRROCtZSTiGE
+kk2L47PxTXJA15zKoAJ9hQiAVI+ZtrWpEqyr7vk5+U8g4OnsVe58t39+L8zG5Ril
+sG8rmY0iBINouzJzDIvnF7rdLpuceXJUKr5yv7IxAoGBALxfb5KmvbdmBe43a5zo
+J+Ig8oURUXbaWPb+8rpp+GaK3Hqf0Asjqlq2Fulz6TlwtkoRPl1yyebkNqz23p+T
+0K52WJWnpxmXi5dRqDTJie/E8Tvm8ff/Xey04jDdS+J56WAAnC0P5O+Lq9BD6iNG
+U3G/2LmJ+zn2NPeSxPyW3PSf
+-----END PRIVATE KEY-----
diff --git a/jstests/libs/cluster_title_foo_no_o_ou_dc.pem.digest.sha1 b/jstests/libs/cluster_title_foo_no_o_ou_dc.pem.digest.sha1
new file mode 100644
index 00000000000..25501110235
--- /dev/null
+++ b/jstests/libs/cluster_title_foo_no_o_ou_dc.pem.digest.sha1
@@ -0,0 +1 @@
+ECA9EA58F05E2C92503D0F0B776BA5264A7D9D4B \ No newline at end of file
diff --git a/jstests/libs/cluster_title_foo_no_o_ou_dc.pem.digest.sha256 b/jstests/libs/cluster_title_foo_no_o_ou_dc.pem.digest.sha256
new file mode 100644
index 00000000000..87c6d2980e6
--- /dev/null
+++ b/jstests/libs/cluster_title_foo_no_o_ou_dc.pem.digest.sha256
@@ -0,0 +1 @@
+D2AAD57CB4C330806DA153860BD0E908E3CFE4C41061986C3F27DBC1DD80B2D2 \ No newline at end of file
diff --git a/jstests/libs/server_title_bar.pem b/jstests/libs/server_title_bar.pem
new file mode 100644
index 00000000000..13d303c4fda
--- /dev/null
+++ b/jstests/libs/server_title_bar.pem
@@ -0,0 +1,53 @@
+# Autogenerated file, do not edit.
+# Generate using jstests/ssl/x509/mkcert.py --config jstests/ssl/x509/certs.yml server_title_bar.pem
+#
+# Server certificate including the title attribute set to bar.
+-----BEGIN CERTIFICATE-----
+MIIDijCCAnKgAwIBAgIEKf++izANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV
+UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO
+BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs
+IFRlc3QgQ0EwHhcNMjMwMzIyMDIzOTE2WhcNMjUwNjIzMDIzOTE2WjB6MQswCQYD
+VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENp
+dHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEPMA0GA1UEAwwG
+c2VydmVyMQwwCgYDVQQMDANiYXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCmLpAVBx01DYjNf4ElBIZvtYm3JXsOAYa5sYhSXHzxMA7t6xBpzynrXqxt
+WDBwVlL+MEEoBi4lP7TBiBD9aZ/6agukeKliv7DuBZUSORIfu8aOsIXEe+U+F35q
+WvCNod8SpQrxvjvvLbQsJCD+zdrzzIVOCgYToAlDb0znu8fXxFQ2gOPbJEu60aX1
+ca6hPA8+rmbt5KfPJ+fIPV/onhaiMuUklTX7PlntMhYgGYANFAP6fVw2OIgeGXjn
+67z+ZD14EQ0reSfzqrKEbvqzrr8MJJ2wJYoYrT/Atu1JsLeudrb8ilmx26jHKa80
+OG3rxObOsg5z/0K2GsWR8AGXE0KzAgMBAAGjHjAcMBoGA1UdEQQTMBGCCWxvY2Fs
+aG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAFWe3TFS1T/GwfM3jE2g7MJQZ
+30+p9HSfiC9kA3KPgyQE6lna17gyAS+YNaAQjC3pT1o3Dbanjs2Y2Ho/6JWAoeoj
+puzrkgCH9IHIKhR/+JX/XwX+yY6txNzwgRvkdVpQkHZ4dp4LBb9sNQ+RA5T3rAlR
+5g9/LwJbBGP0KSG2nxrsDEa3uYtm6HaqyjyNtCe6Hy9ez4qFq0fmKxnu2DnGgRwZ
+O8hxW4rc/c5JRp5q1EuocpEHZTqZ1SigtdA1nBe6cA60gEOqOFfA7DrN4cM5vyk3
+fkxPGQ+uP/6tTSF8DHIL4lE9X8clKMYc8UU47SYCzN6NIKLBQcaQHTCb5V5ZwA==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmLpAVBx01DYjN
+f4ElBIZvtYm3JXsOAYa5sYhSXHzxMA7t6xBpzynrXqxtWDBwVlL+MEEoBi4lP7TB
+iBD9aZ/6agukeKliv7DuBZUSORIfu8aOsIXEe+U+F35qWvCNod8SpQrxvjvvLbQs
+JCD+zdrzzIVOCgYToAlDb0znu8fXxFQ2gOPbJEu60aX1ca6hPA8+rmbt5KfPJ+fI
+PV/onhaiMuUklTX7PlntMhYgGYANFAP6fVw2OIgeGXjn67z+ZD14EQ0reSfzqrKE
+bvqzrr8MJJ2wJYoYrT/Atu1JsLeudrb8ilmx26jHKa80OG3rxObOsg5z/0K2GsWR
+8AGXE0KzAgMBAAECggEAXNBmwofNpULg5D1RaNZlK2EOAI9bchAiKfZgt/dWBPMd
+c341FZORyxZ+YTe/Hg7onXVf/rWs8jrpfqm7K33hzt+JjxuhJzj+3YGap6neWIDs
+vecTXxD/kTVX8pjF/6SnzWcGfMwN92DkXz7yer2Ii1/wGAz7JdzdL5+rKUY0sGnd
+EVs+f3y46hJ36ejD/DM0Lj9zVMzbOlA/Kiuq+uHGrH3DZBiL5qvzecn+3HM9o8kh
+RzmtdllpsXq+P+MxFoea5OuIbq2vuNh4Cpg5PxEMbXhtPT0XwDf2NtN85CHL2glv
+zI0CqaJ/kNLLLorNrbtekXuLllZPJezPxefyXcby0QKBgQDZi0sOKIAmVXc0whdj
+MmsWpgtjs7S1NCgk09DiObSum/OWDMOYarFGdR/tDl30mzvpCbkk7QhNZcOZlNGX
+szfi6jBm7ejbDaEexJ2U7gU3GaeZ13AqIDukAV2ArMwR40S25JyZ+jZvgsiUXhjv
+nRFXFXaPMejYKPVX9CwLeXBvawKBgQDDju54KZYCKzPrZ0j+CbXTKVEC52Ch6G7S
+g3AAOMHoVXGhn26jD3Uietnq3KI7oSHHeNkqQYYdbFCkjMkF25Rp9xlFILoLZ0VA
+G6krXQ73z+BRPK9TPwzCaVxSXf+mxF4AIrGZbYsSZj+htm74opRk6+q3YrGzI9o1
+0ga84tez2QKBgQCCwT1wmhlEcTRAKrTh86j4KP9JgvcHvvyt/f5cKzEVjjjfpHZg
+AyjgX3+7/VmtryxYSnbU4f+Ofa8Ofatokdjyc655/19pYozIMIdCv7m0v5/EUQBi
+4ZLXZdasg6/4xHBFua0Cw6i6Z5Jl0xUL2I1WmVj0gpwgaKXmoqVilDBnVwKBgQCv
+NfqXErtiSg8ElM+jPFP6U4RP07qSlcvlNPo+WJvza8qZgl0AH7NVJzjj4rZAMsgv
+DimUYIynBArkw3bAltHMdyXe98l4uhgjriTNw1zLzyYt4u866Lyn2vpqmemaI0oN
+WQhCbREzdQUCAJBAmHnYSj9L+1M3K6IwonKC/cNBUQKBgA+Jgzck+Q+mD/+ZvC1R
+UWQmXG3IIMrpLRb+7eAanEDZX97sprY1E+Z05TbUuseR6IheED46JoviPtRYFRHV
+ZBYcuhOd/BdDF3u38U08EAQkqaZnBzHM+780IphFRr3o/wH2JSwyeilSg8q1/XxO
+VnZNKtdpmc5+EKlg3UhTb+T8
+-----END PRIVATE KEY-----
diff --git a/jstests/libs/server_title_bar.pem.digest.sha1 b/jstests/libs/server_title_bar.pem.digest.sha1
new file mode 100644
index 00000000000..18a85e869e4
--- /dev/null
+++ b/jstests/libs/server_title_bar.pem.digest.sha1
@@ -0,0 +1 @@
+31E9FDDBBAC424AA6377FF410698241361CCDC3F \ No newline at end of file
diff --git a/jstests/libs/server_title_bar.pem.digest.sha256 b/jstests/libs/server_title_bar.pem.digest.sha256
new file mode 100644
index 00000000000..8c5d7afe58c
--- /dev/null
+++ b/jstests/libs/server_title_bar.pem.digest.sha256
@@ -0,0 +1 @@
+2F1C21FFC8FD92864E6E26AC4052087AE9D34133EA1507D22155170F72903237 \ No newline at end of file
diff --git a/jstests/libs/server_title_foo.pem b/jstests/libs/server_title_foo.pem
new file mode 100644
index 00000000000..af938c1c7a8
--- /dev/null
+++ b/jstests/libs/server_title_foo.pem
@@ -0,0 +1,53 @@
+# Autogenerated file, do not edit.
+# Generate using jstests/ssl/x509/mkcert.py --config jstests/ssl/x509/certs.yml server_title_foo.pem
+#
+# Server certificate including the title attribute set to foo.
+-----BEGIN CERTIFICATE-----
+MIIDijCCAnKgAwIBAgIELeq5MTANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV
+UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO
+BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs
+IFRlc3QgQ0EwHhcNMjMwMzIyMDIzODEyWhcNMjUwNjIzMDIzODEyWjB6MQswCQYD
+VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENp
+dHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEPMA0GA1UEAwwG
+c2VydmVyMQwwCgYDVQQMDANmb28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC4Z+4f6WnJJzMqxxkShigpyObbCx0EElyzuSfECotm523C2jpVgplVh5Pn
+eTL6eIUwwNN2d4XHR0VAvvU+tBS+MB42NrZt6MSh+tWCm/HN21/4zg48hdedGFwH
+wDLTN94kRiaChkZ5aNzVqtLa+PtKX6UEYLvIHt+I7Y95hSvc1t1MSaobaEvLRjbU
+fzihRGYYOXeLB0Yw3zurWi7wJ1Z9D8bIYikzgMkn1sPBPTmYHiqQIlxeDmQ5xmNJ
+uRSjK6t16r8SVeNCTS85/pmWuy7hN7YnZXsdGXhP88sZxZOqdjEpsJsj5zGN0Ki0
+KC9NYasht7tZ8dMGmuPjsvo0dwyzAgMBAAGjHjAcMBoGA1UdEQQTMBGCCWxvY2Fs
+aG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEALbGw66c/ZoiuKT2u2i4dTjpV
+L9xceahK9DWGV3syddTPkloER7vpyZzES6TrkC0Kw/3OMnSDaIy1hR3Gp9zCWhDX
+UQLrqh+rnYMEPucG6oWxjPUovfmkWU0zdsTuiXmdJ0eWW/OLe1NPmt6WHlCG2cUl
+BRJR23v2KfRfCL9YaOyLynsY49TXjEELyKD67csA3M6sYKbJ/pseM1TwDqB0Odyz
+CSKDGQx98UsWGS2skuuhPgic8pgJITdp/WfUuI6JyvjpWRuxrHZykJSo38WhS6RG
+rTyj35fDoapyFiJscx0dVrFkTrvptTlLRRxeIDzbZ40wR+EadnJ2/5DB0Nbu0A==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC4Z+4f6WnJJzMq
+xxkShigpyObbCx0EElyzuSfECotm523C2jpVgplVh5PneTL6eIUwwNN2d4XHR0VA
+vvU+tBS+MB42NrZt6MSh+tWCm/HN21/4zg48hdedGFwHwDLTN94kRiaChkZ5aNzV
+qtLa+PtKX6UEYLvIHt+I7Y95hSvc1t1MSaobaEvLRjbUfzihRGYYOXeLB0Yw3zur
+Wi7wJ1Z9D8bIYikzgMkn1sPBPTmYHiqQIlxeDmQ5xmNJuRSjK6t16r8SVeNCTS85
+/pmWuy7hN7YnZXsdGXhP88sZxZOqdjEpsJsj5zGN0Ki0KC9NYasht7tZ8dMGmuPj
+svo0dwyzAgMBAAECggEAGxL3PSwx4dylgIRWxAd6Yhgi/Mn26qAfiCuJERlTOjqE
+PPV5VxCjnpEXQAblWyzSsUO+SEhoFcf6/PSMYTZjTUEXTnJd+mkQZY/ERTbMG6M3
+xfnK0Uv9Sg1HhcPMMoKjVMQP5137ftvMgHpiFtAzZMoCGlBxgYI2442tYPQSaovJ
+DqjPwz+Mn4PEskR0/xamhW+/dStbl2xaG9URPD5Mf2ZhWl8milMJC02Y/Ytm7igq
+AENT06qMcaBtTQZrQubCmWHN+m/cHdGHLlsg5UN4SwsY5OaNrWqMFqrv8ouZEyC0
+4n5+X0kcU9FtXN6LSlLrlANdnAKKY9Sz5NNynlASYQKBgQD1jC33t4GYzqas+hpa
+WGZfLu8aFdcyN5d2sXyMcPKuzULkytyn2GvfGkzcr4ngrewwM4EpotBxFRWN4CUl
+cABzbehwFi0FM9PE/Ww25TALkBbtGmWxSmNuK9uIMwyCClas4T4fV8BS3pCsBC/p
+Jp1QibOvRxtwTr8NRtWEPI9puwKBgQDAQXf0Q6xiBiM7Jwp7UtPEgEwY5aetTuYj
+lLuasXMbAPpAhuZhBGlgpu4Xg6s/HnkQbuYoYcidoMGtTjwTIrnWtDC507kpdzQp
+DkUJPBijiu6OMvQepJIilf45fyHnyDJ1q881PrrOzYikdHth9Ti61BD80YsHFAuF
+51NJHhedaQKBgFTJsN3G6eNACGHWgt8Lg13+sOWLASH//DcqFl3QapxdmGm0evki
+TC1fwYa6vptssw/52PHtnJhPtX+mFG2W/TDelNKPdcBEIy30bDeQcESt3pzE7rSH
+gUn7rvSa3AjTVRahOHhOLsTuwXoEgB68DLpQslEl9p2TM3l8KiJdXxAdAoGAebT4
+SxnMNwHLq9a7O/bjOLI/ekNoMr6P0laFrRhI1f94bQD5NtGkJBuI/jnMXtjbqxuJ
+eGbuqVrrQNsWDMce/lxzvC/cN/POgW8XJRF2R5HcEwkOoZdtK5foqF1jCWgjCXsK
+YZqkh+Z1aiaTNSAYGa3GU0YTzRdTdCFNCCyUpoECgYAXQe6DhbcgRMv23eqml2Bk
+dtcK8q8SJJ0t/onWItcI6CDo91nZSTkQ0A5aGjhUgTrBSnJFmbYjeyuOfqcVxDc6
++I2Yn7ybBjpmZFQciOd0T2a79aZWKicjCM1PjJTQN4ghIt6/f/HtmcHoH4OBXfIp
+zHWq9QZ48v0gu+1NZTx8xg==
+-----END PRIVATE KEY-----
diff --git a/jstests/libs/server_title_foo.pem.digest.sha1 b/jstests/libs/server_title_foo.pem.digest.sha1
new file mode 100644
index 00000000000..1c08ffd82a1
--- /dev/null
+++ b/jstests/libs/server_title_foo.pem.digest.sha1
@@ -0,0 +1 @@
+AE9780F50789327BB1F6AD5343490CC2FDF559FD \ No newline at end of file
diff --git a/jstests/libs/server_title_foo.pem.digest.sha256 b/jstests/libs/server_title_foo.pem.digest.sha256
new file mode 100644
index 00000000000..1b7bfd18f16
--- /dev/null
+++ b/jstests/libs/server_title_foo.pem.digest.sha256
@@ -0,0 +1 @@
+C2D4EE231C2704118F01DCD559987464EFDE8939873595386A8772B6274C70A1 \ No newline at end of file
diff --git a/jstests/libs/server_title_foo_no_o_ou_dc.pem b/jstests/libs/server_title_foo_no_o_ou_dc.pem
new file mode 100644
index 00000000000..87e5d8964ed
--- /dev/null
+++ b/jstests/libs/server_title_foo_no_o_ou_dc.pem
@@ -0,0 +1,53 @@
+# Autogenerated file, do not edit.
+# Generate using jstests/ssl/x509/mkcert.py --config jstests/ssl/x509/certs.yml server_title_foo_no_o_ou_dc.pem
+#
+# Server certificate including the title attribute set to foo without O, OU, or DC.
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAk+gAwIBAgIEPUtD4TANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV
+UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO
+BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs
+IFRlc3QgQ0EwHhcNMjMwMzIyMDIzODQxWhcNMjUwNjIzMDIzODQxWjBXMQ8wDQYD
+VQQDDAZzZXJ2ZXIxDDAKBgNVBAwMA2ZvbzELMAkGA1UEBhMCVVMxETAPBgNVBAgM
+CE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEA/z49ZcRbY9ZWekDxYgPqwlNffxfXWVtKibaO/FtY
+vI2Ey6ngyqTGCvZrJ1MWvKxaKoILrPIhjxGcREW/FQNb2TG/6kpnhbUeoYe0zy1w
+/hxZv9mkSe3xmkxw0V4RmzmKfaxeGcsq5S8eNJ9SVX1CRLgyindO+bwkikzMdL7f
+5VlVx2ry3t1Jnn1ncRAGBV+PgtoVqQgK5IYFONVcOsoaxikSzr5q6WW1NwrUNhOs
+F/76LoTFvu14o/QmzxiXsSMLmdo9f/Ejimf1THOMEahmD2KFUnx0F3EzcY6dholF
+mE1pEmytTN9LlnMK/xt2CsuOtjn7NHznX17GBSuF7LzX3QIDAQABox4wHDAaBgNV
+HREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQELBQADggEBAHPVIMCg
+kmfyKl7Ci5uJS1OCGAdjpaqoSlC2jz7xWOe8P2Hz9gluQNu38EyG9EHM4G1jktCV
+T3KyfaEcQw/4bgz1QlMEio1xPSEsqwMswAzb4cDPbxI3MEyLkx4mIcYZXG614rlm
+ZX6A4UzZ7dIXRPoETnEy6CUDiOBVmlrGfVqv6lqtx63yUSbDKwoF8HVpJxpSjgQt
+qY6AWKHqohmUImwludPlmjxJLh49yJyOMvXHRPr+BMPM/UYKVJ9mx4YmLJxMZMz4
+GSzPKqiJRNczvT1T1qdInUfYa5DtTxYS7NK2ZfvDqtjllTszoUp18shg3P5+tGJa
+2zKmcCXWNlcqkqY=
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD/Pj1lxFtj1lZ6
+QPFiA+rCU19/F9dZW0qJto78W1i8jYTLqeDKpMYK9msnUxa8rFoqggus8iGPEZxE
+Rb8VA1vZMb/qSmeFtR6hh7TPLXD+HFm/2aRJ7fGaTHDRXhGbOYp9rF4ZyyrlLx40
+n1JVfUJEuDKKd075vCSKTMx0vt/lWVXHavLe3UmefWdxEAYFX4+C2hWpCArkhgU4
+1Vw6yhrGKRLOvmrpZbU3CtQ2E6wX/vouhMW+7Xij9CbPGJexIwuZ2j1/8SOKZ/VM
+c4wRqGYPYoVSfHQXcTNxjp2GiUWYTWkSbK1M30uWcwr/G3YKy462Ofs0fOdfXsYF
+K4XsvNfdAgMBAAECggEAKCBhyKDw+SYWHEwfZphVDM3Moo9d9JdMhY/ktLmrnqDk
+8pu3UkRLOif5OopudaTm2+3r5fl+2x4aogURAD2x79hJYozl73hE44IRI8zyCZDt
+byLJGDJHHEnOJqwSOoP2SMGTXZy6FqOsrPsrF3OEuob2sxwEl3BDklZ2ghgL3OM5
+IIVycNo7tEGjrH1p7Z0+5Uuwf3lNZxlItc17bOTRwAi9eVlIMyoLz/ocaJFt5C3Y
+KgCzkQcvWjjJEVwlMe10u9yyjs51yKAqkBfREOVYrYcAQigH1QSKpeLqbVDULMcl
+5CM0e9y1ZDZAeOsRFqCdFMYHVBB/PdlMxP2eM+12JQKBgQD/2SZUC75jF29ekgmx
+FEePQ+LQAlnP2Hplo1TUp6mOIB4n75B/GXbhvh/Aw/bzkeOPSKpZMbOUDcHGDwNu
+ul63BeZWC7hBV53/rJdLEbknafZo3Aw427foNhRscA6iyb8z+QzsgElRuQ2Po/qF
+0vXYxBI48V9ANkEUxCnBjhs8xwKBgQD/ZP+L29//osaTdxyiVwMqmMxw1NOg72eJ
+pE5h5anJ+Zdj+XlE1BOGnpz/J1OMCEmspa0py0zqlvBTuhB5l1AamgeSlqjt8u7a
+T56ariCmwkDCHVRUDevXXAmzdHgp9c5SPNp1Ka7qj5vhTK3YpVGUtzcWRJxqWeCg
+rHYL/Yl6OwKBgHF1U/j7iD+bWekfbCraKm3PFhtWn4t7nbPK/cicXaXIencNVw/2
+M/EiBiTPAom7TaXx/JE3aEKk4yS47bXB8lTJyf6ojdp0R33lhOZmgqyG4h5YTxc7
+4M+ag+4et27bdu5OaLvMnDcgkHH9rxB/oESzlr0n1Sy9opjZ8QaDxXJrAoGAAfDQ
+iE2JbDXecGxtSUaD/aTfmNPlL8nh7YfUGKZYHfLJlbbllwJNi65U3xN7bQr7FFbF
+9BVZZkbzWI+HZIUj1K/q8tA2RGieLAaC3AYKtXmwaEk0xNa+PgqzACwYZak6giF4
+P3+rlpi0xIeCoqzO6+RghMjMr3ozXMUyuHCaxNUCgYEAkas1e5PagZ1u5AjpXtN3
+SI5Wc7IwwtzJf3PCsT3ijYifo1NGG98xM5jJhr+6Sw9QYuocJ1+dY1iHKVdxsBAK
+WN+jJqncuF1EMEDLJpCk//ecLygG4aXnVuT+HGe38+X1SWzpTshP0wmZQeixZOtv
+gRcYsGOG1GGQc7R4PrXooY0=
+-----END PRIVATE KEY-----
diff --git a/jstests/libs/server_title_foo_no_o_ou_dc.pem.digest.sha1 b/jstests/libs/server_title_foo_no_o_ou_dc.pem.digest.sha1
new file mode 100644
index 00000000000..be0670f8b8f
--- /dev/null
+++ b/jstests/libs/server_title_foo_no_o_ou_dc.pem.digest.sha1
@@ -0,0 +1 @@
+0F500F3768A87910EAD0571578AB10A9E39F2122 \ No newline at end of file
diff --git a/jstests/libs/server_title_foo_no_o_ou_dc.pem.digest.sha256 b/jstests/libs/server_title_foo_no_o_ou_dc.pem.digest.sha256
new file mode 100644
index 00000000000..38a0951e2a4
--- /dev/null
+++ b/jstests/libs/server_title_foo_no_o_ou_dc.pem.digest.sha256
@@ -0,0 +1 @@
+602B89632680A18CC323E067301487BB97A7F49CA9180ED116CC75AE06B2DA94 \ No newline at end of file
diff --git a/jstests/ssl/x509/certs.yml b/jstests/ssl/x509/certs.yml
index ebedbaba66d..f91b148a0a3 100644
--- a/jstests/ssl/x509/certs.yml
+++ b/jstests/ssl/x509/certs.yml
@@ -372,6 +372,81 @@ certs:
<<: *server_pem_extensions
mongoClusterMembership: foo
+- name: 'server_title_foo.pem'
+ description: Server certificate including the title attribute set to foo.
+ Subject:
+ CN: 'server'
+ title: 'foo'
+ extensions:
+ subjectAltName:
+ DNS: localhost
+ IP: 127.0.0.1
+
+- name: 'server_title_bar.pem'
+ description: Server certificate including the title attribute set to bar.
+ Subject:
+ CN: 'server'
+ title: 'bar'
+ extensions:
+ subjectAltName:
+ DNS: localhost
+ IP: 127.0.0.1
+
+- name: 'cluster_title_foo.pem'
+ description: >-
+ Alternate certificate for intracluster auth including the title attribute set to foo.
+ Subject:
+ CN: 'clustertest'
+ title: 'foo'
+ extensions:
+ subjectAltName:
+ DNS: localhost
+ IP: 127.0.0.1
+
+- name: 'server_title_foo_no_o_ou_dc.pem'
+ description: Server certificate including the title attribute set to foo without O, OU, or DC.
+ explicit_subject: true
+ Subject:
+ CN: 'server'
+ title: 'foo'
+ C: 'US'
+ ST: 'New York'
+ L: 'New York City'
+ extensions:
+ subjectAltName:
+ DNS: localhost
+ IP: 127.0.0.1
+
+- name: 'server_title_bar_no_o_ou_dc.pem'
+ description: Server certificate including the title attribute set to bar without O, OU, or DC.
+ explicit_subject: true
+ Subject:
+ CN: 'server'
+ title: 'bar'
+ C: 'US'
+ ST: 'New York'
+ L: 'New York City'
+ extensions:
+ subjectAltName:
+ DNS: localhost
+ IP: 127.0.0.1
+
+- name: 'cluster_title_foo_no_o_ou_dc.pem'
+ description: >-
+ Alternate certificate for intracluster auth including the title attribute set to foo
+ without O, OU, or DC.
+ explicit_subject: true
+ Subject:
+ CN: 'clustertest'
+ title: 'foo'
+ C: 'US'
+ ST: 'New York'
+ L: 'New York City'
+ extensions:
+ subjectAltName:
+ DNS: localhost
+ IP: 127.0.0.1
+
# For tenant migration testing.
- name: 'rs0.pem'
description: General purpose server certificate file.
diff --git a/jstests/sslSpecial/cluster_auth_x509_subject_attributes.js b/jstests/sslSpecial/cluster_auth_x509_subject_attributes.js
new file mode 100644
index 00000000000..c473dd4c382
--- /dev/null
+++ b/jstests/sslSpecial/cluster_auth_x509_subject_attributes.js
@@ -0,0 +1,222 @@
+/**
+ * This test checks that tlsClusterAuthX509Attributes can be set in appropriate scenarios
+ * to specify the X.509 subject attributes that should be matched to consider a connectin client
+ * as a peer server node.
+ *
+ * @tags: [featureFlagConfigurableX509ClusterAuthn]
+ */
+
+(function() {
+'use strict';
+
+load('jstests/ssl/libs/ssl_helpers.js');
+
+if (determineSSLProvider() !== "openssl") {
+ print('Skipping test, tlsClusterAuthX509 options are only available with OpenSSL');
+ return;
+}
+
+const clusterMembershipAttributesDN = "title=foo, C=US, ST=New York, L=New York City";
+const clusterMembershipOverrideDN =
+ "C=US, ST=New York, L=New York, O=MongoDB Inc. (Rollover), OU=Kernel (Rollover), CN=server";
+
+/**
+ * Member certificates whose subjects include OU, O and some attributes matched by
+ * tlsClusterAuthX509Attributes.
+ */
+// Subject: CN=server, title=foo, C=US, ST=New York, L=New York City, O=MongoDB, OU=Kernel
+const serverTitleFooCert = 'jstests/libs/server_title_foo.pem';
+// Subject: CN=clusterTest, title=foo, C=US, ST=New York, L=New York City, O=MongoDB, OU=Kernel
+const clusterTitleFooCert = 'jstests/libs/cluster_title_foo.pem';
+
+/**
+ * Member certificates whose subjects do not include DC, OU, or O.
+ */
+// Subject: CN=server, title=foo, C=US, ST=New York, L=New York City
+const serverTitleFooNoDefaultCert = 'jstests/libs/server_title_foo_no_o_ou_dc.pem';
+// Subject: CN=clusterTest, title=foo, C=US, ST=New York, L=New York City
+const clusterTitleFooNoDefaultCert = 'jstests/libs/cluster_title_foo_no_o_ou_dc.pem';
+
+/**
+ * Certificates that will not satisfy clusterMembershipAttributesDN.
+ */
+// Subject: CN=server, title=bar, C=US, ST=New York, L=New York City, O=MongoDB, OU=Kernel
+const serverTitleBarCert = 'jstests/libs/server_title_bar.pem';
+// Subject: CN=server, C=US, ST=New York, L=New York City, O=MongoDB, OU=Kernel
+const serverDefaultOnlyCert = 'jstests/libs/server.pem';
+// Subject: CN=clusterTest, C=US, ST=New York, L=New York City, O=MongoDB, OU=Kernel
+const clusterDefaultOnlyCert = 'jstests/libs/cluster_cert.pem';
+
+const serverCAFile = 'jstests/libs/ca.pem';
+
+function assertNoStart(opts, errmsg) {
+ clearRawMongoProgramOutput();
+ assert.throws(() => MongoRunner.runMongod(opts));
+ assert(rawMongoProgramOutput().includes(errmsg));
+}
+
+function checkInvalidConfigurations() {
+ // Check that the option cannot be set unless clusterAuthMode == 'x509'.
+ const invalidClusterAuthModeOpts = {
+ auth: '',
+ tlsClusterAuthX509Attributes: clusterMembershipAttributesDN
+ };
+ jsTest.log('No clusterAuthMode set');
+ assertNoStart(
+ invalidClusterAuthModeOpts,
+ 'Cannot set clusterAuthX509.attributes when clusterAuthMode does not allow X.509');
+
+ jsTest.log('clusterAuthMode == keyFile');
+ invalidClusterAuthModeOpts.clusterAuthMode = 'keyFile';
+ assertNoStart(
+ invalidClusterAuthModeOpts,
+ 'Cannot set clusterAuthX509.attributes when clusterAuthMode does not allow X.509');
+
+ // Check that the server fails to start if both tlsClusterAuthX509Attributes and
+ // tlsX509ClusterAuthDNOverride are set.
+ const invalidTlsX509ClusterAuthDNOverrideOpts = {
+ auth: '',
+ tlsClusterAuthX509Attributes: clusterMembershipAttributesDN,
+ clusterAuthMode: 'x509',
+ tlsMode: 'preferTLS',
+ setParameter: {
+ tlsX509ClusterAuthDNOverride: clusterMembershipOverrideDN,
+ },
+ tlsCertificateKeyFile: serverTitleFooCert,
+ tlsCAFile: serverCAFile,
+ tlsClusterFile: clusterTitleFooCert,
+ };
+ jsTest.log('tlsX509ClusterAuthDNOverride also set');
+ assertNoStart(
+ invalidTlsX509ClusterAuthDNOverrideOpts,
+ 'tlsClusterAuthX509Attributes and tlsX509ClusterAuthDNOverride cannot both be set at once');
+
+ // Check that the server fails to start if both tlsClusterAuthX509Attributes and
+ // tlsClusterAuthX509ExtensionValue are set.
+ const invalidClusterAuthX509ExtensionValOpts = {
+ auth: '',
+ tlsClusterAuthX509Attributes: clusterMembershipAttributesDN,
+ tlsClusterAuthX509ExtensionValue: 'foo',
+ clusterAuthMode: 'x509',
+ tlsMode: 'preferTLS',
+ tlsCertificateKeyFile: serverTitleFooCert,
+ tlsCAFile: serverCAFile,
+ tlsClusterFile: clusterTitleFooCert,
+ };
+ jsTest.log('tlsClusterAuthX509ExtensionValue also set');
+ assertNoStart(
+ invalidClusterAuthX509ExtensionValOpts,
+ 'net.tls.clusterAuthX509.attributes is not allowed when net.tls.clusterAuthX509.extensionValue is specified');
+
+ // Check that the server fails to start if the provided tlsClusterFile or tlsCertificateKeyFile
+ // do not contain the attributes + values specified by the tlsClusterAuthX509Attributes option.
+ // This ensures consistency between the member certificates provided to cluster nodes and the
+ // attributes they will be matching on.
+ const mismatchedTlsCertificateKeyFileOpts = {
+ auth: '',
+ tlsClusterAuthX509Attributes: clusterMembershipAttributesDN,
+ clusterAuthMode: 'x509',
+ tlsMode: 'preferTLS',
+ tlsCertificateKeyFile: serverDefaultOnlyCert,
+ tlsCAFile: serverCAFile,
+ tlsClusterFile: clusterDefaultOnlyCert,
+ };
+ jsTest.log('Mismatched tlsCertificateKeyFile');
+ assertNoStart(
+ mismatchedTlsCertificateKeyFileOpts,
+ "The server's outgoing certificate's DN does not contain the attributes specified in tlsClusterAuthX509Attributes");
+}
+
+function authX509(expectedUsername, port, clientCertificate) {
+ const evalCmd = String(function doAuthX509(db, authenticatedUsername) {
+ const external = db.getSiblingDB('$external');
+ assert.commandWorked(external.runCommand({authenticate: 1, mechanism: 'MONGODB-X509'}));
+ const connStatus = assert.commandWorked(external.adminCommand({connectionStatus: 1}));
+ assert.eq(connStatus.authInfo.authenticatedUsers[0].user, authenticatedUsername);
+ });
+
+ const shell = runMongoProgram('mongo',
+ '--host',
+ 'localhost',
+ '--port',
+ port,
+ '--tls',
+ '--tlsCAFile',
+ serverCAFile,
+ '--tlsCertificateKeyFile',
+ clientCertificate,
+ '--eval',
+ evalCmd + ` doAuthX509(db, '${expectedUsername}');`);
+ assert.eq(shell, 0);
+}
+
+function runValidMongodTest(opts, allAttrsMatch, wrongAttrValue, missingAttr) {
+ const conn = MongoRunner.runMongod(opts);
+ const admin = conn.getDB('admin');
+ const external = conn.getDB('$external');
+ assert.commandWorked(admin.runCommand({createUser: 'admin', pwd: 'admin', roles: ['root']}));
+ assert(admin.auth('admin', 'admin'));
+
+ // Incoming certificate containing all attributes in tlsClusterAuthX509Attributes should result
+ // in successful auth as __system.
+ authX509(allAttrsMatch.user, conn.port, allAttrsMatch.certificate);
+
+ // Incoming certificate containing all attributes in tlsClusterAuthX509Attributes but wrong
+ // value(s) should fail to auth as __system. After the subject of the cert exists as a user on
+ // $external, it will succeed as that user.
+ assert.throws(() => authX509('__system', conn.port, wrongAttrValue.certificate));
+ assert.commandWorked(external.runCommand({createUser: wrongAttrValue.user, roles: []}));
+ authX509(wrongAttrValue.user, conn.port, wrongAttrValue.certificate);
+
+ // Incoming certificate missing some attributes in tlsClusterAuthX509Attributes
+ // should fail to auth. After the subject of the cert exists as a user on $external, it will
+ // succeed as that user
+ assert.throws(() => authX509('__system', conn.port, missingAttr.certificate));
+ assert.commandWorked(external.runCommand({createUser: missingAttr.user, roles: []}));
+ authX509(missingAttr.user, conn.port, missingAttr.certificate);
+
+ MongoRunner.stopMongod(conn);
+}
+
+checkInvalidConfigurations();
+
+// First, run the tests with a valid set of member certificates that include one of
+// DC, O, and OU but don't rely on them for membership detection.
+let opts = {
+ auth: '',
+ tlsClusterAuthX509Attributes: clusterMembershipAttributesDN,
+ clusterAuthMode: 'x509',
+ tlsMode: 'preferTLS',
+ tlsCertificateKeyFile: serverTitleFooCert,
+ tlsCAFile: serverCAFile,
+ tlsClusterFile: clusterTitleFooCert,
+};
+runValidMongodTest(
+ opts,
+ {user: '__system', certificate: clusterTitleFooCert},
+ {
+ user: 'title=bar,CN=server,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US',
+ certificate: serverTitleBarCert
+ },
+ {
+ user: 'CN=server,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US',
+ certificate: serverDefaultOnlyCert
+ });
+
+// Now, use member certificates that don't have DC, O, or OU at all. This is
+// valid if tlsClusterAuthX509Attributes is configured appropriately to specify
+// attributes and values that the certificates have.
+opts.tlsCertificateKeyFile = serverTitleFooNoDefaultCert;
+opts.tlsClusterFile = clusterTitleFooNoDefaultCert;
+runValidMongodTest(
+ opts,
+ {user: '__system', certificate: clusterTitleFooNoDefaultCert},
+ {
+ user: 'title=bar,CN=server,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US',
+ certificate: serverTitleBarCert
+ },
+ {
+ user: 'CN=server,OU=Kernel,O=MongoDB,L=New York City,ST=New York,C=US',
+ certificate: serverDefaultOnlyCert
+ });
+})();
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