Skip to content

Commit

Permalink
tls: SNI-based cert selection during TLS handshake
Browse files Browse the repository at this point in the history
Envoy supports selecting certs by selecting filter chain based on SNI.
BUt it is possible that we access different services via one filter
chain, which requires SNI-based cert selection in one single filter
chain during handshake.

Signed-off-by: Luyao Zhong <[email protected]>
  • Loading branch information
Luyao Zhong committed Nov 30, 2022
1 parent ac817e4 commit e62edc4
Show file tree
Hide file tree
Showing 40 changed files with 1,264 additions and 43 deletions.
14 changes: 8 additions & 6 deletions api/envoy/extensions/transport_sockets/tls/v3/tls.proto
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ message UpstreamTlsContext {
google.protobuf.UInt32Value max_session_keys = 4;
}

// [#next-free-field: 9]
// [#next-free-field: 10]
message DownstreamTlsContext {
option (udpa.annotations.versioning).previous_message_type =
"envoy.api.v2.auth.DownstreamTlsContext";
Expand Down Expand Up @@ -123,6 +123,11 @@ message DownstreamTlsContext {
// an accompanying OCSP response or if the response expires at runtime.
// Defaults to LENIENT_STAPLING
OcspStaplePolicy ocsp_staple_policy = 8 [(validate.rules).enum = {defined_only: true}];

// Multiple certificates are allowed in Downstream transport socket to serve different SNI.
// If the client provides SNI but no such cert matched, it will decide to full scan certificates or not based on this config.
// Defaults to false. See more details in :ref:`Multiple TLS certificates <arch_overview_ssl_cert_select>`.
google.protobuf.BoolValue full_scan_certs_on_sni_mismatch = 9;
}

// TLS key log configuration.
Expand Down Expand Up @@ -227,12 +232,9 @@ message CommonTlsContext {
// TLS protocol versions, cipher suites etc.
TlsParameters tls_params = 1;

// Only a single TLS certificate is supported in client contexts. In server contexts,
// :ref:`Multiple TLS certificates <arch_overview_ssl_cert_select>` can be associated with the
// same context to allow both RSA and ECDSA certificates.
//
// Only a single TLS certificate is supported in client contexts. In server contexts, the first
// RSA certificate is used for clients that only support RSA and the first ECDSA certificate is
// used for clients that support ECDSA.
// same context to allow both RSA and ECDSA certificates and support SNI-based selection.
//
// Only one of ``tls_certificates``, ``tls_certificate_sds_secret_configs``,
// and ``tls_certificate_provider_instance`` may be used.
Expand Down
7 changes: 6 additions & 1 deletion changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ removed_config_or_runtime:
removed ``envoy.reloadable_features.do_not_await_headers_on_upstream_timeout_to_emit_stats`` and legacy code paths.
new_features:
- area: tls
change: |
added support for SNI-based cert selection in tls downstream transport socket. Detailed documentation is available :ref:`cert selection<arch_overview_ssl_cert_select>`.
New config option :ref:`full_scan_certs_on_sni_mismatch <envoy_v3_api_field_extensions.transport_sockets.tls.v3.DownstreamTlsContext.full_scan_certs_on_sni_mismatch>`
is introduced to disable or enable full scan when no cert matches to SNI, defaults to false.
New runtime flag ``envoy.reloadable_features.no_full_scan_certs_on_sni_mismatch`` can be used for override the default value.
- area: build
change: |
added an option ``--define=library_autolink=disabled`` to disable autolinking libraries.
Expand Down Expand Up @@ -184,7 +190,6 @@ new_features:
- area: http
change: |
added :ref:`append_x_forwarded_port <envoy_v3_api_field_extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.append_x_forwarded_port>` to append the ``x-forwarded-port`` header to HTTP upstream requests.
- area: ext_authz
change: |
added support to allowlist headers included in the check request to gRPC authorization server (previously only available for HTTP authorization server).
Expand Down
51 changes: 40 additions & 11 deletions docs/root/intro/arch_overview/security/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,50 @@ Certificate selection
---------------------

:ref:`DownstreamTlsContexts <envoy_v3_api_msg_extensions.transport_sockets.tls.v3.DownstreamTlsContext>` support multiple TLS
certificates. These may be a mix of RSA and P-256 ECDSA certificates. The following rules apply:
certificates. These may be a mix of RSA and P-256 ECDSA certificates for multiple server name patterns.

* Only one certificate of a particular type (RSA or ECDSA) may be specified.
Certificate config/loading rules:

* DNS SANs or Subject Common Name is extracted as server name pattern to match SNI during handshake. Subject Common Name is not used if DNS SANs are present in the certificate.
* FQDN like "test.example.com" and wildcard like "\*.example.com" are valid at the same time, which will be loaded
as two different server name patterns.
* Only one certificate of a particular type (RSA or ECDSA) may be specified for each server name pattern.
* Non-P-256 server ECDSA certificates are rejected.
* If the client supports P-256 ECDSA, a P-256 ECDSA certificate will be selected if one is present in the
:ref:`DownstreamTlsContext <envoy_v3_api_msg_extensions.transport_sockets.tls.v3.DownstreamTlsContext>`
and it is in compliance with the OCSP policy.
* If the client only supports RSA certificates, a RSA certificate will be selected if present in the
:ref:`DownstreamTlsContext <envoy_v3_api_msg_extensions.transport_sockets.tls.v3.DownstreamTlsContext>`.
* Otherwise, the first certificate listed is used. This will result in a failed handshake if the
client only supports RSA certificates and the server only has ECDSA certificates.
* Static and SDS certificates may not be mixed in a given :ref:`DownstreamTlsContext
<envoy_v3_api_msg_extensions.transport_sockets.tls.v3.DownstreamTlsContext>`.
* The selected certificate must adhere to the OCSP policy. If no
such certificate is found, the connection is refused.

Certificate selection rules:

* If the client supports SNI, e.g. SNI is "test.example.com", it looks for a cert that exactly matches to the SNI.
If the certificate adheres to the OCSP policy and matches to key type, it is selected for handshake.
If the certificate adheres to the OCSP policy, but key type is RSA while client is ECDSA capable, it is marked as
as the candidate and continues searching until a cert is selected with perfect match or certs exhausted.
Candidate will be selected for handshake if there is no perfect match.
* If the client supports SNI, but no cert is selected from certs that exactly matches to SNI, it matches on wildcard server name.
e.g. if SNI is "test.example.com", a certificate with "test.example.com" will be preferred over "\*.example.com". And wildcard
matching only works for 1 level of depth, so "\*.com" will not be a match for "test.example.com".
Afterwards, it execuates OCSP and key type checking on each cert which is the same as what happens after exact SNI matching.
* If no cert is selected from certs that matches wildcard name, the candidate cert is selected for handshake if it is present.
If there is no candidate, check :ref:`full_scan_certs_on_sni_mismatch <envoy_v3_api_field_extensions.transport_sockets.tls.v3.DownstreamTlsContext.full_scan_certs_on_sni_mismatch>`,
go to full scan all certificates if it is enabled, otherwise pick the first certificate for handshake.
* If the client does not provide SNI at all, go to full scan no matter :ref:`full_scan_certs_on_sni_mismatch <envoy_v3_api_field_extensions.transport_sockets.tls.v3.DownstreamTlsContext.full_scan_certs_on_sni_mismatch>`
is false or true.
* Full scan execuates OCSP and key type checking on each cert which is the same as described above in exact SNI matching.
It falls back to the first cert in the whole list if there is no cert selected.
* Currently only two kinds of key type are supported, RSA or ECDSA. If the client supports P-256 ECDSA, the P-256 ECDSA certificate
is preferred over RSA. The certificate that it falls back to might result in a failed handshake. For instance, a client only supports
RSA certificates and the certificate only support ECDSA.
* The final selected certificate must adhere to the OCSP policy. If no such certificate is found, the connection is refused.

.. note::
With the support of SNI-based certificate selection, it allows configuring large number of certificates for multiple hostnames.
:ref:`full_scan_certs_on_sni_mismatch <envoy_v3_api_field_extensions.transport_sockets.tls.v3.DownstreamTlsContext.full_scan_certs_on_sni_mismatch>`
is introduced to determine if we continue full scan on SNI mismatch when the client provides SNI. SNI mismatch contains two cases in this context, one is there is no cert that matches to SNI,
another one is there are certs matches to SNI while OCSP policy fails on those certs. The :ref:`full_scan_certs_on_sni_mismatch <envoy_v3_api_field_extensions.transport_sockets.tls.v3.DownstreamTlsContext.full_scan_certs_on_sni_mismatch>`
defaults to false, so full scan is disabled by default. The runtime flag ``envoy.reloadable_features.no_full_scan_certs_on_sni_mismatch``
can be used to override the default value of :ref:`full_scan_certs_on_sni_mismatch <envoy_v3_api_field_extensions.transport_sockets.tls.v3.DownstreamTlsContext.full_scan_certs_on_sni_mismatch>`.
If full scan is enabled, it will look for the cert from the whole cert list on SNI mismatch, this could be a problem for a potential DoS attack because of O(n) complexity.


Only a single TLS certificate is supported today for :ref:`UpstreamTlsContexts
<envoy_v3_api_msg_extensions.transport_sockets.tls.v3.UpstreamTlsContext>`.
Expand Down
6 changes: 6 additions & 0 deletions envoy/ssl/context_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ class ServerContextConfig : public virtual ContextConfig {
* @return True if stateless TLS session resumption is disabled, false otherwise.
*/
virtual bool disableStatelessSessionResumption() const PURE;

/**
* @return True if we allow full scan certificates when there is no cert matching SNI during
* downstream TLS handshake, false otherwise.
*/
virtual bool fullScanCertsOnSNIMismatch() const PURE;
};

using ServerContextConfigPtr = std::unique_ptr<ServerContextConfig>;
Expand Down
1 change: 1 addition & 0 deletions source/common/runtime/runtime_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ RUNTIME_GUARD(envoy_reloadable_features_local_ratelimit_match_all_descriptors);
RUNTIME_GUARD(envoy_reloadable_features_lua_respond_with_send_local_reply);
RUNTIME_GUARD(envoy_reloadable_features_no_delay_close_for_upgrades);
RUNTIME_GUARD(envoy_reloadable_features_no_extension_lookup_by_name);
RUNTIME_GUARD(envoy_reloadable_features_no_full_scan_certs_on_sni_mismatch);
RUNTIME_GUARD(envoy_reloadable_features_oauth_header_passthrough_fix);
RUNTIME_GUARD(envoy_reloadable_features_original_dst_rely_on_idle_timeout);
RUNTIME_GUARD(envoy_reloadable_features_postpone_h3_client_connect_to_next_loop);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,11 @@ ServerContextConfigImpl::ServerContextConfigImpl(
PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, require_client_certificate, false)),
ocsp_staple_policy_(ocspStaplePolicyFromProto(config.ocsp_staple_policy())),
session_ticket_keys_provider_(getTlsSessionTicketKeysConfigProvider(factory_context, config)),
disable_stateless_session_resumption_(getStatelessSessionResumptionDisabled(config)) {
disable_stateless_session_resumption_(getStatelessSessionResumptionDisabled(config)),
full_scan_certs_on_sni_mismatch_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
config, full_scan_certs_on_sni_mismatch,
!Runtime::runtimeFeatureEnabled(
"envoy.reloadable_features.no_full_scan_certs_on_sni_mismatch"))) {

if (session_ticket_keys_provider_ != nullptr) {
// Validate tls session ticket keys early to reject bad sds updates.
Expand Down
3 changes: 3 additions & 0 deletions source/extensions/transport_sockets/tls/context_config_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ class ServerContextConfigImpl : public ContextConfigImpl, public Envoy::Ssl::Ser
return disable_stateless_session_resumption_;
}

bool fullScanCertsOnSNIMismatch() const override { return full_scan_certs_on_sni_mismatch_; }

private:
static const unsigned DEFAULT_MIN_VERSION;
static const unsigned DEFAULT_MAX_VERSION;
Expand All @@ -191,6 +193,7 @@ class ServerContextConfigImpl : public ContextConfigImpl, public Envoy::Ssl::Ser

absl::optional<std::chrono::seconds> session_timeout_;
const bool disable_stateless_session_resumption_;
bool full_scan_certs_on_sni_mismatch_;
};

} // namespace Tls
Expand Down
Loading

0 comments on commit e62edc4

Please sign in to comment.