-
Notifications
You must be signed in to change notification settings - Fork 5.5k
ssl: support wildcard matching for verify_subject_alt_name #1298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
3ed769d
bef14f1
e4b97c7
ad7d440
c878f8b
9347913
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,7 +65,9 @@ verify_certificate_hash | |
|
|
||
| verify_subject_alt_name | ||
| *(optional, array)* An optional list of subject alt names. If specified, Envoy will verify | ||
| that the client certificate's subject alt name matches one of the specified values. | ||
| that the client certificate's subject alt name matches one of the specified values. The names | ||
| support wildcard in the end. For example, ``foo.com/*`` will match certificate with subject alt | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/in the end/at the end/. |
||
| name ``foo.com/bar``. | ||
|
|
||
| cipher_suites | ||
| *(optional, string)* If specified, the TLS listener will only support the specified `cipher list | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -209,7 +209,7 @@ bool ContextImpl::verifySubjectAltName(X509* cert, | |
| ASN1_STRING* str = altname->d.uniformResourceIdentifier; | ||
| char* crt_san = reinterpret_cast<char*>(ASN1_STRING_data(str)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It just occurred to me that there is and then we could compare strings only if Sorry for not catching this yesterday!
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll fix this along with the problem mentioned below.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a side question though, if we use
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that is correct. Optimally, You would just pass char* and length to the function, then still use strncmp (or compare if you can). At this point though let's just get something working that everyone agrees with. :)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cool, I'll stick to the current way then
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...it is. Feel free to ignore this. Alternatively, you could use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. right. I'll pass |
||
| for (auto& config_san : subject_alt_names) { | ||
| if (config_san.compare(crt_san) == 0) { | ||
| if (uriMatch(config_san, crt_san)) { | ||
| verified = true; | ||
| break; | ||
| } | ||
|
|
@@ -223,6 +223,15 @@ bool ContextImpl::verifySubjectAltName(X509* cert, | |
| return verified; | ||
| } | ||
|
|
||
| bool ContextImpl::uriMatch(const std::string& uriPattern, const char* uri) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This whole function is low-level C instead of C++11. Please consider using
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes that would be much better. :)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused but
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should use Previously, you were using
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it. Since my original
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Maybe move this function after dNSNameMatch, to be consistent with the order in the .h file. |
||
| size_t pattern_len = uriPattern.length(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
| if (pattern_len > 1 && uriPattern[pattern_len - 1] == '*' && uriPattern[pattern_len - 2] == '/') { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps would be more readable?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agree |
||
| return uriPattern.compare(0, pattern_len - 1, uri, pattern_len - 1) == 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you will need to use strncmp() here since IIRC compare will go off end of string. (See ASAN failure).
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's because the wrong method is used here. It should be: for NULL-terminated string, instead of: which specifies length of the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the problem here is, if
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. from doc:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sigh, indeed. What a terrible API.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll use strncmp() then. |
||
| } | ||
|
|
||
| return uriPattern == uri; | ||
| } | ||
|
|
||
| bool ContextImpl::dNSNameMatch(const std::string& dNSName, const char* pattern) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry to bring this up ... I feel like how this function is called (line 203 in context_impl.cc) is not right. It should be: It makes sense to me that we support wildcard in config, not in certificates. Unfortunately I didn't notice this when I modified this function. This was unnoticed because we don't have integration tests, nor did we tell users this wildcard is supported.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @myidpt yes that make sense to me. Can we fix?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @myidpt @mattklein123 I can fix this and update the doc. So the SAN field, whether it's URI or DNS, should never contain a wildcard; and we allow configs to optionally use wildcard to match multiple SAN values.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you very much. Please also make sure the function signature (parameter naming&order) aligns with the uriMatch function :) |
||
| if (dNSName == pattern) { | ||
| return true; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,6 +68,14 @@ class ContextImpl : public virtual Context { | |
| */ | ||
| static bool dNSNameMatch(const std::string& dnsName, const char* pattern); | ||
|
|
||
| /** | ||
| * Determines whether the given URI matches 'pattern' which may end with a wildcard. | ||
| * @param uriPattern the pattern to match against (foo.bar/baz/ *) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove space and perhaps replace this with the same example as in docs (i.e.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just remove the example, then. |
||
| * @param uri the URI to match | ||
| * @return true if uri matches pattern | ||
| */ | ||
| static bool uriMatch(const std::string& uriPattern, const char* uri); | ||
|
|
||
| SslStats& stats() { return stats_; } | ||
|
|
||
| // Ssl::Context | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,20 @@ TEST_F(SslContextImplTest, TestdNSNameMatching) { | |
| EXPECT_FALSE(ContextImpl::dNSNameMatch("lyft.com", "")); | ||
| } | ||
|
|
||
| TEST_F(SslContextImplTest, TestURIMatch) { | ||
| EXPECT_TRUE(ContextImpl::uriMatch("spiffe://lyft.com/foo", "spiffe://lyft.com/foo")); | ||
| EXPECT_TRUE(ContextImpl::uriMatch("spiffe://lyft.com/*", "spiffe://lyft.com/foo")); | ||
| EXPECT_TRUE(ContextImpl::uriMatch("spiffe://lyft.com/*", "spiffe://lyft.com/")); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if this should pass (logically, not according to the code).
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not? ;) the first case fall back to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only meant the last one, i.e.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see... I'll make this a false case |
||
| EXPECT_TRUE(ContextImpl::uriMatch("spiffe://lyft.com/foo/*", "spiffe://lyft.com/foo/bar")); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you also add test for:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will add |
||
| EXPECT_FALSE(ContextImpl::uriMatch("spiffe://lyft.com/foo", "spiffe://lyft.com/foo/bar")); | ||
| EXPECT_FALSE(ContextImpl::uriMatch("spiffe://lyft.com/*", "")); | ||
| EXPECT_FALSE(ContextImpl::uriMatch("spiffe://lyft.com/*", "spiffe://lyft.net/foo")); | ||
| EXPECT_FALSE(ContextImpl::uriMatch("spiffe://lyft.com*", "spiffe://lyft.com/foo")); | ||
| EXPECT_FALSE(ContextImpl::uriMatch("spiffe://lyft.com/*", "spiffe://lyft.comfoo")); | ||
| EXPECT_FALSE(ContextImpl::uriMatch("*", "foo")); | ||
| EXPECT_FALSE(ContextImpl::uriMatch("f", "foo")); | ||
| } | ||
|
|
||
| TEST_F(SslContextImplTest, TestVerifySubjectAltNameDNSMatched) { | ||
| FILE* fp = fopen( | ||
| TestEnvironment::runfilesPath("test/common/ssl/test_data/san_dns_cert.pem").c_str(), "r"); | ||
|
|
@@ -52,6 +66,11 @@ TEST_F(SslContextImplTest, TestVerifySubjectAltNameURIMatched) { | |
| std::vector<std::string> verify_subject_alt_name_list = {"spiffe://lyft.com/fake-team", | ||
| "spiffe://lyft.com/test-team"}; | ||
| EXPECT_TRUE(ContextImpl::verifySubjectAltName(cert, verify_subject_alt_name_list)); | ||
|
|
||
| verify_subject_alt_name_list.clear(); | ||
| verify_subject_alt_name_list.push_back("spiffe://lyft.com/*"); | ||
| EXPECT_TRUE(ContextImpl::verifySubjectAltName(cert, verify_subject_alt_name_list)); | ||
|
|
||
| X509_free(cert); | ||
| fclose(fp); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/in the end/at the end/.