-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
tls: enable multiple SSL certificate support. #5317
Conversation
This PR wraps up envoyproxy#1319. The patch enables multiple TLS certificate ingest for upstream TLS contexts, adds related unit and integration tests, docs and release notes. Risk Level: Low Testing: Additional unit and integration tests. To avoid combinatorial explosion, we validate mixed TLS v1.2/1.3 behavior in ssl_integration_test only, and have more targeted certificate selection tests in ssl_socket_Test. Docs Changes: Added to architectural overview of TLS support. Fixes envoyproxy#1319. Signed-off-by: Harvey Tuch <htuch@google.com>
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.
Quick drive by. Very cool to see this implemented!
api/envoy/api/v2/auth/cert.proto
Outdated
// Multiple TLS certificates can be associated with the same context. | ||
// E.g. to allow both RSA and ECDSA certificates, two TLS certificates can be configured. | ||
// :ref:`Multiple TLS certificates <arch_overview_ssl_multi_cert>` can be associated with the same | ||
// context.B E.g. to allow both RSA and ECDSA certificates, two TLS certificates can be |
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.
typo ".B"
Multiple certificates | ||
--------------------- | ||
|
||
:ref:`UpstreamTlsContexts <envoy_api_msg_auth.UpstreamTlsContext>` support multiple TLS |
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.
I think it's the reverse? We support multiple contexts for downstream but not for upstream?
--------------------- | ||
|
||
:ref:`UpstreamTlsContexts <envoy_api_msg_auth.UpstreamTlsContext>` support multiple TLS | ||
certificates. These may be a mix of RSA and P-256 ECDSA certificates. The following rules apply: |
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.
I believe that I asked about this before, but could we enforce that at most single RSA and single P-256 ECDSA are configured? We know that anything more is never going to be used at runtime, so it's most likely misconfiguration. This should only require a few easy checks in ContextImpl()
and ServerContextConfigImpl()
.
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.
Yes, OK, let's go with this, it simplifies other logic later on.
certificates. These may be a mix of RSA and P-256 ECDSA certificates. The following rules apply: | ||
|
||
* Only the first certificate of a particular type (RSA or ECDSA) is considered. | ||
* Non-P256 server ECDSA certificates are rejected. |
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.
This isn't specific to multiple certificates.
(We should have it documented somewhere, but probably not here.)
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.
I will rename this section to something like "Certificate selection" and we can keep it here.
|
||
* Only the first certificate of a particular type (RSA or ECDSA) is considered. | ||
* Non-P256 server ECDSA certificates are rejected. | ||
* The client must indicate P-256 support to be considered ECDSA capable. |
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.
This line is redundant with the more descriptive line below.
api/envoy/api/v2/auth/cert.proto
Outdated
// Multiple TLS certificates can be associated with the same context. | ||
// E.g. to allow both RSA and ECDSA certificates, two TLS certificates can be configured. | ||
// :ref:`Multiple TLS certificates <arch_overview_ssl_multi_cert>` can be associated with the same | ||
// context.B E.g. to allow both RSA and ECDSA certificates, two TLS certificates can be |
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/B E.g./, e.g./
, but I'd probably rephrase the whole thing as:
// :ref:`Multiple TLS certificates <arch_overview_ssl_multi_cert> can be associated with the same
// context to allow both RSA and ECDSA certificates.
docs/root/intro/version_history.rst
Outdated
@@ -78,6 +78,7 @@ Version history | |||
* tls: added support for CRLs in :ref:`trusted_ca <envoy_api_field_auth.CertificateValidationContext.trusted_ca>`. | |||
* tls: added support for :ref:`password encrypted private keys <envoy_api_field_auth.TlsCertificate.password>`. | |||
* tls: added ssl.versions.<version> to :ref:`listener metrics <config_listener_stats>` to track TLS versions in use. | |||
* tls: added support for :ref:`multiple server TLS certificates <arch_overview_ssl_multi_cert>`. |
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.
Nit: move it above ssl.versions
.
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.
I think ssl.versions
should be moved above the other to enforce alpha order.
if ((config.common_tls_context().tls_certificates().size() + | ||
config.common_tls_context().tls_certificate_sds_secret_configs().size()) == 0) { | ||
throw EnvoyException("No TLS certificates found for server context"); | ||
} else if ((config.common_tls_context().tls_certificates().size() + | ||
config.common_tls_context().tls_certificate_sds_secret_configs().size()) > 1) { |
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.
As far as I recall, we still only support single certificate served over SDS, don't we?
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.
I think you may have multiple sds config for single cluster/listener? https://github.com/envoyproxy/envoy/blob/master/api/envoy/api/v2/auth/cert.proto#L239
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.
But it doesn't work with multiple certificates, because callback clears all other certificates on update, see:
// This breaks multiple certificate support, but today SDS is only single cert. |
test/common/ssl/context_impl_test.cc
Outdated
ASSERT_EQ(3, tls_certs.size()); | ||
EXPECT_THAT(tls_certs[0].get().privateKeyPath(), EndsWith("selfsigned_key.pem")); | ||
EXPECT_THAT(tls_certs[1].get().privateKeyPath(), EndsWith("selfsigned_key_ecdsa_p256.pem")); | ||
EXPECT_THAT(tls_certs[2].get().privateKeyPath(), EndsWith("selfsigned_key.pem")); |
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.
Erm, we really shouldn't be able to load the same certificate twice.
Multiple certificates | ||
--------------------- | ||
|
||
:ref:`UpstreamTlsContexts <envoy_api_msg_auth.UpstreamTlsContext>` support multiple TLS |
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.
Downstream contexts support multiple TLS certificates, not upstream.
* The client must indicate P-256 support to be considered ECDSA capable. | ||
* If the client supports P-256 ECDSA, a P-256 ECDSA certificate will be selected if present in the | ||
:ref:`UpstreamTlsContext <envoy_api_msg_auth.UpstreamTlsContext>`. | ||
* If the client only supports RSA certificate, an RSA certificate will be selected if present in the |
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/certificate/certificates/
, s/an/a/
(?)
🙀 Error while processing event:
|
Signed-off-by: Harvey Tuch <htuch@google.com>
Signed-off-by: Harvey Tuch <htuch@google.com>
Signed-off-by: Harvey Tuch <htuch@google.com>
throw EnvoyException("A single TLS certificate is required for server contexts"); | ||
} else if (!config.common_tls_context().tls_certificates().empty() && | ||
!config.common_tls_context().tls_certificate_sds_secret_configs().empty()) { | ||
throw EnvoyException("Static and dynamic TLS certificates may not be mixed in server contexts"); |
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.
Nit: this isn't really "static" and "dynamic", since LDS/CDS and SDS can be both "static" and "dynamic".
Maybe just say that (i.e. LDS/CDS and SDS TLS certificates may not be mixed in server contexts
) or is it too nitty-gritty detail for operators?
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.
I think "Non-SDS and SDS TLS certificates may not be mixed.." is clearer here, as LDS/CDS doesn't refer to bootstrap static config.
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.
secrets (i.e. "static" SDS) can be also in the bootstrap static config.
But yeah, I agree that it sounds cleaner.
source/common/ssl/context_impl.cc
Outdated
@@ -259,6 +267,12 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const ContextConfig& config, TimeS | |||
"ECDSA certificates are supported", | |||
ctx.cert_chain_file_path_)); | |||
} | |||
} else if (have_rsa_cert) { |
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.
There are other certificate types (though, they are not very popular), but this code path assumes that !ECDSA == RSA, so it would potentially reject those.
test/common/ssl/ssl_socket_test.cc
Outdated
)EOF"; | ||
|
||
EXPECT_THROW_WITH_REGEX(testUtil(client_ctx_yaml, server_ctx_yaml, "", "", "", "", "", "", "", | ||
"ssl.no_certificate", true, GetParam()), |
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.
The stat and connected status seem bogus here, since this will throw before reaching that stage.
Maybe consider moving those "bad configuration" tests to test/common/ssl/context_impl_test.cc
, where we usually test them?
test/common/ssl/ssl_socket_test.cc
Outdated
)EOF"; | ||
|
||
EXPECT_THROW_WITH_REGEX(testUtil(client_ctx_yaml, server_ctx_yaml, "", "", "", "", "", "", "", | ||
"ssl.no_certificate", true, GetParam()), |
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.
Same here.
test/common/ssl/ssl_socket_test.cc
Outdated
filename: "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_ecdsa_p256_key.pem" | ||
)EOF"; | ||
|
||
testUtil(client_ctx_yaml, server_ctx_yaml, "", "", "", "", "", "", "", "ssl.no_certificate", true, |
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.
Could you test a more "happy" stat, e.g. ssl.handshake
?
test/common/ssl/test_data/certs.sh
Outdated
echo "constexpr char TEST_$(echo $1 | tr a-z A-Z)_CERT_SERIAL[] = \"$(openssl x509 -in $1_cert.pem -noout -serial | cut -d"=" -f2 | awk '{print tolower($0)}')\";" >> $1_cert_info.h | ||
echo "constexpr char TEST_$(echo $1 | tr a-z A-Z)_CERT_NOT_BEFORE[] = \"$(openssl x509 -in $1_cert.pem -noout -startdate | cut -d"=" -f2)\";" >> $1_cert_info.h | ||
echo "constexpr char TEST_$(echo $1 | tr a-z A-Z)_CERT_NOT_AFTER[] = \"$(openssl x509 -in $1_cert.pem -noout -enddate | cut -d"=" -f2)\";" >> $1_cert_info.h | ||
generate_info_header $1 | ||
} | ||
|
||
# $1=<certificate name> |
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.
Nit: add $2=[certificate file name]
(?).
Signed-off-by: Harvey Tuch <htuch@google.com>
Signed-off-by: Harvey Tuch <htuch@google.com>
Signed-off-by: Harvey Tuch <htuch@google.com>
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.
LGTM, other than the 2 typos and too many test certs, but feel free to ignore it if you disagree with my comment.
@@ -41,6 +41,11 @@ rm -f server_ecdsacert.cfg | |||
# Generate cert for the client. | |||
generate_rsa_key client ca | |||
generate_x509_cert client ca | |||
# Generate ECDSA cert for the celitn. |
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/celint/client/
source/common/ssl/context_impl.cc
Outdated
ctx.is_ecdsa_ = EVP_PKEY_id(public_key.get()) == EVP_PKEY_EC; | ||
const int pkey_id = EVP_PKEY_id(public_key.get()); | ||
if (!cert_pkey_ids.insert(pkey_id).second) { | ||
throw EnvoyException(fmt::format("Failed to load certificate from chain {}, at most one " |
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/certificate from chain/certificate chain from/
private_key: | ||
filename: "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_key.pem" | ||
- certificate_chain: | ||
filename: "{{ test_rundir }}/test/common/ssl/test_data/selfsigned2_cert.pem" |
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.
Could you re-use existing certs (selfsigned
& selfsigned_rsa_1024
* and selfsigned_ecdsa_p256
and selfsigned_ecdsa_p384
) instead of adding another set? We have a bit too many test certs already, and we could get rid of the OUTPUT_PREFIX
hack in certs.sh
, then.
*either copy it from #5318 or wait until it's merged (later today?)
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.
I think if we did that, it might work, because we check for duplicate certs of a given type before validating the cert, but this is just an implementation detail which could potentially change so let's stick with this for now.
Long term, I think the right direction to go here is to have some custom Go/Python script for programatically generating the certs (since we can't rely on the openssl CLI in any Bazel automation), so we don't need to check in so many certs.
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.
Long term, I think the right direction to go here is to have some custom Go/Python script for programatically generating the certs (since we can't rely on the openssl CLI in any Bazel automation), so we don't need to check in so many certs.
We used to do openssl in genrule and that broke once in OSX, but I think we may enable that again by using bssl binary?
Signed-off-by: Harvey Tuch <htuch@google.com>
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.
Thanks!
This PR wraps up envoyproxy#1319. The patch enables multiple TLS certificate ingest for downstream TLS contexts, adds related unit and integration tests, docs and release notes. Risk Level: Low Testing: Additional unit and integration tests. To avoid combinatorial explosion, we validate mixed TLS v1.2/1.3 behavior in ssl_integration_test only, and have more targeted certificate selection tests in ssl_socket_Test. Docs Changes: Added to architectural overview of TLS support. Fixes envoyproxy#1319. Signed-off-by: Harvey Tuch <htuch@google.com> Signed-off-by: Fred Douglas <fredlas@google.com>
This PR wraps up #1319. The patch enables multiple TLS certificate
ingest for downstream TLS contexts, adds related unit and integration
tests, docs and release notes.
Risk Level: Low
Testing: Additional unit and integration tests. To avoid combinatorial
explosion, we validate mixed TLS v1.2/1.3 behavior in
ssl_integration_test only, and have more targeted certificate
selection tests in ssl_socket_Test.
Docs Changes: Added to architectural overview of TLS support.
Fixes #1319.
Signed-off-by: Harvey Tuch htuch@google.com