diff --git a/api/envoy/extensions/transport_sockets/tls/v3/tls.proto b/api/envoy/extensions/transport_sockets/tls/v3/tls.proto index ac3641ebd6f5..f94889cfad04 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/tls.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/tls.proto @@ -63,7 +63,7 @@ message UpstreamTlsContext { google.protobuf.BoolValue enforce_rsa_key_usage = 5; } -// [#next-free-field: 10] +// [#next-free-field: 11] message DownstreamTlsContext { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.auth.DownstreamTlsContext"; @@ -119,6 +119,10 @@ message DownstreamTlsContext { bool disable_stateless_session_resumption = 7; } + // If set to true, the TLS server will not maintain a session cache of TLS sessions. (This is + // relevant only for TLSv1.2 and earlier.) + bool disable_stateful_session_resumption = 10; + // If specified, ``session_timeout`` will change the maximum lifetime (in seconds) of the TLS session. // Currently this value is used as a hint for the `TLS session ticket lifetime (for TLSv1.2) `_. // Only seconds can be specified (fractional seconds are ignored). diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 964a4cd719cb..07963281100e 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -238,6 +238,11 @@ new_features: change: | added :ref:`custom_sink ` type to enable writing tap data out to a custom sink extension. +- area: tls + change: | + added :ref:`disable_stateful_session_resumption + ` config option to + disable stateful TLS session resumption. - area: udp_proxy change: | added :ref:`session_filters ` config to diff --git a/envoy/ssl/context_config.h b/envoy/ssl/context_config.h index 68baffc37c7f..596c70df2fea 100644 --- a/envoy/ssl/context_config.h +++ b/envoy/ssl/context_config.h @@ -189,6 +189,11 @@ class ServerContextConfig : public virtual ContextConfig { */ virtual bool disableStatelessSessionResumption() const PURE; + /** + * @return True if stateful TLS session resumption is disabled, false otherwise. + */ + virtual bool disableStatefulSessionResumption() const PURE; + /** * @return True if we allow full scan certificates when there is no cert matching SNI during * downstream TLS handshake, false otherwise. diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index 76578b62b6fe..2fdf9c5ba8ab 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -401,6 +401,7 @@ ServerContextConfigImpl::ServerContextConfigImpl( ocsp_staple_policy_(ocspStaplePolicyFromProto(config.ocsp_staple_policy())), session_ticket_keys_provider_(getTlsSessionTicketKeysConfigProvider(factory_context, config)), disable_stateless_session_resumption_(getStatelessSessionResumptionDisabled(config)), + disable_stateful_session_resumption_(config.disable_stateful_session_resumption()), full_scan_certs_on_sni_mismatch_(PROTOBUF_GET_WRAPPED_OR_DEFAULT( config, full_scan_certs_on_sni_mismatch, !Runtime::runtimeFeatureEnabled( diff --git a/source/extensions/transport_sockets/tls/context_config_impl.h b/source/extensions/transport_sockets/tls/context_config_impl.h index 3cb896fbbeb5..25ba093be9d7 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.h +++ b/source/extensions/transport_sockets/tls/context_config_impl.h @@ -165,6 +165,9 @@ class ServerContextConfigImpl : public ContextConfigImpl, public Envoy::Ssl::Ser bool disableStatelessSessionResumption() const override { return disable_stateless_session_resumption_; } + bool disableStatefulSessionResumption() const override { + return disable_stateful_session_resumption_; + } bool fullScanCertsOnSNIMismatch() const override { return full_scan_certs_on_sni_mismatch_; } @@ -190,6 +193,7 @@ class ServerContextConfigImpl : public ContextConfigImpl, public Envoy::Ssl::Ser absl::optional session_timeout_; const bool disable_stateless_session_resumption_; + const bool disable_stateful_session_resumption_; bool full_scan_certs_on_sni_mismatch_; }; diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index d68219c23ef6..9ffb716f17a5 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -854,6 +854,10 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, }); } + if (config.disableStatefulSessionResumption()) { + SSL_CTX_set_session_cache_mode(ctx.ssl_ctx_.get(), SSL_SESS_CACHE_OFF); + } + if (config.sessionTimeout() && !config.capabilities().handles_session_resumption) { auto timeout = config.sessionTimeout().value().count(); SSL_CTX_set_timeout(ctx.ssl_ctx_.get(), uint32_t(timeout)); diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index f30a49954bda..14fec9379f89 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -3618,10 +3618,10 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, EXPECT_EQ(expect_reuse ? 1UL : 0UL, client_stats_store.counter("ssl.session_reused").value()); } -void testSupportForStatelessSessionResumption(const std::string& server_ctx_yaml, - const std::string& client_ctx_yaml, - bool expect_support, - const Network::Address::IpVersion ip_version) { +void testSupportForSessionResumption(const std::string& server_ctx_yaml, + const std::string& client_ctx_yaml, bool expect_stateless, + bool expect_stateful, + const Network::Address::IpVersion ip_version) { Event::SimulatedTimeSystem time_system; ContextManagerImpl manager(*time_system); @@ -3681,11 +3681,17 @@ void testSupportForStatelessSessionResumption(const std::string& server_ctx_yaml dynamic_cast(server_connection->ssl().get()); SSL* server_ssl_socket = ssl_socket->ssl(); SSL_CTX* server_ssl_context = SSL_get_SSL_CTX(server_ssl_socket); - if (expect_support) { + if (expect_stateless) { EXPECT_EQ(0, (SSL_CTX_get_options(server_ssl_context) & SSL_OP_NO_TICKET)); } else { EXPECT_EQ(SSL_OP_NO_TICKET, (SSL_CTX_get_options(server_ssl_context) & SSL_OP_NO_TICKET)); } + if (expect_stateful) { + EXPECT_EQ(SSL_SESS_CACHE_SERVER, + (SSL_CTX_get_session_cache_mode(server_ssl_context) & SSL_SESS_CACHE_SERVER)); + } else { + EXPECT_EQ(SSL_SESS_CACHE_OFF, SSL_CTX_get_session_cache_mode(server_ssl_context)); + } })); EXPECT_CALL(callbacks, recordConnectionsAcceptedOnSocketEvent(_)); @@ -4144,6 +4150,25 @@ TEST_P(SslSocketTest, TicketSessionResumptionDifferentServerCertDifferentSAN) { version_); } +TEST_P(SslSocketTest, SessionResumptionDisabled) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_key.pem" + disable_stateless_session_resumption: true + disable_stateful_session_resumption: true +)EOF"; + + const std::string client_ctx_yaml = R"EOF( + common_tls_context: + )EOF"; + + testSupportForSessionResumption(server_ctx_yaml, client_ctx_yaml, false, false, version_); +} + TEST_P(SslSocketTest, StatelessSessionResumptionDisabled) { const std::string server_ctx_yaml = R"EOF( common_tls_context: @@ -4159,10 +4184,28 @@ TEST_P(SslSocketTest, StatelessSessionResumptionDisabled) { common_tls_context: )EOF"; - testSupportForStatelessSessionResumption(server_ctx_yaml, client_ctx_yaml, false, version_); + testSupportForSessionResumption(server_ctx_yaml, client_ctx_yaml, false, true, version_); +} + +TEST_P(SslSocketTest, StatefulSessionResumptionDisabled) { + const std::string server_ctx_yaml = R"EOF( + common_tls_context: + tls_certificates: + certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_key.pem" + disable_stateful_session_resumption: true +)EOF"; + + const std::string client_ctx_yaml = R"EOF( + common_tls_context: + )EOF"; + + testSupportForSessionResumption(server_ctx_yaml, client_ctx_yaml, true, false, version_); } -TEST_P(SslSocketTest, SatelessSessionResumptionEnabledExplicitly) { +TEST_P(SslSocketTest, SessionResumptionEnabledExplicitly) { const std::string server_ctx_yaml = R"EOF( common_tls_context: tls_certificates: @@ -4171,16 +4214,17 @@ TEST_P(SslSocketTest, SatelessSessionResumptionEnabledExplicitly) { private_key: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/unittest_key.pem" disable_stateless_session_resumption: false + disable_stateful_session_resumption: false )EOF"; const std::string client_ctx_yaml = R"EOF( common_tls_context: )EOF"; - testSupportForStatelessSessionResumption(server_ctx_yaml, client_ctx_yaml, true, version_); + testSupportForSessionResumption(server_ctx_yaml, client_ctx_yaml, true, true, version_); } -TEST_P(SslSocketTest, StatelessSessionResumptionEnabledByDefault) { +TEST_P(SslSocketTest, SessionResumptionEnabledByDefault) { const std::string server_ctx_yaml = R"EOF( common_tls_context: tls_certificates: @@ -4194,7 +4238,7 @@ TEST_P(SslSocketTest, StatelessSessionResumptionEnabledByDefault) { common_tls_context: )EOF"; - testSupportForStatelessSessionResumption(server_ctx_yaml, client_ctx_yaml, true, version_); + testSupportForSessionResumption(server_ctx_yaml, client_ctx_yaml, true, true, version_); } // Test that if two listeners use the same cert and session ticket key, but diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index fdcac0aa565e..a7e85351b41e 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -142,6 +142,7 @@ class MockServerContextConfig : public ServerContextConfig { MOCK_METHOD(OcspStaplePolicy, ocspStaplePolicy, (), (const)); MOCK_METHOD(const std::vector&, sessionTicketKeys, (), (const)); MOCK_METHOD(bool, disableStatelessSessionResumption, (), (const)); + MOCK_METHOD(bool, disableStatefulSessionResumption, (), (const)); MOCK_METHOD(const Network::Address::IpList&, tlsKeyLogLocal, (), (const)); MOCK_METHOD(const Network::Address::IpList&, tlsKeyLogRemote, (), (const)); MOCK_METHOD(const std::string&, tlsKeyLogPath, (), (const));