diff --git a/api/envoy/config/rbac/v2/rbac.proto b/api/envoy/config/rbac/v2/rbac.proto index 3cfe43a828fe..77e1aa687fa9 100644 --- a/api/envoy/config/rbac/v2/rbac.proto +++ b/api/envoy/config/rbac/v2/rbac.proto @@ -170,8 +170,9 @@ message Principal { reserved 1; reserved "name"; - // The name of the principal. If set, The URI SAN is used from the certificate, otherwise the - // subject field is used. If unset, it applies to any user that is authenticated. + // The name of the principal. If set, The URI SAN or DNS SAN in that order is used from the + // certificate, otherwise the subject field is used. If unset, it applies to any user that is + // authenticated. envoy.type.matcher.StringMatcher principal_name = 2; } diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index 4d5123dd04bb..7a1245ae965c 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -18,6 +18,7 @@ Version history * http: added the ability to reject HTTP/1.1 requests with invalid HTTP header values, using the runtime feature `envoy.reloadable_features.strict_header_validation`. * http: added the ability to :ref:`merge adjacent slashes` in the path. * listeners: added :ref:`HTTP inspector listener filter `. +* rbac: added support for DNS SAN as :ref:`principal_name `. * router: added :ref:`rq_retry_skipped_request_not_complete ` counter stat to router stats. * router check tool: add coverage reporting & enforcement. * tls: added verification of IP address SAN fields in certificates against configured SANs in the diff --git a/source/extensions/filters/common/rbac/matchers.cc b/source/extensions/filters/common/rbac/matchers.cc index d718e8b34167..07472b49a003 100644 --- a/source/extensions/filters/common/rbac/matchers.cc +++ b/source/extensions/filters/common/rbac/matchers.cc @@ -142,10 +142,17 @@ bool AuthenticatedMatcher::matches(const Network::Connection& connection, const auto uriSans = ssl->uriSanPeerCertificate(); std::string principal; - if (uriSans.empty()) { - principal = ssl->subjectPeerCertificate(); - } else { + // If set, The URI SAN or DNS SAN in that order is used as Principal, otherwise the subject field + // is used. + if (!uriSans.empty()) { principal = uriSans[0]; + } else { + const auto dnsSans = ssl->dnsSansPeerCertificate(); + if (!dnsSans.empty()) { + principal = dnsSans[0]; + } else { + principal = ssl->subjectPeerCertificate(); + } } return matcher_.value().match(principal); diff --git a/test/extensions/filters/common/rbac/matchers_test.cc b/test/extensions/filters/common/rbac/matchers_test.cc index 424c1c56a74e..1262d8281550 100644 --- a/test/extensions/filters/common/rbac/matchers_test.cc +++ b/test/extensions/filters/common/rbac/matchers_test.cc @@ -205,12 +205,35 @@ TEST(AuthenticatedMatcher, uriSanPeerCertificate) { checkMatcher(AuthenticatedMatcher(auth), false, conn); } +TEST(AuthenticatedMatcher, dnsSanPeerCertificate) { + Envoy::Network::MockConnection conn; + Envoy::Ssl::MockConnectionInfo ssl; + + const std::vector uri_sans; + const std::vector dns_sans{"foo", "baz"}; + + EXPECT_CALL(ssl, uriSanPeerCertificate()).WillRepeatedly(Return(uri_sans)); + EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(&ssl)); + + EXPECT_CALL(ssl, dnsSansPeerCertificate()).WillRepeatedly(Return(dns_sans)); + EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(&ssl)); + + // We should get the first DNS SAN as URI SAN is not available. + envoy::config::rbac::v2::Principal_Authenticated auth; + auth.mutable_principal_name()->set_exact("foo"); + checkMatcher(AuthenticatedMatcher(auth), true, conn); + + auth.mutable_principal_name()->set_exact("bar"); + checkMatcher(AuthenticatedMatcher(auth), false, conn); +} + TEST(AuthenticatedMatcher, subjectPeerCertificate) { Envoy::Network::MockConnection conn; Envoy::Ssl::MockConnectionInfo ssl; const std::vector sans; EXPECT_CALL(ssl, uriSanPeerCertificate()).WillRepeatedly(Return(sans)); + EXPECT_CALL(ssl, dnsSansPeerCertificate()).WillRepeatedly(Return(sans)); EXPECT_CALL(ssl, subjectPeerCertificate()).WillRepeatedly(Return("bar")); EXPECT_CALL(Const(conn), ssl()).WillRepeatedly(Return(&ssl));