diff --git a/api/envoy/api/v2/auth/cert.proto b/api/envoy/api/v2/auth/cert.proto index 297d3bbe73c86..8c7fef4cba78c 100644 --- a/api/envoy/api/v2/auth/cert.proto +++ b/api/envoy/api/v2/auth/cert.proto @@ -279,6 +279,9 @@ message UpstreamTlsContext { // // TLS renegotiation is considered insecure and shouldn't be used unless absolutely necessary. bool allow_renegotiation = 3; + + // If true, clients will resume TLS sessions + bool allow_session_resumption = 4; } message DownstreamTlsContext { diff --git a/include/envoy/ssl/context_config.h b/include/envoy/ssl/context_config.h index 7b30b50c82837..321aa4c716670 100644 --- a/include/envoy/ssl/context_config.h +++ b/include/envoy/ssl/context_config.h @@ -86,6 +86,10 @@ class ClientContextConfig : public virtual ContextConfig { * @return true if server-initiated TLS renegotiation will be allowed. */ virtual bool allowRenegotiation() const PURE; + /** + * @return true if client should resume TLS sessions + */ + virtual bool allowSessionResumption() const PURE; }; typedef std::unique_ptr ClientContextConfigPtr; diff --git a/source/common/ssl/context_config_impl.cc b/source/common/ssl/context_config_impl.cc index 8c80ecdb426c7..3a3e9c4f512eb 100644 --- a/source/common/ssl/context_config_impl.cc +++ b/source/common/ssl/context_config_impl.cc @@ -141,7 +141,8 @@ ClientContextConfigImpl::ClientContextConfigImpl( const envoy::api::v2::auth::UpstreamTlsContext& config, Server::Configuration::TransportSocketFactoryContext& factory_context) : ContextConfigImpl(config.common_tls_context(), factory_context), - server_name_indication_(config.sni()), allow_renegotiation_(config.allow_renegotiation()) { + server_name_indication_(config.sni()), allow_renegotiation_(config.allow_renegotiation()), + allow_session_resumption_(config.allow_session_resumption()) { // BoringSSL treats this as a C string, so embedded NULL characters will not // be handled correctly. if (server_name_indication_.find('\0') != std::string::npos) { diff --git a/source/common/ssl/context_config_impl.h b/source/common/ssl/context_config_impl.h index 7304108ff3aac..0e723d818fea6 100644 --- a/source/common/ssl/context_config_impl.h +++ b/source/common/ssl/context_config_impl.h @@ -102,10 +102,12 @@ class ClientContextConfigImpl : public ContextConfigImpl, public ClientContextCo // Ssl::ClientContextConfig const std::string& serverNameIndication() const override { return server_name_indication_; } bool allowRenegotiation() const override { return allow_renegotiation_; } + bool allowSessionResumption() const override { return allow_session_resumption_; } private: const std::string server_name_indication_; const bool allow_renegotiation_; + const bool allow_session_resumption_; }; class ServerContextConfigImpl : public ContextConfigImpl, public ServerContextConfig { diff --git a/source/common/ssl/context_impl.cc b/source/common/ssl/context_impl.cc index 29f6937f3a5f2..a7c21c763a2a6 100644 --- a/source/common/ssl/context_impl.cc +++ b/source/common/ssl/context_impl.cc @@ -20,6 +20,7 @@ #include "openssl/hmac.h" #include "openssl/rand.h" #include "openssl/x509v3.h" +#include namespace Envoy { namespace Ssl { @@ -483,17 +484,41 @@ CertificateDetailsPtr ContextImpl::certificateDetails(X509* cert, const std::str ClientContextImpl::ClientContextImpl(Stats::Scope& scope, const ClientContextConfig& config) : ContextImpl(scope, config), server_name_indication_(config.serverNameIndication()), - allow_renegotiation_(config.allowRenegotiation()) { + allow_renegotiation_(config.allowRenegotiation()), allow_session_resumption_(config.allowSessionResumption()) { if (!parsed_alpn_protocols_.empty()) { int rc = SSL_CTX_set_alpn_protos(ctx_.get(), &parsed_alpn_protocols_[0], parsed_alpn_protocols_.size()); RELEASE_ASSERT(rc == 0, ""); } + + if (allow_session_resumption_) { + SSL_CTX_set_session_cache_mode(ctx_.get(), SSL_SESS_CACHE_CLIENT|SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb( + ctx_.get(), + [](SSL * ssl, SSL_SESSION *session) { + ContextImpl* context_impl = static_cast( + SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl), sslContextIndex())); + ClientContextImpl* client_context_impl = dynamic_cast(context_impl); + int len = i2d_SSL_SESSION(session, NULL); + if (len <= ENVOY_MAX_SESSION_SIZE) { + client_context_impl->session_.resize(len); + unsigned char* temp = client_context_impl->session_.data(); + i2d_SSL_SESSION(session, &temp); + } + return 0; + }); + } } bssl::UniquePtr ClientContextImpl::newSsl() const { bssl::UniquePtr ssl_con(ContextImpl::newSsl()); + if (allow_session_resumption_ && !session_.empty()) { + const unsigned char* temp = session_.data(); + SSL_SESSION *session = d2i_SSL_SESSION(NULL, &temp, session_.size()); + SSL_set_session(ssl_con.get(), session); + } + if (!server_name_indication_.empty()) { int rc = SSL_set_tlsext_host_name(ssl_con.get(), server_name_indication_.c_str()); RELEASE_ASSERT(rc, ""); diff --git a/source/common/ssl/context_impl.h b/source/common/ssl/context_impl.h index 2096045222328..da5a724795852 100644 --- a/source/common/ssl/context_impl.h +++ b/source/common/ssl/context_impl.h @@ -19,6 +19,8 @@ namespace Envoy { #error Envoy requires BoringSSL #endif +#define ENVOY_MAX_SESSION_SIZE 4096 + namespace Ssl { // clang-format off @@ -146,6 +148,8 @@ class ClientContextImpl : public ContextImpl, public ClientContext { private: const std::string server_name_indication_; const bool allow_renegotiation_; + const bool allow_session_resumption_; + std::vector session_; }; class ServerContextImpl : public ContextImpl, public ServerContext {