From 225f31787643c682de486280d915d62992c10a80 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 15 Feb 2022 09:20:34 +0000 Subject: [PATCH 01/35] TLS bumping integration Merge following components for integration PoC - Certificate Provider framework - SNI-based cert selection in tls transport socket - A new network filter - BumpingFilter - Certificate Provider instance - LocalMimicCertProvider Signed-off-by: Luyao Zhong Signed-off-by: LeiZhang --- api/BUILD | 1 + api/envoy/config/bootstrap/v3/bootstrap.proto | 3 +- .../local_certificate/v3/BUILD | 12 + .../v3/local_certificate.proto | 28 ++ .../filters/network/bumping/v3/BUILD | 13 + .../filters/network/bumping/v3/bumping.proto | 43 +++ .../transport_sockets/tls/v3/common.proto | 4 +- .../transport_sockets/tls/v3/tls.proto | 12 +- api/versioning/BUILD | 2 + baidu.com.key | 28 ++ baidu.com.pem | 24 ++ changelogs/current.yaml | 108 ++++++ .../root/intro/arch_overview/security/ssl.rst | 36 +- envoy-bumping-integ.yaml | 95 +++++ envoy/certificate_provider/BUILD | 48 +++ .../certificate_provider.h | 101 ++++++ .../certificate_provider_factory.h | 29 ++ .../certificate_provider_manager.h | 35 ++ envoy/event/dispatcher.h | 3 +- envoy/network/connection.h | 2 + envoy/server/BUILD | 1 + envoy/server/instance.h | 6 + envoy/server/transport_socket_config.h | 6 + envoy/ssl/BUILD | 1 + .../certificate_validation_context_config.h | 13 + envoy/ssl/context_config.h | 5 + envoy/upstream/BUILD | 1 + envoy/upstream/cluster_manager.h | 6 + root-ca.key | 52 +++ root-ca.pem | 31 ++ source/common/certificate_provider/BUILD | 23 ++ .../certificate_provider_manager_impl.cc | 36 ++ .../certificate_provider_manager_impl.h | 34 ++ source/common/common/logger.h | 1 + source/common/event/dispatcher_impl.cc | 6 +- source/common/event/dispatcher_impl.h | 3 +- source/common/network/connection_impl.cc | 20 +- source/common/network/connection_impl.h | 4 +- ...tificate_validation_context_config_impl.cc | 23 +- ...rtificate_validation_context_config_impl.h | 14 +- source/common/upstream/cluster_manager_impl.h | 9 +- .../local_certificate/BUILD | 39 +++ .../local_certificate/config.cc | 30 ++ .../local_certificate/config.h | 29 ++ .../local_certificate/local_certificate.cc | 229 ++++++++++++ .../local_certificate/local_certificate.h | 75 ++++ source/extensions/extensions_build_config.bzl | 8 +- source/extensions/extensions_metadata.yaml | 14 + .../extensions/filters/network/bumping/BUILD | 79 +++++ .../filters/network/bumping/bumping.cc | 330 ++++++++++++++++++ .../filters/network/bumping/bumping.h | 242 +++++++++++++ .../filters/network/bumping/config.cc | 36 ++ .../filters/network/bumping/config.h | 32 ++ .../filters/network/well_known_names.h | 2 + .../tls/context_config_impl.cc | 44 ++- .../tls/context_config_impl.h | 6 + .../transport_sockets/tls/context_impl.cc | 131 ++++++- .../transport_sockets/tls/context_impl.h | 16 + source/server/BUILD | 1 + source/server/active_stream_listener_base.cc | 2 +- .../config_validation/cluster_manager.h | 4 +- source/server/config_validation/server.cc | 4 +- source/server/config_validation/server.h | 4 + source/server/configuration_impl.cc | 7 + source/server/server.cc | 5 +- source/server/server.h | 8 + source/server/transport_socket_config_impl.h | 3 + .../tls/context_impl_test.cc | 103 +++++- .../transport_sockets/tls/ssl_socket_test.cc | 189 +++++++++- .../transport_sockets/tls/test_data/certs.sh | 28 ++ .../tls/test_data/no_san_cn_2_cert.pem | 24 ++ .../tls/test_data/no_san_cn_2_cert_info.h | 8 + .../tls/test_data/no_san_cn_2_key.pem | 27 ++ .../tls/test_data/no_san_cn_server2_cert.cfg | 31 ++ .../tls/test_data/san_dns_ecdsa_1_cert.pem | 20 ++ .../tls/test_data/san_dns_ecdsa_1_cert_info.h | 8 + .../tls/test_data/san_dns_ecdsa_1_key.pem | 8 + .../tls/test_data/san_dns_ecdsa_2_cert.pem | 20 ++ .../tls/test_data/san_dns_ecdsa_2_cert_info.h | 8 + .../tls/test_data/san_dns_ecdsa_2_key.pem | 8 + .../tls/test_data/san_dns_rsa_1_cert.pem | 25 ++ .../tls/test_data/san_dns_rsa_1_cert_info.h | 8 + .../tls/test_data/san_dns_rsa_1_cert_key.pem | 27 ++ .../tls/test_data/san_dns_rsa_1_key.pem | 27 ++ .../tls/test_data/san_dns_rsa_2_cert.pem | 25 ++ .../tls/test_data/san_dns_rsa_2_cert_info.h | 8 + .../tls/test_data/san_dns_rsa_2_key.pem | 27 ++ .../tls/test_data/san_dns_server1_cert.cfg | 36 ++ .../tls/test_data/san_dns_server2_cert.cfg | 36 ++ .../tls/test_data/san_multiple_dns_1_cert.cfg | 37 ++ .../tls/test_data/san_multiple_dns_1_cert.pem | 25 ++ .../test_data/san_multiple_dns_1_cert_info.h | 8 + .../tls/test_data/san_multiple_dns_1_key.pem | 27 ++ tools/extensions/extensions_schema.yaml | 1 + 94 files changed, 3033 insertions(+), 68 deletions(-) create mode 100644 api/envoy/extensions/certificate_providers/local_certificate/v3/BUILD create mode 100644 api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto create mode 100644 api/envoy/extensions/filters/network/bumping/v3/BUILD create mode 100644 api/envoy/extensions/filters/network/bumping/v3/bumping.proto create mode 100644 baidu.com.key create mode 100644 baidu.com.pem create mode 100644 envoy-bumping-integ.yaml create mode 100644 envoy/certificate_provider/BUILD create mode 100644 envoy/certificate_provider/certificate_provider.h create mode 100644 envoy/certificate_provider/certificate_provider_factory.h create mode 100644 envoy/certificate_provider/certificate_provider_manager.h create mode 100644 root-ca.key create mode 100644 root-ca.pem create mode 100644 source/common/certificate_provider/BUILD create mode 100644 source/common/certificate_provider/certificate_provider_manager_impl.cc create mode 100644 source/common/certificate_provider/certificate_provider_manager_impl.h create mode 100644 source/extensions/certificate_providers/local_certificate/BUILD create mode 100644 source/extensions/certificate_providers/local_certificate/config.cc create mode 100644 source/extensions/certificate_providers/local_certificate/config.h create mode 100644 source/extensions/certificate_providers/local_certificate/local_certificate.cc create mode 100644 source/extensions/certificate_providers/local_certificate/local_certificate.h create mode 100644 source/extensions/filters/network/bumping/BUILD create mode 100644 source/extensions/filters/network/bumping/bumping.cc create mode 100644 source/extensions/filters/network/bumping/bumping.h create mode 100644 source/extensions/filters/network/bumping/config.cc create mode 100644 source/extensions/filters/network/bumping/config.h create mode 100644 test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/no_san_cn_2_key.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/no_san_cn_server2_cert.cfg create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_key.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_key.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_key.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_server1_cert.cfg create mode 100644 test/extensions/transport_sockets/tls/test_data/san_dns_server2_cert.cfg create mode 100644 test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.cfg create mode 100644 test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem diff --git a/api/BUILD b/api/BUILD index f92cef4a4b..8225b3270e 100644 --- a/api/BUILD +++ b/api/BUILD @@ -131,6 +131,7 @@ proto_library( "//envoy/extensions/access_loggers/wasm/v3:pkg", "//envoy/extensions/bootstrap/internal_listener/v3:pkg", "//envoy/extensions/cache/simple_http_cache/v3:pkg", + "//envoy/extensions/certificate_providers/local_certificate/v3:pkg", "//envoy/extensions/clusters/aggregate/v3:pkg", "//envoy/extensions/clusters/dynamic_forward_proxy/v3:pkg", "//envoy/extensions/clusters/redis/v3:pkg", diff --git a/api/envoy/config/bootstrap/v3/bootstrap.proto b/api/envoy/config/bootstrap/v3/bootstrap.proto index 6795058151..04e98bd758 100644 --- a/api/envoy/config/bootstrap/v3/bootstrap.proto +++ b/api/envoy/config/bootstrap/v3/bootstrap.proto @@ -315,9 +315,8 @@ message Bootstrap { // Global map of CertificateProvider instances. These instances are referred to by name in the // :ref:`CommonTlsContext.CertificateProviderInstance.instance_name - // ` + // ` // field. - // [#not-implemented-hide:] map certificate_provider_instances = 25; // Specifies a set of headers that need to be registered as inline header. This configuration diff --git a/api/envoy/extensions/certificate_providers/local_certificate/v3/BUILD b/api/envoy/extensions/certificate_providers/local_certificate/v3/BUILD new file mode 100644 index 0000000000..1c1a6f6b44 --- /dev/null +++ b/api/envoy/extensions/certificate_providers/local_certificate/v3/BUILD @@ -0,0 +1,12 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/core/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto new file mode 100644 index 0000000000..d477b0ab10 --- /dev/null +++ b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package envoy.extensions.certificate_providers.local_certificate.v3; + +import "envoy/config/core/v3/base.proto"; + +import "udpa/annotations/status.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.certificate_providers.local_certificate.v3"; +option java_outer_classname = "LocalCertificateProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/certificate_providers/local_certificate/v3;local_certificatev3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Local Certificate Provider] + +// [#extension: envoy.certificate_providers.local_certificate] +message LocalCertificate { + // Key and cert of root ca used to sign certificates. + config.core.v3.DataSource rootca_cert = 1; + + config.core.v3.DataSource rootca_key = 2; + + // Default Identity key and cert used for TLS handshake + config.core.v3.DataSource default_identity_cert = 3; + + config.core.v3.DataSource default_identity_key = 4; +} diff --git a/api/envoy/extensions/filters/network/bumping/v3/BUILD b/api/envoy/extensions/filters/network/bumping/v3/BUILD new file mode 100644 index 0000000000..d7a869c28d --- /dev/null +++ b/api/envoy/extensions/filters/network/bumping/v3/BUILD @@ -0,0 +1,13 @@ +# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = [ + "//envoy/config/accesslog/v3:pkg", + "//envoy/extensions/transport_sockets/tls/v3:pkg", + "@com_github_cncf_udpa//udpa/annotations:pkg", + ], +) diff --git a/api/envoy/extensions/filters/network/bumping/v3/bumping.proto b/api/envoy/extensions/filters/network/bumping/v3/bumping.proto new file mode 100644 index 0000000000..fd8f263e27 --- /dev/null +++ b/api/envoy/extensions/filters/network/bumping/v3/bumping.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +package envoy.extensions.filters.network.bumping.v3; + +import "envoy/config/accesslog/v3/accesslog.proto"; +import "envoy/extensions/transport_sockets/tls/v3/common.proto"; + +import "google/protobuf/wrappers.proto"; + +import "udpa/annotations/status.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.filters.network.bumping.v3"; +option java_outer_classname = "BumpingProto"; +option java_multiple_files = true; +option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/bumping/v3;bumpingv3"; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Bumping] +// Bumping :ref:`configuration overview `. +// [#extension: envoy.filters.network.bumping] + +// [#next-free-field: 6] +message Bumping { + // The prefix to use when emitting :ref:`statistics + // `. + string stat_prefix = 1 [(validate.rules).string = {min_len: 1}]; + + // The upstream cluster to connect to. + string cluster = 2; + + // Configuration for :ref:`access logs ` + // emitted by the this bumping filter. + repeated config.accesslog.v3.AccessLog access_log = 3; + + // The maximum number of unsuccessful connection attempts that will be made before + // giving up. If the parameter is not specified, 1 connection attempt will be made. + google.protobuf.UInt32Value max_connect_attempts = 4 [(validate.rules).uint32 = {gte: 1}]; + + // Certificate provider instance for fetching TLS certificates. + transport_sockets.tls.v3.CertificateProviderPluginInstance + tls_certificate_provider_instance = 5; +} \ No newline at end of file diff --git a/api/envoy/extensions/transport_sockets/tls/v3/common.proto b/api/envoy/extensions/transport_sockets/tls/v3/common.proto index b54f9306af..fd6f1adbca 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/common.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/common.proto @@ -246,7 +246,6 @@ message TlsSessionTicketKeys { // The plugin instances are defined in the client's bootstrap file. // The plugin allows certificates to be fetched/refreshed over the network asynchronously with // respect to the TLS handshake. -// [#not-implemented-hide:] message CertificateProviderPluginInstance { // Provider instance name. If not present, defaults to "default". // @@ -341,8 +340,7 @@ message CertificateValidationContext { // Certificate provider instance for fetching TLS certificates. // - // Only one of ``trusted_ca`` and ``ca_certificate_provider_instance`` may be specified. - // [#not-implemented-hide:] + // Only one of *trusted_ca* and *ca_certificate_provider_instance* may be specified. CertificateProviderPluginInstance ca_certificate_provider_instance = 13 [(udpa.annotations.field_migrate).oneof_promotion = "ca_cert_source"]; diff --git a/api/envoy/extensions/transport_sockets/tls/v3/tls.proto b/api/envoy/extensions/transport_sockets/tls/v3/tls.proto index 6f976380bb..a908a2065e 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/tls.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/tls.proto @@ -227,12 +227,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 ` 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. @@ -257,9 +254,8 @@ message CommonTlsContext { // Certificate provider instance for fetching TLS certs. // - // Only one of ``tls_certificates``, ``tls_certificate_sds_secret_configs``, - // and ``tls_certificate_provider_instance`` may be used. - // [#not-implemented-hide:] + // Only one of *tls_certificates*, *tls_certificate_sds_secret_configs*, + // and *tls_certificate_provider_instance* may be used. CertificateProviderPluginInstance tls_certificate_provider_instance = 14; // Certificate provider for fetching TLS certificates. diff --git a/api/versioning/BUILD b/api/versioning/BUILD index 5db1ad3171..e675922a1d 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -70,6 +70,7 @@ proto_library( "//envoy/extensions/access_loggers/wasm/v3:pkg", "//envoy/extensions/bootstrap/internal_listener/v3:pkg", "//envoy/extensions/cache/simple_http_cache/v3:pkg", + "//envoy/extensions/certificate_providers/local_certificate/v3:pkg", "//envoy/extensions/clusters/aggregate/v3:pkg", "//envoy/extensions/clusters/dynamic_forward_proxy/v3:pkg", "//envoy/extensions/clusters/redis/v3:pkg", @@ -139,6 +140,7 @@ proto_library( "//envoy/extensions/filters/listener/original_src/v3:pkg", "//envoy/extensions/filters/listener/proxy_protocol/v3:pkg", "//envoy/extensions/filters/listener/tls_inspector/v3:pkg", + "//envoy/extensions/filters/network/bumping/v3:pkg", "//envoy/extensions/filters/network/connection_limit/v3:pkg", "//envoy/extensions/filters/network/direct_response/v3:pkg", "//envoy/extensions/filters/network/dubbo_proxy/router/v3:pkg", diff --git a/baidu.com.key b/baidu.com.key new file mode 100644 index 0000000000..0405de5ca1 --- /dev/null +++ b/baidu.com.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCgCMNZWCDIiO0G +87xBMU/Y0lZDvZhSZg8NA5HQbWumPMNhCvPQyI8dqRvDh0TChWn4ePx24/atl6ay +ICe3SPsE3n850C/gf0zISK9aegl8Ov8rZIH8aw6lqEa1UZcIcFpgQYP/IveshpRf +seyvQPXeMSlwnpNuhAjU2i2HzSMx9Pg6tPyfnV14jqgfrvoPy4K0HMmiDr7mAdcR +7dHMSd4pvH8Akuj97fQn+UDEyi5MMEzcFzj13Or3/f3XCwFy9VuGn6ydpI1ZissU +xZQjfTCcM6+yh2+pO0nzzsHOc3tcm1zsoLEjJB9a+BIEbJ9xQzPbQtxB3hYgZa8H +dYAbN/i1AgMBAAECggEAF+93mENQE+421VVbEL0ZXiqHcHZI8/oDwkSIaI4VDbYE +2tzSfqWgkiUa2/G9XQKrSNh/miCaYnhOHFIm3ojx0lac3L7Aimk/yKzmXTfnd0cz +pv/PWTuB41D50mCzoDV9ruvdnClUtd8jDfUEm2mx3FuqAQgWsO7ai0teh/Mskmn+ +p+esD8j9z87NZbcC47NAxlS5zWhJueTwsDcGh2gM4akZD7ry1C66enSENXJjB223 +z6Cwt200cQfhMah4RqzlC8TomvmKh1sQyXG/kVM6m/DGMom3cno8muiJXJMPEMag +zxD4J3u05hItr7odFYxVYAssnjzGYUQ0ReTJL7OZAQKBgQDSBz150+CFXYKZQb3m +SdU/OPCaOIlLpqLuLT6xhG3gR0KNCEVCZGMyf9P/puQypFAOUNeU+UeKqZEiXD9o +ONwqHZnODcPx+xDM96KQHuIE+l5uyzijnia5t7I6JKgNkoy2LksP/CMLYtLjXtz9 +KdIKKBivCIpuQZG00BVM2LbsIQKBgQDDECXeo0fm+LaSRljnMus60jU33uOmsYmR +5KOq2MtSTYE9N8SFdCdjws+tZjosFOvpk2+v5JwriypOF/oWt0X3DBlyi28CqjoN +4A+cQLT50ogMN5gVAmruOf4EoXp5ppkimU5jqSzx3b0jUHoKO+naIyP+6FPL29Xe +r7IgptNaFQKBgQCwQpM9IqtSQV/Q7nNISL5GCKsjQj821en2qNHseI8dobAgW8iE +J2t4ff6UlqL8PRlSalYCGBIKNLQweepctRtP3PevDa5b7a/z0/8BpGladCO8J6Co +75jgU2GnmgvPGCYu3jrwd1GRxKXnWz1q2SYEkBHnEuvaC/0UGW1GyLj7AQKBgD2E +0Ty3KE0a8ZGOaCl6cJ+bfjdBr3B6G5YMkuWl+/HYCcOB3BPuvyGCZjccv4n5izGe +UyIZKnu9Jzl77F7PrFwuz7PFb9xaTXbkzGeOLMWBdXDM88Tkf07ksU1KlDLbrP4x +dXyO9WLOeQEzN1oU7TrjzE1vfkYT7g6OQNJ1asDFAoGBAMDbDovgu3WGAHgBiaNu +Cy0LhT1ZVvLZqrzEQTk0jkpCGdREiFuWJgJIAF4tnEeBxJXucVYRi2Z/v4a1d7Vr +s9ztrhoRGxTDv4l9InGqW17KhLlSrVpxgAZpnM2LtdohpG1WhAJs7n7nNUa8FNTR +wrK+f0mwOf7+jtoDLZvpZ509 +-----END PRIVATE KEY----- diff --git a/baidu.com.pem b/baidu.com.pem new file mode 100644 index 0000000000..9d8a5dc3f8 --- /dev/null +++ b/baidu.com.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID/zCCAeegAwIBAgIBADANBgkqhkiG9w0BAQsFADA8MRQwEgYDVQQLDAtNeVJv +b3RDQSBSMjERMA8GA1UECgwITXlSb290Q0ExETAPBgNVBAMMCE15Um9vdENBMB4X +DTIyMDcxMjAyNDExNVoXDTIzMDcxMjAyNDExNVowFDESMBAGA1UEAwwJYmFpZHUu +Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoAjDWVggyIjtBvO8 +QTFP2NJWQ72YUmYPDQOR0G1rpjzDYQrz0MiPHakbw4dEwoVp+Hj8duP2rZemsiAn +t0j7BN5/OdAv4H9MyEivWnoJfDr/K2SB/GsOpahGtVGXCHBaYEGD/yL3rIaUX7Hs +r0D13jEpcJ6TboQI1Noth80jMfT4OrT8n51deI6oH676D8uCtBzJog6+5gHXEe3R +zEneKbx/AJLo/e30J/lAxMouTDBM3Bc49dzq9/391wsBcvVbhp+snaSNWYrLFMWU +I30wnDOvsodvqTtJ887BznN7XJtc7KCxIyQfWvgSBGyfcUMz20LcQd4WIGWvB3WA +Gzf4tQIDAQABozQwMjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAYBgNVHREEETAP +gg13d3cuYmFpZHUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQAhN+RueMYTf7MmhmmO +LZ5b3sXRfJhsnWbFWEEu0FFqT5t6fqiulCLSdSPcCfnqjG9/S6He3eGJ0DyhkEFc +fjsniHycaaKYqyR3nxMHcOeIOHPMECxfQ8WDWhxpvkxNPp47Lmc+qWPwFz9fv8qn +j8rpAgkIs/06y342xQWhcAKKxlbcmhji+i9Ns7f5GPv7OAhoNpKhljhVaUpKkUXo +SFjWza1haK79sSM4ifIZ7ii8zLIazHZhP1uuZsLDFVPKFH1ryBn/RvSDq8ZKXRQ6 +zqdcLD431YEeYcUbp4ayXKz2djScqYfpM7j2fbzlXnv91RqSy9aM5eJvMKPQS0GA +hc8qF5x+0oGJY6Aaw0i62l/ZgbaEnvaFo0ReqGJyakmQ3ipHxtYLA1zrSFD7+0Bm +ytE971P6zD+R+7hv9kR4NgidPsCOC6gaepXm1/VmS6EHQMzrUhnkEtx6xBBi8o+q +fHEbffxhpHgrurKX7MbEPWVel+EXZ9EhrTPSjOf1+paqKy7Sl8Sh9wPaJL94XdnA +YPmg1KCBHL49eqOhpqR5pW8RWmEbz227PA/gbnPCU06xRSXSf4vcKTA94nblMkb0 +z8Duegu0pRY/AIn7vv+ea8Zwf6IH3eC/iBG3AGM7Y/yLNSCU4qNef4wV8tyrVrhf +5fXXzSLQHtjH5TwN03Lk2VrQvw== +-----END CERTIFICATE----- diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 9ecf0d6e48..237cd15724 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -13,5 +13,113 @@ removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` new_features: +- area: header_formatters + change: | + all access log formatters can be used as custom request/response headers. Custom header's syntax is parsed using access logger's parser and + header values are obtained using access log's substitution formatters. This feature can be reversed by setting runtime guard + ``envoy.reloadable_features.unified_header_formatter`` to false. +- area: http + change: | + made the :ref:`admission control ` work as an upstream filter. + change: | +- area: http + change: | + added default-false ``envoy.reloadable_features.http1_use_balsa_parser`` for experimental BalsaParser. + change: | + added ``envoy.reloadable_features.allow_upstream_filters`` for experimental upstream filters. +- area: dns_resolver + change: | + added DNS stats for c-ares DNS resolver. Detailed documentation is available :ref:`here `. +- area: gzip + change: | + added support for :ref:`max_inflate_ratio`. +- area: access_log + change: | + added downstream handshake timing to connection streamInfo. Can be accessed by custom access loggers. +- area: build + change: | + official released binary is now built on Ubuntu 20.04, requires glibc >= 2.30. +- area: listener + change: | + added multiple listening addresses in single listener. :ref:`listener additional addresses`. +- area: load balancer + change: | + added a new field to subset load balancer config: :ref:`metadata_fallback_policy`. +- area: thrift + change: | + added stats for downstream connection close to detect SR drop. +- area: compression + change: | + added support for :ref:`choose_first`. +- area: cors + change: | + added support for cors PNA. This behavioral change can be temporarily reverted by setting runtime guard ``envoy_reloadable_features_cors_private_network_access`` to false. More details refer to https://developer.chrome.com/blog/private-network-access-preflight. +- area: upstream + change: | + added a filter state object to control the destination address in :ref:`ORIGINAL_DST clusters `. +- area: upstream + change: | + added a new field :ref:`additional_source_addresses ` to the BindConfig, it enables to specify multiple source addresses, and the source address selection is based on target host's address' version. +- area: admin + change: | + added new :ref:`/heap_dump ` endpoint to dump heap profile of Envoy. +- area: health check + change: | + added :ref:`method ` support to configure http health check http method. +- area: access_log + change: | + updated command operator ``%GRPC_STATUS%`` to suppoprt the snake case. +- area: listener + change: | + expose the implementation of :ref:`internal listener ` in xDS API. +- area: ratelimit + change: | + add support for :ref:`adding response headers ` to rate-limited responses. +- area: access_log + change: | + added support for number values in substitution format string in json_format. +- area: http + change: | + added the expected :ref:`receive ` payload check for HTTP health check. + Added :ref:`response_buffer_size ` to configure the maximum HTTP health check response buffer size. +- area: lua + change: | + added new headers method "setHttp1ReasonPhrase" for lua filter, please see :ref:`lua header wrapper `. +- area: lua + change: | + added stats for lua filter, please see :ref:`lua filter stats `. +- area: subset load balancer + change: | + added multiple keys or multiple selectors support for :ref:`single host per subset mode `. +- area: listener + change: | + allow network filters other than HTTP Connection Manager to be created for QUIC listeners. +- area: lua + change: | + added an alternative function signature to ``httpCall()`` with ``options`` as an argument. This allows to skip sampling the produced trace span + by setting ``{["trace_sampled"] = false}`` as the ``options``. And this allows to return multiple header values for same header name by setting + ``{["return_duplicate_headers"] = true}`` as the ``options``. +- area: cluster + change: | + added support to override original destination port via setting :ref:`upstream_port_override `. +- area: generic_proxy + change: | + added an new network filter :ref:`generic_proxy filter `. +- area: redis + change: | + added support for quit command to the redis proxy. +- area: access_log + change: | + log ``duration``, ``upstream_request_attempt_count``, ``connection_termination_details`` and tls ``ja3`` field in the grpc access log + and also log the tls ``sni`` and ``ja3`` field in the grpc access log when envoy is configured as a tls forward proxy. +- area: grpc_json_transcoder + change: | + added support for parsing enum value case insensitively enabled by the config :ref:`case_insensitive_enum_parsing `. +- area: generic_proxy + change: | + added an new network filter :ref:`generic_proxy filter `. +- area: tls + change: | + added support for SNI-based cert selection in tls downstream transport socket. Detailed documentation is available :ref:`cert selection`. deprecated: diff --git a/docs/root/intro/arch_overview/security/ssl.rst b/docs/root/intro/arch_overview/security/ssl.rst index 8878c1faf4..4fe028cbc6 100644 --- a/docs/root/intro/arch_overview/security/ssl.rst +++ b/docs/root/intro/arch_overview/security/ssl.rst @@ -114,21 +114,35 @@ Certificate selection --------------------- :ref:`DownstreamTlsContexts ` 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 SANs. -* 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. +* 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 pattern. +* 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 ` - 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 `. -* 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 `. -* The selected certificate must adhere to the OCSP policy. If no - such certificate is found, the connection is refused. + +Certificate selection rules, including SNI matching, Public Key Type matching and OCSP: + +* If the client supports SNI, a certificate with proper DNS SANs or Subject Common Name should be selected +* It matches on exact server name first, then matches on wildcard server name if an exact name match isn't found, e.g. if SNI is + "test.example.com", a certificate with "test.example.com" will be preferred over for "\*.example.com". +* If no certificate is matched to SNI or the client does not support SNI, subsequent particular type (RSA or ECDSA) + matching will be executed with all certificates as candidates. +* Otherwise, particular type (RSA or ECDSA) matching will be executed with SNI-matched certificates as candidates. +* Key type matching is based on SNI matching results +* If the client supports P-256 ECDSA, the first P-256 ECDSA certificate is selected if it is present and the OCSP check is passed. +* If the client only supports RSA, the first RSA certificate is selected if it is present. +* If no exact match, fallback to the first certificate in the candidates. +* The certificate that it fallbacks to might result in a failed handshake. For instance, a client only supports + RSA certificates and the certificate only support ECDSA, or a client only supports ECDSA certificate and the + certificate only support RSA. +* After key type matching, one certificate is selected, and the selected certificate must adhere to the OCSP policy. + If no such certificate is found, the connection is refused. Only a single TLS certificate is supported today for :ref:`UpstreamTlsContexts `. diff --git a/envoy-bumping-integ.yaml b/envoy-bumping-integ.yaml new file mode 100644 index 0000000000..b912ab0bee --- /dev/null +++ b/envoy-bumping-integ.yaml @@ -0,0 +1,95 @@ +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 1234 + listener_filters: + - name: envoy.filters.listener.tls_inspector + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: ["www.baidu.com", "www.google.com", "www.linkedin.com"] # bumping w/ connect or w/o connect + filters: + - name: envoy.filters.network.sni_dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig + port_value: 443 + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.network.bumping + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.bumping.v3.Bumping + stat_prefix: destination + cluster: dynamic_forward_proxy_cluster_bumping + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: dynamic_forward_proxy_cluster_bumping + http_filters: + - name: envoy.filters.http.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + common_tls_context: + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + clusters: + - name: dynamic_forward_proxy_cluster_bumping + connect_timeout: 1s + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + validation_context: + trusted_ca: + filename: /etc/ssl/certs/ca-certificates.crt +certificate_provider_instances: + local_cert_provider: + name: envoy.certificate_providers.local_certificate + typed_config: + "@type": type.googleapis.com/envoy.extensions.certificate_providers.local_certificate.v3.LocalCertificate + rootca_cert: + filename: root-ca.pem + rootca_key: + filename: root-ca.key + default_identity_cert: + filename: baidu.com.pem + default_identity_key: + filename: baidu.com.key diff --git a/envoy/certificate_provider/BUILD b/envoy/certificate_provider/BUILD new file mode 100644 index 0000000000..d1df9eaffc --- /dev/null +++ b/envoy/certificate_provider/BUILD @@ -0,0 +1,48 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "certificate_provider_interface", + hdrs = ["certificate_provider.h"], + external_deps = [ + "ssl", + ], + deps = [ + "//envoy/common:callback", + "//envoy/event:dispatcher_interface", + "//envoy/ssl:connection_interface", + "@com_google_absl//absl/strings", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", + ], +) + +envoy_cc_library( + name = "certificate_provider_manager_interface", + hdrs = [ + "certificate_provider_manager.h", + ], + deps = [ + ":certificate_provider_interface", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + ], +) + +envoy_cc_library( + name = "certificate_provider_factory_lib", + hdrs = [ + "certificate_provider_factory.h", + ], + deps = [ + ":certificate_provider_interface", + "//envoy/registry", + "@com_google_absl//absl/strings", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + ], +) diff --git a/envoy/certificate_provider/certificate_provider.h b/envoy/certificate_provider/certificate_provider.h new file mode 100644 index 0000000000..152bd55313 --- /dev/null +++ b/envoy/certificate_provider/certificate_provider.h @@ -0,0 +1,101 @@ +#pragma once + +#include + +#include "envoy/common/callback.h" +#include "envoy/common/pure.h" +#include "envoy/event/dispatcher.h" +#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" +#include "envoy/ssl/connection.h" + +#include "absl/strings/string_view.h" +#include "openssl/ssl.h" +#include "openssl/x509v3.h" + +namespace Envoy { +namespace CertificateProvider { + +class OnDemandUpdateMetadata { +public: + virtual ~OnDemandUpdateMetadata() = default; + + virtual Envoy::Ssl::ConnectionInfoConstSharedPtr connectionInfo() const PURE; +}; + +using OnDemandUpdateMetadataPtr = std::shared_ptr; + +class OnDemandUpdateCallbacks { +public: + virtual ~OnDemandUpdateCallbacks() = default; + + /** + * Called when cert is already in cache. + * @param host supplies host of cert. + */ + virtual void onCacheHit(const std::string host) const PURE; + /** + * Called when cert cache is missed. + * @param host supplies host of cert. + */ + virtual void onCacheMiss(const std::string host) const PURE; +}; + +class OnDemandUpdateHandle { +public: + virtual ~OnDemandUpdateHandle() = default; +}; + +using OnDemandUpdateHandlePtr = std::unique_ptr; + +class CertificateProvider { +public: + struct Capabilities { + /* whether or not a provider supports generating identity certificates on demand */ + bool provide_on_demand_identity_certs = false; + }; + + virtual ~CertificateProvider() = default; + + virtual Capabilities capabilities() const PURE; + + /** + * @return CA certificate used for validation + */ + virtual const std::string trustedCA(const std::string& cert_name) const PURE; + + /** + * Certificate provider instance which used to get tls certificates + * should provide at least one tls certificate. + * @return Identity certificates used for handshake + */ + virtual std::vector> + tlsCertificates(const std::string& cert_name) const PURE; + + /** + * Add on-demand callback into certificate provider, this function might be invoked from worker + * thread during runtime + * + * @param cert_name is certificate provider name in commontlscontext configuration. + * @param metadata is passed to provider for certs fetching/refreshing. + * @param thread_local_dispatcher is the dispatcher from callee's thread. + * @param callback registers callback to be executed for on demand update. + * @return OnDemandUpdateHandle the handle which can remove that update callback. + */ + virtual OnDemandUpdateHandlePtr addOnDemandUpdateCallback( + const std::string cert_name, Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + Event::Dispatcher& thread_local_dispatcher, OnDemandUpdateCallbacks& callback) PURE; + + /** + * Add certificate update callback into certificate provider for asychronous usage. + * + * @param callback callback that is executed by certificate provider. + * @return CallbackHandle the handle which can remove that update callback. + */ + virtual Common::CallbackHandlePtr addUpdateCallback(const std::string& cert_name, + std::function callback) PURE; +}; + +using CertificateProviderSharedPtr = std::shared_ptr; + +} // namespace CertificateProvider +} // namespace Envoy diff --git a/envoy/certificate_provider/certificate_provider_factory.h b/envoy/certificate_provider/certificate_provider_factory.h new file mode 100644 index 0000000000..3802aa710f --- /dev/null +++ b/envoy/certificate_provider/certificate_provider_factory.h @@ -0,0 +1,29 @@ +#pragma once + +#include "envoy/certificate_provider/certificate_provider.h" +#include "envoy/common/pure.h" +#include "envoy/config/core/v3/extension.pb.h" +#include "envoy/registry/registry.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Server { +namespace Configuration { +class TransportSocketFactoryContext; +} // namespace Configuration +} // namespace Server +namespace CertificateProvider { + +class CertificateProviderFactory : public Config::TypedFactory { +public: + virtual Envoy::CertificateProvider::CertificateProviderSharedPtr + createCertificateProviderInstance( + const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) PURE; + + std::string category() const override { return "envoy.certificate_providers"; } +}; + +} // namespace CertificateProvider +} // namespace Envoy diff --git a/envoy/certificate_provider/certificate_provider_manager.h b/envoy/certificate_provider/certificate_provider_manager.h new file mode 100644 index 0000000000..bc0789bd35 --- /dev/null +++ b/envoy/certificate_provider/certificate_provider_manager.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "envoy/certificate_provider/certificate_provider.h" +#include "envoy/common/pure.h" +#include "envoy/config/core/v3/extension.pb.h" +#include "envoy/singleton/instance.h" + +namespace Envoy { +namespace Server { +namespace Configuration { +class TransportSocketFactoryContext; +} // namespace Configuration +} // namespace Server +namespace CertificateProvider { + +/** + * A manager for certificate provider instances. + */ +class CertificateProviderManager : public Singleton::Instance { +public: + ~CertificateProviderManager() override = default; + + virtual void addCertificateProvider( + absl::string_view name, const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) PURE; + + virtual CertificateProviderSharedPtr getCertificateProvider(absl::string_view name) PURE; +}; + +using CertificateProviderManagerPtr = std::unique_ptr; + +} // namespace CertificateProvider +} // namespace Envoy diff --git a/envoy/event/dispatcher.h b/envoy/event/dispatcher.h index c3741bc15f..d17dd634e6 100644 --- a/envoy/event/dispatcher.h +++ b/envoy/event/dispatcher.h @@ -200,7 +200,8 @@ class Dispatcher : public DispatcherBase, public ScopeTracker { virtual Network::ServerConnectionPtr createServerConnection(Network::ConnectionSocketPtr&& socket, Network::TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info) PURE; + StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory) PURE; /** * Creates an instance of Envoy's Network::ClientConnection. Does NOT initiate the connection; diff --git a/envoy/network/connection.h b/envoy/network/connection.h index 3cbada199d..ec5738bbf7 100644 --- a/envoy/network/connection.h +++ b/envoy/network/connection.h @@ -351,6 +351,8 @@ class Connection : public Event::DeferredDeletable, * return value is cwnd(in packets) times the connection's MSS. */ virtual absl::optional congestionWindowInBytes() const PURE; + + bool write_disable{false}; }; using ConnectionPtr = std::unique_ptr; diff --git a/envoy/server/BUILD b/envoy/server/BUILD index 77eeef992b..4963578bdf 100644 --- a/envoy/server/BUILD +++ b/envoy/server/BUILD @@ -126,6 +126,7 @@ envoy_cc_library( ":options_interface", "//envoy/access_log:access_log_interface", "//envoy/api:api_interface", + "//envoy/certificate_provider:certificate_provider_manager_interface", "//envoy/common:mutex_tracer", "//envoy/event:timer_interface", "//envoy/http:context_interface", diff --git a/envoy/server/instance.h b/envoy/server/instance.h index 575c156c90..d1cad09453 100644 --- a/envoy/server/instance.h +++ b/envoy/server/instance.h @@ -7,6 +7,7 @@ #include "envoy/access_log/access_log.h" #include "envoy/api/api.h" +#include "envoy/certificate_provider/certificate_provider_manager.h" #include "envoy/common/mutex_tracer.h" #include "envoy/common/random_generator.h" #include "envoy/config/trace/v3/http_tracer.pb.h" @@ -142,6 +143,11 @@ class Instance { */ virtual Secret::SecretManager& secretManager() PURE; + /** + * @return the server's certificate provider manager + */ + virtual CertificateProvider::CertificateProviderManager& certificateProviderManager() PURE; + /** * @return the server's CLI options. */ diff --git a/envoy/server/transport_socket_config.h b/envoy/server/transport_socket_config.h index 9b3d5e3ad1..bcfcb957b4 100644 --- a/envoy/server/transport_socket_config.h +++ b/envoy/server/transport_socket_config.h @@ -2,6 +2,7 @@ #include +#include "envoy/certificate_provider/certificate_provider_manager.h" #include "envoy/config/core/v3/health_check.pb.h" #include "envoy/config/typed_config.h" #include "envoy/event/dispatcher.h" @@ -104,6 +105,11 @@ class TransportSocketFactoryContext { * @return reference to the access log manager object */ virtual AccessLog::AccessLogManager& accessLogManager() PURE; + + /** + * @return the instance of certificate provider manager. + */ + virtual CertificateProvider::CertificateProviderManager& certificateProviderManager() PURE; }; using TransportSocketFactoryContextPtr = std::unique_ptr; diff --git a/envoy/ssl/BUILD b/envoy/ssl/BUILD index 4d2a2c077a..70e4aad161 100644 --- a/envoy/ssl/BUILD +++ b/envoy/ssl/BUILD @@ -63,6 +63,7 @@ envoy_cc_library( hdrs = ["certificate_validation_context_config.h"], external_deps = ["abseil_optional"], deps = [ + "//envoy/certificate_provider:certificate_provider_interface", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", "@envoy_api//envoy/type/matcher/v3:pkg_cc_proto", ], diff --git a/envoy/ssl/certificate_validation_context_config.h b/envoy/ssl/certificate_validation_context_config.h index aa0214b77a..3839dcbb5d 100644 --- a/envoy/ssl/certificate_validation_context_config.h +++ b/envoy/ssl/certificate_validation_context_config.h @@ -5,6 +5,7 @@ #include #include "envoy/api/api.h" +#include "envoy/certificate_provider/certificate_provider.h" #include "envoy/common/pure.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" @@ -94,6 +95,18 @@ class CertificateValidationContextConfig { * @return the max depth used when verifying the certificate-chain */ virtual absl::optional maxVerifyDepth() const PURE; + + /** + * @return the CA provider which can provide CA certificate to use for peer validation + */ + virtual Envoy::CertificateProvider::CertificateProviderSharedPtr caProvider() const PURE; + + /** + * Add ca callback into validation context config. When trusted ca is refreshed from + * ca provider, this callback is invoked to update config. + * @param callback callback that is executed by validation context config. + */ + virtual void setCAUpdateCallback(std::function callback) PURE; }; using CertificateValidationContextConfigPtr = std::unique_ptr; diff --git a/envoy/ssl/context_config.h b/envoy/ssl/context_config.h index 701637c5eb..b25e9a3b18 100644 --- a/envoy/ssl/context_config.h +++ b/envoy/ssl/context_config.h @@ -111,6 +111,11 @@ class ContextConfig { * @return the access log manager object reference */ virtual AccessLog::AccessLogManager& accessLogManager() const PURE; + + /** + * @return the set of capabilities for Certificate Provider instance specified in this context + */ + virtual CertificateProvider::CertificateProvider::Capabilities certProviderCaps() const PURE; }; class ClientContextConfig : public virtual ContextConfig { diff --git a/envoy/upstream/BUILD b/envoy/upstream/BUILD index be7bd4aea4..39c9460663 100644 --- a/envoy/upstream/BUILD +++ b/envoy/upstream/BUILD @@ -20,6 +20,7 @@ envoy_cc_library( ":thread_local_cluster_interface", ":upstream_interface", "//envoy/access_log:access_log_interface", + "//envoy/certificate_provider:certificate_provider_manager_interface", "//envoy/common:random_generator_interface", "//envoy/config:grpc_mux_interface", "//envoy/config:subscription_factory_interface", diff --git a/envoy/upstream/cluster_manager.h b/envoy/upstream/cluster_manager.h index bbdcaf2711..0540070c5f 100644 --- a/envoy/upstream/cluster_manager.h +++ b/envoy/upstream/cluster_manager.h @@ -7,6 +7,7 @@ #include "envoy/access_log/access_log.h" #include "envoy/api/api.h" +#include "envoy/certificate_provider/certificate_provider_manager.h" #include "envoy/common/random_generator.h" #include "envoy/config/bootstrap/v3/bootstrap.pb.h" #include "envoy/config/cluster/v3/cluster.pb.h" @@ -520,6 +521,11 @@ class ClusterManagerFactory { * Returns the singleton manager. */ virtual Singleton::Manager& singletonManager() PURE; + + /** + * Returns the certificate provider manager + */ + virtual CertificateProvider::CertificateProviderManager& certificateProviderManager() PURE; }; /** diff --git a/root-ca.key b/root-ca.key new file mode 100644 index 0000000000..7e5ab56968 --- /dev/null +++ b/root-ca.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDZUQLO9FvTEj0c +cYuV5pwTguFrDxp8EOLYxep0acXN+eW5mlb4mckqmEbJ4H7C7Ge4V1W8nVrSosT0 +Ijo/mqW8N70OuxpW0eGRb8Tycxq7C+3Rpnmxurjo4yeqXXhFGnFHGnJ6/xH0GHxq +HKNQMfpOVbzh9uf+WsdAGS2idjpjC7r7XnmOdLYWSb9PkqBJCvvlH3M3t2W5OiMw +CK5clDBCfHnP4M5d6lE/jxQO2oFNqiLg2o9aGcxrbp2pVQrM9F9pOPYkABSE9vAi +9nXuBdgkmISX7MsQSu48hOz5m23qkQZd2Jp6wGJKXr5nhut3YtpoGJgNxb+QYFbg +/BtbzZkeqVZGOQyq2YUrItknnBSXOVs/LQ7D603bJFBWn23gYkNojrcl5wWcb+De +3LvWpM+qPk7KecyqFQT3yhF91p7gdIT+IqAnLM+myYT5eMVwHdSNw06HFChT5ItJ +jHFetaLimKasXAn87j//aESnDz9gzu+3otMHlnM4Ue/Z4vUb31xGDdktLLRSksbe +Q6IW1Soax+1fn8mePQp45YVZYoYFX3h1xv1ZiC6EH4bXEYCBXrG6E5X9iiW1ECxh +M1pdaAIR5P0+VQkXQnWfrBIm0fFUMZwMfLoz2okJ+xYAowCLQVQjD3xO6MUDeUSW +NTVRuTSkMdWsE2YyxoW/H5JDYSLG5wIDAQABAoICAQCWZ3kYHwtGZEMjYnqHPtoa +ruSwof1kjJNUEHaaJ3Kdgi06bdVvrZALCOVFZziUU+XdMaTHK+jmC31gsie6SaAX +SkRw45HxMMV7UO3wFzYS6sin5x4moeLEXCLhyFsrCIgJ/AXrlomodSH2lud4434n +pTNa+PXLlqx8cnChFk6GqmG5A8QBkklL17Kusf55Tz7TbxbhIu8maVRdNETpEi7s +dRYhh6eMXFXrNW1ecmA3jl8hXGMGqwBOAnrf7VnSS/eU7bV9aQjKldXRfN5V5HmZ +qm1qcJqCJjoIbL9OagKC6+iKHxWbi3b7neMN59UkBai/jvX94jUZCXRXtmSA/yTP +S0pFqq7hNKzhB+yd2Fvp+Tx8s6kx5Zbr1xwzAfwYNwoZ2kGbZIBHtRfJB+N0tdPd +TJEcooVxvzhvvQfKf734IJOOAIREjZ2U7FnA9inczu2bvP4jibba09VoD/E6hmm6 +n+jHF9ajjXNHF6iHanUBZrOiX3uCojJVQU35xzqZMKmjGQPS4EF+zNJ7Eht5O2ix +wvEAaWIpYCWa7fUhuCxPXVxUP7rl/nCXpY4M4KlANW1FotwTez6LJeb5mRW72Toi +pgPNpwKqEbtV780/aMcmpLQtx0xNbg49X3xW5deMnEgL0yQjwZ13mLhprzngI+4T +QIWUU4YhQmSjMA2bjY2bEQKCAQEA6+01XkTEgYvzPHSHonlt8b5rrILKYk+PAW+o +h1ypsuH6wIV+HfEq8qxRMwEPUJ5ZJaYNbqza0b7lFAZTgfajwrMKN72CBYXu3N36 +QKSLxWtgkEjyEpw3i3OoO2tjyJ0TbNpe1l1ACHzjOiQ1tpquHg8XrcVfkvepV//y +md2yyqgRfzxH3B5/+tA/5Ge1s+HsRkiey+wpR6BecrcQMK2gxSSNn1gQ5DaeQgha +RqH5KNfV0PBmbM3mowu9Ip1h4L3LWZibUVYNyJbzYUEJGOvk4+QgDmvQZcLx+H6+ +iWbkt425kK64P7KqMTGFVYYEAURGBIlvSCra2TOICci78NdccwKCAQEA685y8/j4 +H+2glIMXqs5ck7xxs8iSM1jL3Ai6qA/NLmcC7crEepiuR0NOWcBli5F2HlLI9zNW +GAosdkTq08HT4fcTYG/RW5tiSG5YsxKM/pnyuWUB8nbf/6UjBN4NTgHuf0xwMgKB +4ir6naGpVD0ZB4SiD9Fa88aPFHfca3BKJQtTxqQkrHqC1LsHr3uDx3Ph0WZst/3n +z+NARuUuBGZUodjna7ptfPnzq2k60k6sVt8T/xcFb5Nks1lsFoV8TrbK85zI+Khu +d6NeLEx8ocPMaFp6nf9YkpCL/uD0ig9NdFXhtJ/VUkWCo757moadL9x1472b7xo4 +WgN+xWmPl+LivQKCAQA+UV76cpGxYmUyf9lf6lFHPLQ2UinhRACTMYpDjw9oJ9bT +Sa+hexWMGkYsgcgo1FrZ3f3IUTJPmuvpmS4vsxVYlXBKfu08Rj9s6dlzf8NHt3A/ +THVcwxTM6hTy9Q/bato5s4i/eMw0McDXl770Z/E8EWKAzRTMvkxl6T6MtAnJDBHr +GOImW4wUlTN7QoW1aSS+/+mqFcxllg+8n2xQX1yLFneuMlXr4h11db1a0Of+iNx6 +nzV+4fhc8vnXjdjPw3JWTUAhgOiNJopNEx+zDIReSeDJzmKyroa5y8jKhHo/ndj5 +RZ13fdsEuX7ts0OVjhIsmpzhZCiAnOL/Kp2yJUDPAoIBAQDpqIynzFCx8ploD+5n +xkxd6CES8PBOCPMYeV3cIqxCCrkQQBgpExm+a6c7sDXu8piEu6t/qFLrR0l/eBLo ++eumwVll+ecOSw48j3s9Fmi38Hxf0QL462pSu6PTsuSkI63cXjXemDVzkvr7L7HG +UzR2Cqs72Uuyu1IUCiFUJNSTmIHPF99vOYZinWGkNVQDU4S0JjYcq3AEwz5yAa5z +laLPLUtDqFvNtzAI7iDnPCXxfuMlpf4w7XCfd0BvoJ3osaCxfO531w5/ZlZutuvY +r1fhH6bY4Kk3shvG18n48+jgGimo/ag3+cDV2G5KVJF1V5NJtBzL9MNaDpSTsWIk +MyJRAoIBAQDfnbjTICVRt37gIA4HGy0Mxuqlb2MiHlEJue/K/ZulwBBmVt1GlsAG +1u7T/pGo4QgNkTP/VHWkOzOn74fQ9+G5Chct7UqyGEG2d5fXkiRMjWEvhDrnezRr +Fs0Xy3hMAHRZ9yzySZW2NGTwq5rGIrmfuIlY8keXzi1/piBzbx/9zg/oLiaoK1pI +ga5LrZJrPAvLMeUOFI7mS2JQo/xQuSOz++RDQgu7Z/Oa/5S2oOKuo71bghmjCnWy +3mL77dfvP2dLGe3Zoc16ncHQKdMT0E8H/euwgzb2bFwNT9xh/9PavTEQoWF/xv8Y +H45/ESlk8j8L+rUiAofifqiVXiAb3hqK +-----END PRIVATE KEY----- diff --git a/root-ca.pem b/root-ca.pem new file mode 100644 index 0000000000..a9f011cea3 --- /dev/null +++ b/root-ca.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIUdMXgXhEMwcXCCNeQGL9ARPWMWHowDQYJKoZIhvcNAQEL +BQAwPDEUMBIGA1UECwwLTXlSb290Q0EgUjIxETAPBgNVBAoMCE15Um9vdENBMREw +DwYDVQQDDAhNeVJvb3RDQTAeFw0yMTA5MTAwNzI5MDFaFw0zMTA5MDgwNzI5MDFa +MDwxFDASBgNVBAsMC015Um9vdENBIFIyMREwDwYDVQQKDAhNeVJvb3RDQTERMA8G +A1UEAwwITXlSb290Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDZ +UQLO9FvTEj0ccYuV5pwTguFrDxp8EOLYxep0acXN+eW5mlb4mckqmEbJ4H7C7Ge4 +V1W8nVrSosT0Ijo/mqW8N70OuxpW0eGRb8Tycxq7C+3Rpnmxurjo4yeqXXhFGnFH +GnJ6/xH0GHxqHKNQMfpOVbzh9uf+WsdAGS2idjpjC7r7XnmOdLYWSb9PkqBJCvvl +H3M3t2W5OiMwCK5clDBCfHnP4M5d6lE/jxQO2oFNqiLg2o9aGcxrbp2pVQrM9F9p +OPYkABSE9vAi9nXuBdgkmISX7MsQSu48hOz5m23qkQZd2Jp6wGJKXr5nhut3Ytpo +GJgNxb+QYFbg/BtbzZkeqVZGOQyq2YUrItknnBSXOVs/LQ7D603bJFBWn23gYkNo +jrcl5wWcb+De3LvWpM+qPk7KecyqFQT3yhF91p7gdIT+IqAnLM+myYT5eMVwHdSN +w06HFChT5ItJjHFetaLimKasXAn87j//aESnDz9gzu+3otMHlnM4Ue/Z4vUb31xG +DdktLLRSksbeQ6IW1Soax+1fn8mePQp45YVZYoYFX3h1xv1ZiC6EH4bXEYCBXrG6 +E5X9iiW1ECxhM1pdaAIR5P0+VQkXQnWfrBIm0fFUMZwMfLoz2okJ+xYAowCLQVQj +D3xO6MUDeUSWNTVRuTSkMdWsE2YyxoW/H5JDYSLG5wIDAQABo1MwUTAdBgNVHQ4E +FgQUB6fDsw06ciRbx9EqnxqE73UikL0wHwYDVR0jBBgwFoAUB6fDsw06ciRbx9Eq +nxqE73UikL0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAgTu8 +vUg2FMvy+Jchi/+68wgUse9FXL4Tw32Cxi5bOU4iYT7rd/S+XsGsvj3qE5epW6VW +d95MbZ4inzRmCT4zia1ksXpk6fkBUwnskj02UtbCp+xb/AXLkoJvBt/XdGKNTTDv +njfPt2AFGJ4xXCJF9DfcxsXahQP3pkJBxygXdzgrKSlS0fO9djdSIWJo7gCzUn1W +lZlULUBn2HOObum4GwwnksWyl3t01ejFJA1zgNBlw9R6tYnYqmE6qUcOHbNLrMDc +pNEgFnmNFLtdCx3yWStlRkliIRLLefmKr/xf7k63KbzH6tywigy87rb3kM1gTXK5 +Xe0y7tPBfdS7n5vVqorrFjH1ZUywFOyUOHT+kvrzKG/8OBHzR9G2etR1BMA4B7fW +Sqkvb+m+qmGQjzxM+/AmtLhu2AMXH2caFZOvXajiElo3Emoy1c8W4ncFWGAwC/Nu +EuTAzjWPVUo91IZ+l6kFN8D8/R0xGiyHk9y0fAq6/2eVyyPBcc5AaL5+YAHgfTgZ +zUJgnJfSRD3xuZs2vAQWqcEqoZsytYIlwVQBfcYlPA0NX1m0fRHH2svoKHN3keNw +79NXzuRgA1cFOaW3jeHHwZQIO26d6MdbpXPmN8vJ3stLN782Xg7UQFSulMXEO5mn +A3t5xUjMepZvD7/HpEcU2lP6pE75sZHCiz3wq+E= +-----END CERTIFICATE----- diff --git a/source/common/certificate_provider/BUILD b/source/common/certificate_provider/BUILD new file mode 100644 index 0000000000..378688d4cd --- /dev/null +++ b/source/common/certificate_provider/BUILD @@ -0,0 +1,23 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_library( + name = "certificate_provider_manager_impl_lib", + srcs = ["certificate_provider_manager_impl.cc"], + hdrs = ["certificate_provider_manager_impl.h"], + deps = [ + "//envoy/api:api_interface", + "//envoy/certificate_provider:certificate_provider_factory_lib", + "//envoy/certificate_provider:certificate_provider_manager_interface", + "//source/common/config:utility_lib", + "@com_google_absl//absl/container:flat_hash_map", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", + ], +) diff --git a/source/common/certificate_provider/certificate_provider_manager_impl.cc b/source/common/certificate_provider/certificate_provider_manager_impl.cc new file mode 100644 index 0000000000..a87806c118 --- /dev/null +++ b/source/common/certificate_provider/certificate_provider_manager_impl.cc @@ -0,0 +1,36 @@ +#include "source/common/certificate_provider/certificate_provider_manager_impl.h" + +#include + +#include "envoy/common/exception.h" + +#include "source/common/config/utility.h" + +namespace Envoy { +namespace CertificateProvider { + +CertificateProviderManagerImpl::CertificateProviderManagerImpl(Api::Api& api) : api_(api) {} + +void CertificateProviderManagerImpl::addCertificateProvider( + absl::string_view name, const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) { + auto& cert_provider_factory = + Envoy::Config::Utility::getAndCheckFactory(config); + + auto cert_provider_instance = + cert_provider_factory.createCertificateProviderInstance(config, factory_context, api_); + certificate_provider_instances_.emplace(name, cert_provider_instance); +} + +Envoy::CertificateProvider::CertificateProviderSharedPtr +CertificateProviderManagerImpl::getCertificateProvider(absl::string_view name) { + auto it = certificate_provider_instances_.find(name); + if (it != certificate_provider_instances_.end()) { + return it->second; + } else { + return nullptr; + } +} + +} // namespace CertificateProvider +} // namespace Envoy diff --git a/source/common/certificate_provider/certificate_provider_manager_impl.h b/source/common/certificate_provider/certificate_provider_manager_impl.h new file mode 100644 index 0000000000..4d74bbcbda --- /dev/null +++ b/source/common/certificate_provider/certificate_provider_manager_impl.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include "envoy/api/api.h" +#include "envoy/certificate_provider/certificate_provider_factory.h" +#include "envoy/certificate_provider/certificate_provider_manager.h" +#include "envoy/config/core/v3/extension.pb.h" + +#include "absl/container/flat_hash_map.h" + +namespace Envoy { +namespace CertificateProvider { + +/** + * A manager for certificate provider instances. + */ +class CertificateProviderManagerImpl : public CertificateProviderManager { +public: + CertificateProviderManagerImpl(Api::Api& api); + + void addCertificateProvider( + absl::string_view name, const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context) override; + + CertificateProviderSharedPtr getCertificateProvider(absl::string_view name) override; + +private: + absl::flat_hash_map certificate_provider_instances_; + Api::Api& api_; +}; + +} // namespace CertificateProvider +} // namespace Envoy diff --git a/source/common/common/logger.h b/source/common/common/logger.h index fe6fcead67..cfd8bb03fb 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -33,6 +33,7 @@ namespace Logger { FUNCTION(assert) \ FUNCTION(backtrace) \ FUNCTION(cache_filter) \ + FUNCTION(cert_provider) \ FUNCTION(client) \ FUNCTION(config) \ FUNCTION(connection) \ diff --git a/source/common/event/dispatcher_impl.cc b/source/common/event/dispatcher_impl.cc index 104d72afc9..dbfab31089 100644 --- a/source/common/event/dispatcher_impl.cc +++ b/source/common/event/dispatcher_impl.cc @@ -149,10 +149,12 @@ void DispatcherImpl::clearDeferredDeleteList() { Network::ServerConnectionPtr DispatcherImpl::createServerConnection(Network::ConnectionSocketPtr&& socket, Network::TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info) { + StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory) { ASSERT(isThreadSafe()); return std::make_unique( - *this, std::move(socket), std::move(transport_socket), stream_info, true); + *this, std::move(socket), std::move(transport_socket), stream_info, + transport_socket_factory, true); } Network::ClientConnectionPtr DispatcherImpl::createClientConnection( diff --git a/source/common/event/dispatcher_impl.h b/source/common/event/dispatcher_impl.h index a55738a02e..9de8017077 100644 --- a/source/common/event/dispatcher_impl.h +++ b/source/common/event/dispatcher_impl.h @@ -65,7 +65,8 @@ class DispatcherImpl : Logger::Loggable, Network::ServerConnectionPtr createServerConnection(Network::ConnectionSocketPtr&& socket, Network::TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info) override; + StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory) override; Network::ClientConnectionPtr createClientConnection( Network::Address::InstanceConstSharedPtr address, Network::Address::InstanceConstSharedPtr source_address, diff --git a/source/common/network/connection_impl.cc b/source/common/network/connection_impl.cc index 796fa59922..841a822559 100644 --- a/source/common/network/connection_impl.cc +++ b/source/common/network/connection_impl.cc @@ -588,7 +588,9 @@ void ConnectionImpl::onFileEvent(uint32_t events) { } if (events & Event::FileReadyType::Write) { - onWriteReady(); + if (!write_disable) { + onWriteReady(); + } } // It's possible for a write event callback to close the socket (which will cause fd_ to be -1). @@ -811,9 +813,11 @@ void ConnectionImpl::dumpState(std::ostream& os, int indent_level) const { ServerConnectionImpl::ServerConnectionImpl(Event::Dispatcher& dispatcher, ConnectionSocketPtr&& socket, TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info, bool connected) + StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory, + bool connected) : ConnectionImpl(dispatcher, std::move(socket), std::move(transport_socket), stream_info, - connected) {} + connected), transport_socket_factory_(transport_socket_factory) {} void ServerConnectionImpl::setTransportSocketConnectTimeout(std::chrono::milliseconds timeout, Stats::Counter& timeout_stat) { @@ -851,6 +855,16 @@ void ServerConnectionImpl::onTransportSocketConnectTimeout() { setFailureReason("connect timeout"); } +void ServerConnectionImpl::refreshTransportSocket() { + ENVOY_CONN_LOG(info, "refresh transport socket", *this); + auto transport_socket = transport_socket_factory_.createDownstreamTransportSocket(); + std::swap(transport_socket, transport_socket_); + transport_socket_->setTransportSocketCallbacks(*this); + // Currently setSslConnection doesn't allow swapping connectioninfo if old connection info exists + // Revisit this when related issues are discovered. + //socket_->connectionInfoProvider().setSslConnection(transport_socket_->ssl()); +} + ClientConnectionImpl::ClientConnectionImpl( Event::Dispatcher& dispatcher, const Address::InstanceConstSharedPtr& remote_address, const Network::Address::InstanceConstSharedPtr& source_address, diff --git a/source/common/network/connection_impl.h b/source/common/network/connection_impl.h index bf9933f180..7af1cf782f 100644 --- a/source/common/network/connection_impl.h +++ b/source/common/network/connection_impl.h @@ -239,13 +239,14 @@ class ServerConnectionImpl : public ConnectionImpl, virtual public ServerConnect public: ServerConnectionImpl(Event::Dispatcher& dispatcher, ConnectionSocketPtr&& socket, TransportSocketPtr&& transport_socket, StreamInfo::StreamInfo& stream_info, - bool connected); + const DownstreamTransportSocketFactory& transport_socket_factory, bool connected); // ServerConnection impl void setTransportSocketConnectTimeout(std::chrono::milliseconds timeout, Stats::Counter& timeout_stat) override; void raiseEvent(ConnectionEvent event) override; + void refreshTransportSocket(); private: void onTransportSocketConnectTimeout(); @@ -254,6 +255,7 @@ class ServerConnectionImpl : public ConnectionImpl, virtual public ServerConnect // call to setTransportSocketConnectTimeout and is reset when the connection is established. Event::TimerPtr transport_socket_connect_timer_; Stats::Counter* transport_socket_timeout_stat_; + const Network::DownstreamTransportSocketFactory& transport_socket_factory_; }; /** diff --git a/source/common/ssl/certificate_validation_context_config_impl.cc b/source/common/ssl/certificate_validation_context_config_impl.cc index 034409ad38..73bb485384 100644 --- a/source/common/ssl/certificate_validation_context_config_impl.cc +++ b/source/common/ssl/certificate_validation_context_config_impl.cc @@ -19,8 +19,9 @@ static const std::string INLINE_STRING = ""; CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl( const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& config, - Api::Api& api) - : ca_cert_(Config::DataSource::read(config.trusted_ca(), true, api)), + Api::Api& api, Server::Configuration::TransportSocketFactoryContext& factory_context) + : ca_cert_(config.has_trusted_ca() ? Config::DataSource::read(config.trusted_ca(), true, api) + : EMPTY_STRING), ca_cert_path_(Config::DataSource::getPath(config.trusted_ca()) .value_or(ca_cert_.empty() ? EMPTY_STRING : INLINE_STRING)), certificate_revocation_list_(Config::DataSource::read(config.crl(), true, api)), @@ -43,7 +44,14 @@ CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl( max_verify_depth_(config.has_max_verify_depth() ? absl::optional(config.max_verify_depth().value()) : absl::nullopt) { - if (ca_cert_.empty() && custom_validator_config_ == absl::nullopt) { + + if (config.has_ca_certificate_provider_instance()) { + ca_provider_instance_ = factory_context.certificateProviderManager().getCertificateProvider( + config.ca_certificate_provider_instance().instance_name()); + ca_provider_cert_name_ = config.ca_certificate_provider_instance().certificate_name(); + } + if (ca_cert_.empty() && ca_provider_instance_ == nullptr && + custom_validator_config_ == absl::nullopt) { if (!certificate_revocation_list_.empty()) { throw EnvoyException(fmt::format("Failed to load CRL from {} without trusted CA", certificateRevocationListPath())); @@ -58,6 +66,15 @@ CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl( } } +void CertificateValidationContextConfigImpl::setCAUpdateCallback(std::function callback) { + if (ca_provider_instance_) { + ca_update_callback_handle_ = + ca_provider_instance_->addUpdateCallback(ca_provider_cert_name_, [this, callback]() { + ca_cert_ = ca_provider_instance_->trustedCA(ca_provider_cert_name_); + callback(); + }); + } +} std::vector CertificateValidationContextConfigImpl::getSubjectAltNameMatchers( const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& config) { diff --git a/source/common/ssl/certificate_validation_context_config_impl.h b/source/common/ssl/certificate_validation_context_config_impl.h index b93d67fe6d..eb899bc646 100644 --- a/source/common/ssl/certificate_validation_context_config_impl.h +++ b/source/common/ssl/certificate_validation_context_config_impl.h @@ -5,6 +5,7 @@ #include "envoy/api/api.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" #include "envoy/extensions/transport_sockets/tls/v3/common.pb.h" +#include "envoy/server/transport_socket_config.h" #include "envoy/ssl/certificate_validation_context_config.h" #include "envoy/type/matcher/v3/string.pb.h" @@ -15,7 +16,7 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte public: CertificateValidationContextConfigImpl( const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& config, - Api::Api& api); + Api::Api& api, Server::Configuration::TransportSocketFactoryContext& factory_context); const std::string& caCert() const override { return ca_cert_; } const std::string& caCertPath() const override { return ca_cert_path_; } @@ -53,11 +54,17 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte absl::optional maxVerifyDepth() const override { return max_verify_depth_; } + Envoy::CertificateProvider::CertificateProviderSharedPtr caProvider() const override { + return ca_provider_instance_; + }; + + void setCAUpdateCallback(std::function callback) override; + private: static std::vector getSubjectAltNameMatchers( const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext& config); - const std::string ca_cert_; + std::string ca_cert_; const std::string ca_cert_path_; const std::string certificate_revocation_list_; const std::string certificate_revocation_list_path_; @@ -72,6 +79,9 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte Api::Api& api_; const bool only_verify_leaf_cert_crl_; absl::optional max_verify_depth_; + Envoy::CertificateProvider::CertificateProviderSharedPtr ca_provider_instance_; + std::string ca_provider_cert_name_; + Envoy::Common::CallbackHandlePtr ca_update_callback_handle_; }; } // namespace Ssl diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index a779bef35a..ad47995d9c 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -58,7 +58,9 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { Api::Api& api, Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, AccessLog::AccessLogManager& log_manager, Singleton::Manager& singleton_manager, const Server::Options& options, - Quic::QuicStatNames& quic_stat_names, const Server::Instance& server) + Quic::QuicStatNames& quic_stat_names, + CertificateProvider::CertificateProviderManager& certificate_provider_manager, + const Server::Instance& server) : server_context_(server_context), context_(options, main_thread_dispatcher, api, local_info, admin, runtime, singleton_manager, validation_context.staticValidationVisitor(), stats, tls), @@ -66,6 +68,7 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { grpc_context_(grpc_context), router_context_(router_context), admin_(admin), stats_(stats), tls_(tls), dns_resolver_(dns_resolver), ssl_context_manager_(ssl_context_manager), local_info_(local_info), secret_manager_(secret_manager), log_manager_(log_manager), + certificate_provider_manager_(certificate_provider_manager), quic_stat_names_(quic_stat_names), alternate_protocols_cache_manager_factory_(singleton_manager, tls_, {context_}), alternate_protocols_cache_manager_(alternate_protocols_cache_manager_factory_.get()), @@ -97,6 +100,9 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { ClusterManager& cm) override; Secret::SecretManager& secretManager() override { return secret_manager_; } Singleton::Manager& singletonManager() override { return server_context_.singletonManager(); } + CertificateProvider::CertificateProviderManager& certificateProviderManager() override { + return certificate_provider_manager_; + } protected: Server::Configuration::ServerFactoryContext& server_context_; @@ -113,6 +119,7 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { const LocalInfo::LocalInfo& local_info_; Secret::SecretManager& secret_manager_; AccessLog::AccessLogManager& log_manager_; + CertificateProvider::CertificateProviderManager& certificate_provider_manager_; Quic::QuicStatNames& quic_stat_names_; Http::HttpServerPropertiesCacheManagerFactoryImpl alternate_protocols_cache_manager_factory_; Http::HttpServerPropertiesCacheManagerSharedPtr alternate_protocols_cache_manager_; diff --git a/source/extensions/certificate_providers/local_certificate/BUILD b/source/extensions/certificate_providers/local_certificate/BUILD new file mode 100644 index 0000000000..e19b545033 --- /dev/null +++ b/source/extensions/certificate_providers/local_certificate/BUILD @@ -0,0 +1,39 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "local_certificate_provider", + srcs = ["local_certificate.cc"], + hdrs = ["local_certificate.h"], + deps = [ + "//envoy/api:api_interface", + "//envoy/certificate_provider:certificate_provider_interface", + "//envoy/event:dispatcher_interface", + "//envoy/server:transport_socket_config_interface", + "//source/common/common:callback_impl_lib", + "//source/common/common:logger_lib", + "//source/common/common:utility_lib", + "//source/common/config:datasource_lib", + "//source/common/protobuf:message_validator_lib", + "@envoy_api//envoy/extensions/certificate_providers/local_certificate/v3:pkg_cc_proto", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":local_certificate_provider", + "//envoy/certificate_provider:certificate_provider_factory_lib", + "//envoy/certificate_provider:certificate_provider_interface", + ], +) diff --git a/source/extensions/certificate_providers/local_certificate/config.cc b/source/extensions/certificate_providers/local_certificate/config.cc new file mode 100644 index 0000000000..e0a4f200ce --- /dev/null +++ b/source/extensions/certificate_providers/local_certificate/config.cc @@ -0,0 +1,30 @@ +#include "source/extensions/certificate_providers/local_certificate/config.h" + +#include "envoy/certificate_provider/certificate_provider.h" + +#include "source/extensions/certificate_providers/local_certificate/local_certificate.h" + +#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" + +namespace Envoy { +namespace Extensions { +namespace CertificateProviders { +namespace LocalCertificate { + +CertificateProvider::CertificateProviderSharedPtr LocalCertificateFactory::createCertificateProviderInstance( + const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) { + return std::make_shared(config, factory_context, api); +} + +ProtobufTypes::MessagePtr LocalCertificateFactory::createEmptyConfigProto() { + return ProtobufTypes::MessagePtr{ + new envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate()}; +} + +REGISTER_FACTORY(LocalCertificateFactory, CertificateProvider::CertificateProviderFactory); +} // namespace LocalCertificate +} // namespace CertificateProviders +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/certificate_providers/local_certificate/config.h b/source/extensions/certificate_providers/local_certificate/config.h new file mode 100644 index 0000000000..cb112ad0c3 --- /dev/null +++ b/source/extensions/certificate_providers/local_certificate/config.h @@ -0,0 +1,29 @@ +#pragma once + +//#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" +#include "envoy/certificate_provider/certificate_provider.h" +#include "envoy/certificate_provider/certificate_provider_factory.h" + +namespace Envoy { +namespace Extensions { +namespace CertificateProviders { +namespace LocalCertificate { + +/** + * Config registration for the LocalCertificate provider. @see + * CertificateProvider::CertificateProviderFactory + */ +class LocalCertificateFactory : public CertificateProvider::CertificateProviderFactory { +public: + std::string name() const override { return "envoy.certificate_providers.local_certificate"; }; + CertificateProvider::CertificateProviderSharedPtr createCertificateProviderInstance( + const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context, + Api::Api& api) override; + + ProtobufTypes::MessagePtr createEmptyConfigProto() override; +}; +} // namespace LocalCertificate +} // namespace CertificateProviders +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc new file mode 100644 index 0000000000..a9f579f164 --- /dev/null +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -0,0 +1,229 @@ +#include "source/extensions/certificate_providers/local_certificate/local_certificate.h" + +#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" + +#include "source/common/common/logger.h" +#include "source/common/config/datasource.h" +#include "source/common/config/utility.h" +#include "source/common/protobuf/message_validator_impl.h" + +namespace Envoy { +namespace Extensions { +namespace CertificateProviders { +namespace LocalCertificate { + +Provider::Provider(const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context, + Api::Api& api) + : main_thread_dispatcher_(factory_context.mainThreadDispatcher()) { + envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate message; + Config::Utility::translateOpaqueConfig(config.typed_config(), + ProtobufMessage::getStrictValidationVisitor(), message); + ca_cert_ = Config::DataSource::read(message.rootca_cert(), true, api); + ca_key_ = Config::DataSource::read(message.rootca_key(), true, api); + default_identity_cert_ = Config::DataSource::read(message.default_identity_cert(), true, api); + default_identity_key_ = Config::DataSource::read(message.default_identity_key(), true, api); + // Generate TLSCertificate + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); + tls_certificate->mutable_certificate_chain()->set_inline_string(default_identity_cert_); + tls_certificate->mutable_private_key()->set_inline_string(default_identity_key_); + // Update certificates_ map + { + absl::WriterMutexLock writer_lock{&certificates_lock_}; + certificates_.try_emplace("default", tls_certificate); + } +} + +Envoy::CertificateProvider::CertificateProvider::Capabilities Provider::capabilities() const { + Envoy::CertificateProvider::CertificateProvider::Capabilities cap; + cap.provide_on_demand_identity_certs = true; + return cap; +} + +const std::string Provider::trustedCA(const std::string&) const { return ""; } + +std::vector> +Provider::tlsCertificates(const std::string&) const { + std::vector> result; + absl::ReaderMutexLock reader_lock{&certificates_lock_}; + for (auto [_, value] : certificates_) { + result.push_back(*value); + } + return result; +} + +Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateCallback( + const std::string sni, ::Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + Event::Dispatcher& thread_local_dispatcher, + Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) { + + auto handle = std::make_unique( + on_demand_update_callbacks_, sni, callback); + + // TODO: we need to improve this cache_hit check + // It is possible that two SNIs use the same cert, so they share the same SANs. + // if we generate two mimic certs for these SNIs, it can not pass the certs config check + // since we do not allow duplicated SANs. + // We need to align this cache_hit with current transport socket behavior + bool cache_hit = [&]() { + absl::ReaderMutexLock reader_lock{&certificates_lock_}; + auto it = certificates_.find(sni); + return it != certificates_.end() ? true : false; + }(); + + if (cache_hit) { + // Cache hit, run on-demand update callback directly + runOnDemandUpdateCallback(sni, thread_local_dispatcher, true); + } else { + // Cache miss, generate self-signed cert + main_thread_dispatcher_.post([sni, metadata, &thread_local_dispatcher, this] { + signCertificate(sni, metadata, thread_local_dispatcher); }); + } + return handle; +} + +Common::CallbackHandlePtr Provider::addUpdateCallback(const std::string&, + std::function callback) { + return update_callback_manager_.add(callback); +} + +void Provider::runAddUpdateCallback() { update_callback_manager_.runCallbacks(); } + +void Provider::runOnDemandUpdateCallback(const std::string& host, + Event::Dispatcher& thread_local_dispatcher, + bool in_cache) { + auto host_it = on_demand_update_callbacks_.find(host); + if (host_it != on_demand_update_callbacks_.end()) { + for (auto* pending_callbacks : host_it->second) { + auto& callbacks = pending_callbacks->callbacks_; + pending_callbacks->cancel(); + if (in_cache) { + thread_local_dispatcher.post([&callbacks, host] { callbacks.onCacheHit(host); }); + } else { + thread_local_dispatcher.post([&callbacks, host] { callbacks.onCacheMiss(host); }); + } + } + on_demand_update_callbacks_.erase(host_it); + } +} + +// TODO: we meed to copy more information of original cert, such as SANs, the whole subject, +// expiration time, etc. +void Provider::signCertificate(const std::string sni, + ::Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + Event::Dispatcher& thread_local_dispatcher) { + bssl::UniquePtr bio(BIO_new_mem_buf(const_cast(ca_cert_.data()), ca_cert_.size())); + RELEASE_ASSERT(bio != nullptr, ""); + bssl::UniquePtr ca_cert; + ca_cert.reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + + bio.reset(BIO_new_mem_buf(const_cast(ca_key_.data()), ca_key_.size())); + RELEASE_ASSERT(bio != nullptr, ""); + bssl::UniquePtr ca_key; + ca_key.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); + + /********* generate identity certificate locally *****/ + EVP_PKEY* key = EVP_PKEY_new(); + X509* crt = X509_new(); + + X509_REQ* req = X509_REQ_new(); + BIGNUM* bne = BN_new(); + BN_set_word(bne, RSA_F4); + RSA* rsa = RSA_new(); + RSA_generate_key_ex(rsa, 2048, bne, nullptr); + X509_REQ_set_version(req, 0); + + X509_NAME* x509_name = X509_REQ_get_subject_name(req); + setSubject(metadata->connectionInfo()->subjectPeerCertificate().data(), x509_name); + X509_set_subject_name(crt, x509_name); + + EVP_PKEY_assign_RSA(key, rsa); + X509_REQ_set_pubkey(req, key); + X509_REQ_sign(req, key, EVP_sha1()); // return x509_req->signature->length + + X509_set_version(crt, 2); + auto gens = sk_GENERAL_NAME_new_null(); + + for (auto& dns : metadata->connectionInfo()->dnsSansPeerCertificate()) { + auto ia5 = ASN1_IA5STRING_new(); + ASN1_STRING_set(ia5, dns.c_str(), -1); + auto gen = GENERAL_NAME_new(); + GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); + sk_GENERAL_NAME_push(gens, gen); + } + + for (auto& uri : metadata->connectionInfo()->uriSanPeerCertificate()) { + auto ia5 = ASN1_IA5STRING_new(); + ASN1_STRING_set(ia5, uri.c_str(), -1); + auto gen = GENERAL_NAME_new(); + GENERAL_NAME_set0_value(gen, GEN_URI, ia5); + sk_GENERAL_NAME_push(gens, gen); + } + + X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, 0); + //X509_EXTENSION* ext = + // X509V3_EXT_nconf_nid(nullptr, nullptr, NID_subject_alt_name, subAltName.c_str()); + //X509_add_ext(crt, ext, -1); + + X509_set_issuer_name(crt, X509_get_subject_name(ca_cert.get())); + X509_gmtime_adj(X509_get_notBefore(crt), 0); + X509_gmtime_adj(X509_get_notAfter(crt), 2 * 365 * 24 * 3600); + X509_set_subject_name(crt, X509_REQ_get_subject_name(req)); + EVP_PKEY* req_pubkey = X509_REQ_get_pubkey(req); + X509_set_pubkey(crt, req_pubkey); + X509_sign(crt, ca_key.get(), EVP_sha256()); + /********* generate identity certificate locally *****/ + + bssl::UniquePtr buf(BIO_new(BIO_s_mem())); + PEM_write_bio_X509(buf.get(), crt); + const uint8_t* output; + size_t length; + BIO_mem_contents(buf.get(), &output, &length); + std::string cert_pem(reinterpret_cast(output), length); + buf.reset(BIO_new(BIO_s_mem())); + PEM_write_bio_PrivateKey(buf.get(), key, nullptr, nullptr, length, nullptr, nullptr); + BIO_mem_contents(buf.get(), &output, &length); + std::string key_pem(reinterpret_cast(output), length); + + // Generate TLSCertificate + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); + tls_certificate->mutable_certificate_chain()->set_inline_string(cert_pem); + tls_certificate->mutable_private_key()->set_inline_string(key_pem); + + // Update certificates_ map + { + absl::WriterMutexLock writer_lock{&certificates_lock_}; + certificates_.try_emplace( + sni, tls_certificate); + } + + runAddUpdateCallback(); + runOnDemandUpdateCallback(sni, thread_local_dispatcher, false); +} + +void Provider::setSubject(const std::string& subject, X509_NAME* x509_name) { + const std::string delim = ", "; + std::string item; + size_t start = 0, end = subject.find(delim), pos = 0; + for ( ; end != std::string::npos; start = end + 2, end = subject.find(delim, start)) { + item = subject.substr(start, end - start); + if ((pos = item.find("=")) != std::string::npos) { + //X509_NAME_add_entry_by_txt(x509_name, "CN", MBSTRING_ASC, + // reinterpret_cast(szCommon), -1, -1, 0); + // reference: https://github.com/openssl/openssl/blob/master/test/v3nametest.c + X509_NAME_add_entry_by_txt(x509_name, item.substr(0, pos).c_str(), MBSTRING_ASC, + reinterpret_cast(item.substr(pos + 1).c_str()), + -1, -1, 0); + } + } + item = subject.substr(start, subject.length() - start); + if ((pos = item.find("=")) != std::string::npos) { + X509_NAME_add_entry_by_txt(x509_name, item.substr(0, pos).c_str(), MBSTRING_ASC, + reinterpret_cast(item.substr(pos + 1).c_str()), + -1, -1, 0); + } +} +} // namespace LocalCertificate +} // namespace CertificateProviders +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h new file mode 100644 index 0000000000..d013a9e501 --- /dev/null +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -0,0 +1,75 @@ +#pragma once + +#include "envoy/certificate_provider/certificate_provider.h" +#include "envoy/common/callback.h" +#include "envoy/event/dispatcher.h" +#include "envoy/server/transport_socket_config.h" + +#include "source/common/common/callback_impl.h" +#include "source/common/common/logger.h" + +namespace Envoy { +namespace Extensions { +namespace CertificateProviders { +namespace LocalCertificate { + +// Local cert provider +class Provider : public CertificateProvider::CertificateProvider, + Logger::Loggable { +public: + Provider(const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api); + + // CertificateProvider::CertificateProvider + Envoy::CertificateProvider::CertificateProvider::Capabilities capabilities() const override; + const std::string trustedCA(const std::string& cert_name) const override; + std::vector> + tlsCertificates(const std::string& cert_name) const override; + Envoy::CertificateProvider::OnDemandUpdateHandlePtr addOnDemandUpdateCallback( + const std::string cert_name, + Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + Event::Dispatcher& thread_local_dispatcher, + ::Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) override; + Common::CallbackHandlePtr addUpdateCallback(const std::string& cert_name, + std::function callback) override; + +private: + struct OnDemandUpdateHandleImpl : public ::Envoy::CertificateProvider::OnDemandUpdateHandle, + RaiiMapOfListElement { + OnDemandUpdateHandleImpl( + absl::flat_hash_map>& parent, + absl::string_view host, ::Envoy::CertificateProvider::OnDemandUpdateCallbacks& callbacks) + : RaiiMapOfListElement(parent, host, this), + callbacks_(callbacks) {} + + ::Envoy::CertificateProvider::OnDemandUpdateCallbacks& callbacks_; + }; + + mutable absl::Mutex certificates_lock_; + absl::flat_hash_map + certificates_ ABSL_GUARDED_BY(certificates_lock_); + + void runAddUpdateCallback(); + void runOnDemandUpdateCallback(const std::string& host, + Event::Dispatcher& thread_local_dispatcher, bool in_cache = true); + //void signCertificate(std::string sni, absl::Span dns_sans, const std::string subject, + // Event::Dispatcher& thread_local_dispatcher); + void signCertificate(const std::string sni, + Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + Event::Dispatcher& thread_local_dispatcher); + void setSubject(const std::string& subject, X509_NAME* x509_name); + Event::Dispatcher& main_thread_dispatcher_; + std::string ca_cert_; + std::string ca_key_; + std::string default_identity_cert_; + std::string default_identity_key_; + + Common::CallbackManager<> update_callback_manager_; + absl::flat_hash_map> + on_demand_update_callbacks_; +}; +} // namespace LocalCertificate +} // namespace CertificateProviders +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index df452a4beb..0d4c340093 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -154,7 +154,7 @@ EXTENSIONS = { "envoy.filters.network.sni_dynamic_forward_proxy": "//source/extensions/filters/network/sni_dynamic_forward_proxy:config", "envoy.filters.network.wasm": "//source/extensions/filters/network/wasm:config", "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", - + "envoy.filters.network.bumping": "//source/extensions/filters/network/bumping:config", # # UDP filters # @@ -367,6 +367,12 @@ EXTENSIONS = { # "envoy.route.early_data_policy.default": "//source/extensions/early_data:default_early_data_policy_lib", + + # + # Certificate providers + # + + "envoy.certificate_providers.local_certificate": "//source/extensions/certificate_providers/local_certificate:config", } # These can be changed to ["//visibility:public"], for downstream builds which diff --git a/source/extensions/extensions_metadata.yaml b/source/extensions/extensions_metadata.yaml index 019d495084..0e5be724d5 100644 --- a/source/extensions/extensions_metadata.yaml +++ b/source/extensions/extensions_metadata.yaml @@ -614,6 +614,13 @@ envoy.filters.network.zookeeper_proxy: status: alpha type_urls: - envoy.extensions.filters.network.zookeeper_proxy.v3.ZooKeeperProxy +envoy.filters.network.bumping: + categories: + - envoy.filters.network + security_posture: unknown + status: alpha + type_urls: + - envoy.extensions.filters.network.bumping.v3.Bumping envoy.filters.thrift.header_to_metadata: categories: - envoy.thrift_proxy.filters @@ -1280,3 +1287,10 @@ envoy.matching.custom_matchers.trie_matcher: status: alpha type_urls: - xds.type.matcher.v3.IPMatcher +envoy.certificate_providers.local_certificate: + categories: + - envoy.certificate_providers + security_posture: unknown + status: alpha + type_urls: + - envoy.extensions.certificate_providers.local_certificate.v3.LocalCertificate diff --git a/source/extensions/filters/network/bumping/BUILD b/source/extensions/filters/network/bumping/BUILD new file mode 100644 index 0000000000..d74c928582 --- /dev/null +++ b/source/extensions/filters/network/bumping/BUILD @@ -0,0 +1,79 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_library( + name = "bumping_lib", + srcs = [ + "bumping.cc", + ], + hdrs = [ + "bumping.h", + ], + external_deps = [ + "ssl", + ], + deps = [ + "//envoy/access_log:access_log_interface", + "//envoy/buffer:buffer_interface", + "//envoy/common:time_interface", + "//envoy/certificate_provider:certificate_provider_interface", + "//envoy/event:dispatcher_interface", + "//envoy/network:connection_interface", + "//envoy/network:filter_interface", + "//envoy/router:router_interface", + "//envoy/server:filter_config_interface", + "//envoy/server:transport_socket_config_interface", + "//envoy/stats:stats_interface", + "//envoy/stats:stats_macros", + "//envoy/stats:timespan_interface", + "//envoy/stream_info:filter_state_interface", + "//envoy/tcp:conn_pool_interface", + "//envoy/tcp:upstream_interface", + "//envoy/upstream:cluster_manager_interface", + "//envoy/upstream:upstream_interface", + "//source/common/access_log:access_log_lib", + "//source/common/common:assert_lib", + "//source/common/common:empty_string", + "//source/common/common:macros", + "//source/common/common:minimal_logger_lib", + "//source/common/http:codec_client_lib", + "//source/common/network:application_protocol_lib", + "//source/common/network:cidr_range_lib", + "//source/common/network:filter_lib", + "//source/common/network:connection_impl", + "//source/common/network:proxy_protocol_filter_state_lib", + "//source/common/network:socket_option_factory_lib", + "//source/common/network:transport_socket_options_lib", + "//source/common/network:upstream_server_name_lib", + "//source/common/network:upstream_socket_options_filter_state_lib", + "//source/common/network:utility_lib", + "//source/common/stream_info:stream_info_lib", + "//source/common/upstream:load_balancer_lib", + "//source/extensions/filters/network:well_known_names", + "//source/extensions/upstreams/tcp/generic:config", + "@envoy_api//envoy/config/accesslog/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/filters/network/bumping/v3:pkg_cc_proto", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + ":bumping_lib", + "//envoy/registry", + "//source/common/tcp_proxy", + "//source/extensions/filters/network:well_known_names", + "//source/extensions/filters/network/common:factory_base_lib", + "@envoy_api//envoy/extensions/filters/network/bumping/v3:pkg_cc_proto", + ], +) \ No newline at end of file diff --git a/source/extensions/filters/network/bumping/bumping.cc b/source/extensions/filters/network/bumping/bumping.cc new file mode 100644 index 0000000000..d46232ffc3 --- /dev/null +++ b/source/extensions/filters/network/bumping/bumping.cc @@ -0,0 +1,330 @@ +#include "source/extensions/filters/network/bumping/bumping.h" + +#include +#include +#include + +#include "envoy/buffer/buffer.h" +#include "envoy/config/accesslog/v3/accesslog.pb.h" +#include "envoy/event/dispatcher.h" +#include "envoy/event/timer.h" +#include "envoy/extensions/filters/network/bumping/v3/bumping.pb.h" +#include "envoy/server/transport_socket_config.h" +#include "envoy/stats/scope.h" +#include "envoy/upstream/cluster_manager.h" +#include "envoy/upstream/upstream.h" + +#include "source/common/access_log/access_log_impl.h" +#include "source/common/common/assert.h" +#include "source/common/common/empty_string.h" +#include "source/common/common/enum_to_int.h" +#include "source/common/common/fmt.h" +#include "source/common/common/macros.h" +#include "source/common/common/utility.h" +#include "source/common/config/utility.h" +#include "source/common/config/well_known_names.h" +#include "source/common/network/application_protocol.h" +#include "source/common/network/connection_impl.h" +#include "source/common/network/proxy_protocol_filter_state.h" +#include "source/common/network/socket_option_factory.h" +#include "source/common/network/transport_socket_options_impl.h" +#include "source/common/network/upstream_server_name.h" +#include "source/common/network/upstream_socket_options_filter_state.h" + +namespace Envoy { +namespace Bumping { + +Config::SimpleRouteImpl::SimpleRouteImpl(const Config& parent, absl::string_view cluster_name) + : parent_(parent), cluster_name_(cluster_name) {} + +Config::Config(const envoy::extensions::filters::network::bumping::v3::Bumping& config, + Server::Configuration::FactoryContext& context) + : main_dispatcher_(context.getServerFactoryContext().mainThreadDispatcher()), context_(context), + stats_scope_(context.scope().createScope(fmt::format("bumping.{}", config.stat_prefix()))), + stats_(generateStats(*stats_scope_)), + max_connect_attempts_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_connect_attempts, 1)) { + if (!config.cluster().empty()) { + default_route_ = std::make_shared(*this, config.cluster()); + } + + for (const envoy::config::accesslog::v3::AccessLog& log_config : config.access_log()) { + access_logs_.emplace_back(AccessLog::AccessLogFactory::fromProto(log_config, context)); + } + + if (config.has_tls_certificate_provider_instance()) { + tls_certificate_provider_ = + context.getTransportSocketFactoryContext() + .certificateProviderManager() + .getCertificateProvider(config.tls_certificate_provider_instance().instance_name()); + tls_certificate_name_ = config.tls_certificate_provider_instance().certificate_name(); + } +} + +BumpingStats Config::generateStats(Stats::Scope& scope) { + return {ALL_BUMPING_STATS(POOL_COUNTER(scope))}; +} + +RouteConstSharedPtr Config::getRoute() { + if (default_route_ != nullptr) { + return default_route_; + } + + // no match, no more routes to try + return nullptr; +} + +Filter::Filter(ConfigSharedPtr config, Upstream::ClusterManager& cluster_manager) + : config_(config), cluster_manager_(cluster_manager), + upstream_callbacks_(new UpstreamCallbacks(this)) { + ASSERT(config != nullptr); +} + +Filter::~Filter() { + for (const auto& access_log : config_->accessLogs()) { + access_log->log(nullptr, nullptr, nullptr, getStreamInfo()); + } +} + +void Filter::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) { + read_callbacks_ = &callbacks; + ENVOY_CONN_LOG(debug, "new bumping session", read_callbacks_->connection()); + + // Need to disable reads and writes to postpone downstream handshakes. + // This will get re-enabled when transport socket is refreshed with mimic cert. + // TODO, needs refactoring + read_callbacks_->connection().readDisable(true); + read_callbacks_->connection().write_disable = true; +} + +StreamInfo::StreamInfo& Filter::getStreamInfo() { + return read_callbacks_->connection().streamInfo(); +} + +void Filter::UpstreamCallbacks::onEvent(Network::ConnectionEvent event) { + if (event == Network::ConnectionEvent::Connected || + event == Network::ConnectionEvent::ConnectedZeroRtt) { + return; + } + parent_->onUpstreamEvent(event); +} + +Network::FilterStatus Filter::establishUpstreamConnection() { + const std::string& cluster_name = route_ ? route_->clusterName() : EMPTY_STRING; + Upstream::ThreadLocalCluster* thread_local_cluster = + cluster_manager_.getThreadLocalCluster(cluster_name); + + if (!thread_local_cluster) { + ENVOY_CONN_LOG(debug, "Cluster not found {}.", read_callbacks_->connection(), cluster_name); + config_->stats().downstream_cx_no_route_.inc(); + getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::NoClusterFound); + onInitFailure(UpstreamFailureReason::NoRoute); + return Network::FilterStatus::StopIteration; + } + + ENVOY_CONN_LOG(debug, "Creating connection to cluster {}", read_callbacks_->connection(), + cluster_name); + + const Upstream::ClusterInfoConstSharedPtr& cluster = thread_local_cluster->info(); + + // Check this here because the TCP conn pool will queue our request waiting for a connection that + // will never be released. + if (!cluster->resourceManager(Upstream::ResourcePriority::Default).connections().canCreate()) { + getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamOverflow); + cluster->stats().upstream_cx_overflow_.inc(); + onInitFailure(UpstreamFailureReason::ResourceLimitExceeded); + return Network::FilterStatus::StopIteration; + } + + const uint32_t max_connect_attempts = config_->maxConnectAttempts(); + if (connect_attempts_ >= max_connect_attempts) { + getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamRetryLimitExceeded); + cluster->stats().upstream_cx_connect_attempts_exceeded_.inc(); + onInitFailure(UpstreamFailureReason::ConnectFailed); + return Network::FilterStatus::StopIteration; + } + + auto& downstream_connection = read_callbacks_->connection(); + auto& filter_state = downstream_connection.streamInfo().filterState(); + transport_socket_options_ = + Network::TransportSocketOptionsUtility::fromFilterState(*filter_state); + + if (auto typed_state = filter_state->getDataReadOnly( + Network::UpstreamSocketOptionsFilterState::key()); + typed_state != nullptr) { + auto downstream_options = typed_state->value(); + if (!upstream_options_) { + upstream_options_ = std::make_shared(); + } + Network::Socket::appendOptions(upstream_options_, downstream_options); + } + + if (!maybeTunnel(*thread_local_cluster)) { + // Either cluster is unknown or there are no healthy hosts. tcpConnPool() increments + // cluster->stats().upstream_cx_none_healthy in the latter case. + getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::NoHealthyUpstream); + onInitFailure(UpstreamFailureReason::NoHealthyUpstream); + } + return Network::FilterStatus::StopIteration; +} + +bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { + TcpProxy::GenericConnPoolFactory* factory = nullptr; + if (cluster.info()->upstreamConfig().has_value()) { + factory = Envoy::Config::Utility::getFactory( + cluster.info()->upstreamConfig().value()); + } else { + factory = Envoy::Config::Utility::getFactoryByName( + "envoy.filters.connection_pools.tcp.generic"); + } + if (!factory) { + return false; + } + + generic_conn_pool_ = factory->createGenericConnPool( + cluster, TcpProxy::TunnelingConfigHelperOptConstRef(), this, *upstream_callbacks_); + if (generic_conn_pool_) { + connecting_ = true; + connect_attempts_++; + getStreamInfo().setAttemptCount(connect_attempts_); + generic_conn_pool_->newStream(*this); + // Because we never return open connections to the pool, this either has a handle waiting on + // connection completion, or onPoolFailure has been invoked. Either way, stop iteration. + return true; + } + return false; +} + +void Filter::onGenericPoolFailure(ConnectionPool::PoolFailureReason reason, + absl::string_view failure_reason, + Upstream::HostDescriptionConstSharedPtr host) { + generic_conn_pool_.reset(); + read_callbacks_->upstreamHost(host); + //TODO fix potential segmentation fault issue here + getStreamInfo().upstreamInfo()->setUpstreamHost(host); + getStreamInfo().upstreamInfo()->setUpstreamTransportFailureReason(failure_reason); + + switch (reason) { + case ConnectionPool::PoolFailureReason::Overflow: + case ConnectionPool::PoolFailureReason::LocalConnectionFailure: + upstream_callbacks_->onEvent(Network::ConnectionEvent::LocalClose); + break; + case ConnectionPool::PoolFailureReason::RemoteConnectionFailure: + upstream_callbacks_->onEvent(Network::ConnectionEvent::RemoteClose); + break; + case ConnectionPool::PoolFailureReason::Timeout: + onConnectTimeout(); + break; + } +} + +void Filter::onGenericPoolReady(StreamInfo::StreamInfo*, + std::unique_ptr&& upstream, + Upstream::HostDescriptionConstSharedPtr&, + const Network::ConnectionInfoProvider&, + Ssl::ConnectionInfoConstSharedPtr info) { + + // Request mimick cert from local certificate provider. + requestCertificate(info); + upstream_ = std::move(upstream); + generic_conn_pool_.reset(); + onUpstreamConnection(); +} + +void Filter::requestCertificate(Ssl::ConnectionInfoConstSharedPtr info) { + ENVOY_CONN_LOG(info, "sni: {}", read_callbacks_->connection(), read_callbacks_->connection().requestedServerName()); + config_->main_dispatcher_.post([this, info]() { + this->on_demand_handle_ = config_->tls_certificate_provider_->addOnDemandUpdateCallback( + std::string(read_callbacks_->connection().requestedServerName()), + std::make_shared(info), + read_callbacks_->connection().dispatcher(), *this); + }); +} + +void Filter::onConnectTimeout() { + ENVOY_CONN_LOG(debug, "connect timeout", read_callbacks_->connection()); + getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure); + + // Raise LocalClose, which will trigger a reconnect if needed/configured. + upstream_callbacks_->onEvent(Network::ConnectionEvent::LocalClose); +} + +Network::FilterStatus Filter::onData(Buffer::Instance&, bool) { + return Network::FilterStatus::Continue; +} + +Network::FilterStatus Filter::onNewConnection() { + ASSERT(upstream_ == nullptr); + route_ = pickRoute(); + return establishUpstreamConnection(); +} + +void Filter::onUpstreamEvent(Network::ConnectionEvent event) { + if (event == Network::ConnectionEvent::ConnectedZeroRtt) { + return; + } + // Update the connecting flag before processing the event because we may start a new connection + // attempt in establishUpstreamConnection. + bool connecting = connecting_; + connecting_ = false; + + if (event == Network::ConnectionEvent::RemoteClose || + event == Network::ConnectionEvent::LocalClose) { + upstream_.reset(); + disableIdleTimer(); + + // happens in on onPoolFailure + if (connecting) { + if (event == Network::ConnectionEvent::RemoteClose) { + getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure); + } + establishUpstreamConnection(); + } + } +} + +void Filter::onUpstreamConnection() { + connecting_ = false; + + ENVOY_CONN_LOG(debug, "TCP:onUpstreamEvent(), requestedServerName: {}", + read_callbacks_->connection(), + getStreamInfo().downstreamAddressProvider().requestedServerName()); +} + +void Filter::resetIdleTimer() { + if (idle_timer_ != nullptr) { + ASSERT(config_->idleTimeout()); + idle_timer_->enableTimer(config_->idleTimeout().value()); + } +} + +void Filter::disableIdleTimer() { + if (idle_timer_ != nullptr) { + idle_timer_->disableTimer(); + idle_timer_.reset(); + } +} + +void Filter::onCacheHit(const std::string) const { + // Re-enable downstream reads and writes + read_callbacks_->connection().readDisable(false); + read_callbacks_->connection().write_disable = false; + + read_callbacks_->continueReading(); +} + +void Filter::onCacheMiss(const std::string) const { + // Recreate transport socket to use newly generate certificate + try{ + dynamic_cast(read_callbacks_->connection()).refreshTransportSocket(); + } + catch(std::bad_cast exp) { + ENVOY_CONN_LOG(warn, "connection cast failed in bumping filter", read_callbacks_->connection()); + } + // Re-enable downstream reads and writes + read_callbacks_->connection().readDisable(false); + read_callbacks_->connection().write_disable = false; + + read_callbacks_->continueReading(); +} +} // namespace Bumping +} // namespace Envoy diff --git a/source/extensions/filters/network/bumping/bumping.h b/source/extensions/filters/network/bumping/bumping.h new file mode 100644 index 0000000000..6374d11f1f --- /dev/null +++ b/source/extensions/filters/network/bumping/bumping.h @@ -0,0 +1,242 @@ +#pragma once + +#include +#include +#include +#include + +#include "envoy/access_log/access_log.h" +#include "envoy/certificate_provider/certificate_provider.h" +#include "envoy/common/random_generator.h" +#include "envoy/event/timer.h" +#include "envoy/extensions/filters/network/bumping/v3/bumping.pb.h" +#include "envoy/http/header_evaluator.h" +#include "envoy/network/connection.h" +#include "envoy/network/filter.h" +#include "envoy/runtime/runtime.h" +#include "envoy/server/filter_config.h" +#include "envoy/stats/scope.h" +#include "envoy/stats/stats_macros.h" +#include "envoy/stats/timespan.h" +#include "envoy/stream_info/filter_state.h" +#include "envoy/tcp/upstream.h" +#include "envoy/upstream/cluster_manager.h" +#include "envoy/upstream/upstream.h" + +#include "source/common/common/logger.h" +#include "source/common/network/cidr_range.h" +#include "source/common/network/filter_impl.h" +#include "source/common/network/utility.h" +#include "source/common/stream_info/stream_info_impl.h" +#include "source/common/upstream/load_balancer_impl.h" + +namespace Envoy { +namespace Bumping { + +/** + * All bumping stats. @see stats_macros.h + */ +#define ALL_BUMPING_STATS(COUNTER) COUNTER(downstream_cx_no_route) +/** + * Struct definition for all bumping stats. @see stats_macros.h + */ +struct BumpingStats { + ALL_BUMPING_STATS(GENERATE_COUNTER_STRUCT); +}; + +/** + * Route is an individual resolved route for a connection. + */ +class Route { +public: + virtual ~Route() = default; + + /** + * Check whether this route matches a given connection. + * @param connection supplies the connection to test against. + * @return bool true if this route matches a given connection. + */ + virtual bool matches(Network::Connection& connection) const PURE; + + /** + * @return const std::string& the upstream cluster that owns the route. + */ + virtual const std::string& clusterName() const PURE; +}; + +using RouteConstSharedPtr = std::shared_ptr; + +/** + * Filter configuration. + * + * This configuration holds a TLS slot, and therefore it must be destructed + * on the main thread. + */ +class Config { +public: + Config(const envoy::extensions::filters::network::bumping::v3::Bumping& config, + Server::Configuration::FactoryContext& context); + + /** + * Find out which cluster an upstream connection should be opened to based on the + * parameters of a downstream connection. + * @param connection supplies the parameters of the downstream connection for + * which the proxy needs to open the corresponding upstream. + * @return the route to be used for the upstream connection. + * If no route applies, returns nullptr. + */ + RouteConstSharedPtr getRoute(); + const BumpingStats& stats() { return stats_; } + const std::vector& accessLogs() { return access_logs_; } + uint32_t maxConnectAttempts() const { return max_connect_attempts_; } + const absl::optional& idleTimeout() { return idle_timeout_; } + Event::Dispatcher& main_dispatcher_; + Server::Configuration::FactoryContext& context_; + CertificateProvider::CertificateProviderSharedPtr tls_certificate_provider_; + std::string tls_certificate_name_; + +private: + struct SimpleRouteImpl : public Route { + SimpleRouteImpl(const Config& parent, absl::string_view cluster_name); + + // Route + bool matches(Network::Connection&) const override { return true; } + const std::string& clusterName() const override { return cluster_name_; } + + const Config& parent_; + std::string cluster_name_; + }; + RouteConstSharedPtr default_route_; + + static BumpingStats generateStats(Stats::Scope& scope); + + std::vector access_logs_; + absl::optional idle_timeout_; + const Stats::ScopeSharedPtr stats_scope_; + const BumpingStats stats_; + const uint32_t max_connect_attempts_; + absl::optional max_downstream_connection_duration_; +}; + +using ConfigSharedPtr = std::shared_ptr; + +class BumpingMetadata : public CertificateProvider::OnDemandUpdateMetadata { +public: + BumpingMetadata(Envoy::Ssl::ConnectionInfoConstSharedPtr info) : info_(info){}; + + Envoy::Ssl::ConnectionInfoConstSharedPtr connectionInfo() const override { return info_; } + +private: + Envoy::Ssl::ConnectionInfoConstSharedPtr info_; +}; + +/** + * An implementation of a Bumping filter. This filter will instantiate a new outgoing TCP + * connection using TCP connection pool for the configured cluster. The established connection + * is only used for getting upstream cert, no data exchange happens. + */ +class Filter : public Network::ReadFilter, + public Upstream::LoadBalancerContextBase, + protected Logger::Loggable, + public TcpProxy::GenericConnectionPoolCallbacks, + public CertificateProvider::OnDemandUpdateCallbacks { +public: + Filter(ConfigSharedPtr config, Upstream::ClusterManager& cluster_manager); + ~Filter() override; + + // Network::ReadFilter + Network::FilterStatus onData(Buffer::Instance& data, bool end_stream) override; + Network::FilterStatus onNewConnection() override; + void initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) override; + + // GenericConnectionPoolCallbacks + void onGenericPoolReady(StreamInfo::StreamInfo* info, + std::unique_ptr&& upstream, + Upstream::HostDescriptionConstSharedPtr& host, + const Network::ConnectionInfoProvider& address_provider, + Ssl::ConnectionInfoConstSharedPtr ssl_info) override; + void onGenericPoolFailure(ConnectionPool::PoolFailureReason reason, + absl::string_view failure_reason, + Upstream::HostDescriptionConstSharedPtr host) override; + + // Upstream::LoadBalancerContext + const Router::MetadataMatchCriteria* metadataMatchCriteria() override { return nullptr; } + absl::optional computeHashKey() override { return {}; } + const Network::Connection* downstreamConnection() const override { + return &read_callbacks_->connection(); + } + + // CertificateProvider::OnDemandUpdateCallbacks + void onCacheHit(const std::string host) const override; + void onCacheMiss(const std::string host) const override; + + void requestCertificate(Ssl::ConnectionInfoConstSharedPtr info); + + struct UpstreamCallbacks : public Tcp::ConnectionPool::UpstreamCallbacks { + UpstreamCallbacks(Filter* parent) : parent_(parent) {} + + // Tcp::ConnectionPool::UpstreamCallbacks + void onUpstreamData(Buffer::Instance&, bool) override{}; + void onEvent(Network::ConnectionEvent event) override; + void onAboveWriteBufferHighWatermark() override{}; + void onBelowWriteBufferLowWatermark() override{}; + + Filter* parent_{}; + }; + + StreamInfo::StreamInfo& getStreamInfo(); + +protected: + enum class UpstreamFailureReason { + ConnectFailed, + NoHealthyUpstream, + ResourceLimitExceeded, + NoRoute, + }; + + virtual RouteConstSharedPtr pickRoute() { return config_->getRoute(); } + + virtual void onInitFailure(UpstreamFailureReason) { + read_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush); + } + + // Create connection to the upstream cluster. This function can be repeatedly called on upstream + // connection failure. + Network::FilterStatus establishUpstreamConnection(); + + // The callback upon on demand cluster discovery response. + // void onClusterDiscoveryCompletion(Upstream::ClusterDiscoveryStatus cluster_status); + + bool maybeTunnel(Upstream::ThreadLocalCluster& cluster); + void onConnectTimeout(); + void onUpstreamEvent(Network::ConnectionEvent event); + void onUpstreamConnection(); + void onIdleTimeout(); + void resetIdleTimer(); + void disableIdleTimer(); + + const ConfigSharedPtr config_; + Upstream::ClusterManager& cluster_manager_; + Network::ReadFilterCallbacks* read_callbacks_{}; + + Event::TimerPtr idle_timer_; + Event::TimerPtr connection_duration_timer_; + + std::shared_ptr upstream_callbacks_; // shared_ptr required for passing as a + // read filter. + // The upstream handle (either TCP or HTTP). This is set in onGenericPoolReady and should persist + // until either the upstream or downstream connection is terminated. + std::unique_ptr upstream_; + // The connection pool used to set up |upstream_|. + // This will be non-null from when an upstream connection is attempted until + // it either succeeds or fails. + std::unique_ptr generic_conn_pool_; + RouteConstSharedPtr route_; + Network::TransportSocketOptionsConstSharedPtr transport_socket_options_; + Network::Socket::OptionsSharedPtr upstream_options_; + uint32_t connect_attempts_{}; + bool connecting_{}; + Envoy::CertificateProvider::OnDemandUpdateHandlePtr on_demand_handle_; +}; +} // namespace Bumping +} // namespace Envoy diff --git a/source/extensions/filters/network/bumping/config.cc b/source/extensions/filters/network/bumping/config.cc new file mode 100644 index 0000000000..33757d5b12 --- /dev/null +++ b/source/extensions/filters/network/bumping/config.cc @@ -0,0 +1,36 @@ +#include "source/extensions/filters/network/bumping/config.h" + +#include "envoy/extensions/filters/network/bumping/v3/bumping.pb.h" +#include "envoy/extensions/filters/network/bumping/v3/bumping.pb.validate.h" +#include "envoy/registry/registry.h" + +#include "source/extensions/filters/network/bumping/bumping.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace Bumping { + +Network::FilterFactoryCb ConfigFactory::createFilterFactoryFromProtoTyped( + const envoy::extensions::filters::network::bumping::v3::Bumping& proto_config, + Server::Configuration::FactoryContext& context) { + ASSERT(!proto_config.stat_prefix().empty()); + + Envoy::Bumping::ConfigSharedPtr filter_config( + std::make_shared(proto_config, context)); + return [filter_config, &context](Network::FilterManager& filter_manager) -> void { + filter_manager.addReadFilter( + std::make_shared(filter_config, context.clusterManager())); + }; +} + +/** + * Static registration for the bumping filter. @see RegisterFactory. + */ +REGISTER_FACTORY(ConfigFactory, + Server::Configuration::NamedNetworkFilterConfigFactory){"envoy.bumping"}; + +} // namespace Bumping +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/bumping/config.h b/source/extensions/filters/network/bumping/config.h new file mode 100644 index 0000000000..de44785970 --- /dev/null +++ b/source/extensions/filters/network/bumping/config.h @@ -0,0 +1,32 @@ +#pragma once + +#include "envoy/extensions/filters/network/bumping/v3/bumping.pb.h" +#include "envoy/extensions/filters/network/bumping/v3/bumping.pb.validate.h" + +#include "source/extensions/filters/network/common/factory_base.h" +#include "source/extensions/filters/network/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace NetworkFilters { +namespace Bumping { + +/** + * Config registration for the bumping filter. @see NamedNetworkFilterConfigFactory. + */ + +class ConfigFactory + : public Common::FactoryBase { +public: + ConfigFactory() : FactoryBase(NetworkFilterNames::get().Bumping, false) {} + +private: + Network::FilterFactoryCb createFilterFactoryFromProtoTyped( + const envoy::extensions::filters::network::bumping::v3::Bumping& proto_config, + Server::Configuration::FactoryContext& context) override; +}; + +} // namespace Bumping +} // namespace NetworkFilters +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/filters/network/well_known_names.h b/source/extensions/filters/network/well_known_names.h index 0017db6f1f..6c2924414a 100644 --- a/source/extensions/filters/network/well_known_names.h +++ b/source/extensions/filters/network/well_known_names.h @@ -59,6 +59,8 @@ class NetworkFilterNameValues { const std::string ZooKeeperProxy = "envoy.filters.network.zookeeper_proxy"; // WebAssembly filter const std::string Wasm = "envoy.filters.network.wasm"; + // Bumping filter + const std::string Bumping = "envoy.filters.network.bumping"; }; using NetworkFilterNames = ConstSingleton; diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index 747d3badf5..9619bb1158 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -187,6 +187,19 @@ ContextConfigImpl::ContextConfigImpl( factory_context_(factory_context), tls_keylog_path_(config.key_log().path()), tls_keylog_local_(config.key_log().local_address_range()), tls_keylog_remote_(config.key_log().remote_address_range()) { + + if (tls_certificate_providers_.empty()) { + if (config.has_tls_certificate_provider_instance()) { + tls_certificates_hybrid_provider_ = + factory_context.certificateProviderManager().getCertificateProvider( + config.tls_certificate_provider_instance().instance_name()); + tls_certificate_name_ = config.tls_certificate_provider_instance().certificate_name(); + if (tls_certificates_hybrid_provider_) { + cert_provider_caps_ = tls_certificates_hybrid_provider_->capabilities(); + } + } + } + if (certificate_validation_context_provider_ != nullptr) { if (default_cvc_) { // We need to validate combined certificate validation context. @@ -209,7 +222,7 @@ ContextConfigImpl::ContextConfigImpl( getCombinedValidationContextConfig(*certificate_validation_context_provider_->secret()); } else { validation_context_config_ = std::make_unique( - *certificate_validation_context_provider_->secret(), api_); + *certificate_validation_context_provider_->secret(), api_, factory_context); } } } @@ -220,6 +233,11 @@ ContextConfigImpl::ContextConfigImpl( tls_certificate_configs_.emplace_back(*provider->secret(), factory_context, api_); } } + } else if (tls_certificates_hybrid_provider_ != nullptr) { + for (auto tls_certificate : + tls_certificates_hybrid_provider_->tlsCertificates(tls_certificate_name_)) { + tls_certificate_configs_.emplace_back(tls_certificate.get(), factory_context, api_); + } } HandshakerFactoryContextImpl handshaker_factory_context(api_, options_, alpn_protocols_, @@ -250,10 +268,14 @@ Ssl::CertificateValidationContextConfigPtr ContextConfigImpl::getCombinedValidat envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext combined_cvc = *default_cvc_; combined_cvc.MergeFrom(dynamic_cvc); - return std::make_unique(combined_cvc, api_); + return std::make_unique(combined_cvc, api_, + factory_context_); } void ContextConfigImpl::setSecretUpdateCallback(std::function callback) { + if (validation_context_config_) { + validation_context_config_->setCAUpdateCallback(callback); + } // When any of tls_certificate_providers_ receives a new secret, this callback updates // ContextConfigImpl::tls_certificate_configs_ with new secret. for (const auto& tls_certificate_provider : tls_certificate_providers_) { @@ -279,6 +301,7 @@ void ContextConfigImpl::setSecretUpdateCallback(std::function callback) certificate_validation_context_provider_->addUpdateCallback([this, callback]() { validation_context_config_ = getCombinedValidationContextConfig( *certificate_validation_context_provider_->secret()); + validation_context_config_->setCAUpdateCallback(callback); callback(); }); } else { @@ -288,11 +311,23 @@ void ContextConfigImpl::setSecretUpdateCallback(std::function callback) certificate_validation_context_provider_->addUpdateCallback([this, callback]() { validation_context_config_ = std::make_unique( - *certificate_validation_context_provider_->secret(), api_); + *certificate_validation_context_provider_->secret(), api_, factory_context_); + validation_context_config_->setCAUpdateCallback(callback); callback(); }); } } + if (tls_certificates_hybrid_provider_) { + tc_update_callback_handles_.push_back(tls_certificates_hybrid_provider_->addUpdateCallback( + tls_certificate_name_, [this, callback]() { + tls_certificate_configs_.clear(); + for (auto& tls_certificate : + tls_certificates_hybrid_provider_->tlsCertificates(tls_certificate_name_)) { + tls_certificate_configs_.emplace_back(tls_certificate.get(), factory_context_, api_); + } + callback(); + })); + } } Ssl::HandshakerFactoryCb ContextConfigImpl::createHandshaker() const { @@ -405,7 +440,8 @@ ServerContextConfigImpl::ServerContextConfigImpl( if (!capabilities().provides_certificates) { if ((config.common_tls_context().tls_certificates().size() + - config.common_tls_context().tls_certificate_sds_secret_configs().size()) == 0) { + config.common_tls_context().tls_certificate_sds_secret_configs().size()) + + config.common_tls_context().has_tls_certificate_provider_instance() == 0) { throw EnvoyException("No TLS certificates found for server context"); } else if (!config.common_tls_context().tls_certificates().empty() && !config.common_tls_context().tls_certificate_sds_secret_configs().empty()) { diff --git a/source/extensions/transport_sockets/tls/context_config_impl.h b/source/extensions/transport_sockets/tls/context_config_impl.h index 9815cae051..6078e84734 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.h +++ b/source/extensions/transport_sockets/tls/context_config_impl.h @@ -61,6 +61,9 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { void setSecretUpdateCallback(std::function callback) override; Ssl::HandshakerFactoryCb createHandshaker() const override; Ssl::HandshakerCapabilities capabilities() const override { return capabilities_; } + CertificateProvider::CertificateProvider::Capabilities certProviderCaps() const override { + return cert_provider_caps_; + } Ssl::SslCtxCb sslctxCb() const override { return sslctx_cb_; } Ssl::CertificateValidationContextConfigPtr getCombinedValidationContextConfig( @@ -94,6 +97,8 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { std::unique_ptr default_cvc_; std::vector tls_certificate_providers_; + CertificateProvider::CertificateProviderSharedPtr tls_certificates_hybrid_provider_; + std::string tls_certificate_name_; // Handle for TLS certificate dynamic secret callback. std::vector tc_update_callback_handles_; Secret::CertificateValidationContextConfigProviderSharedPtr @@ -106,6 +111,7 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { Ssl::HandshakerFactoryCb handshaker_factory_cb_; Ssl::HandshakerCapabilities capabilities_; + CertificateProvider::CertificateProvider::Capabilities cert_provider_caps_; Ssl::SslCtxCb sslctx_cb_; Server::Configuration::TransportSocketFactoryContext& factory_context_; const std::string tls_keylog_path_; diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 506a153788..af51b590bd 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -3,9 +3,11 @@ #include #include +#include #include #include #include +#include #include #include "envoy/admin/v3/certs.pb.h" @@ -90,7 +92,8 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c ssl_versions_(stat_name_set_->add("ssl.versions")), ssl_curves_(stat_name_set_->add("ssl.curves")), ssl_sigalgs_(stat_name_set_->add("ssl.sigalgs")), capabilities_(config.capabilities()), - tls_keylog_local_(config.tlsKeyLogLocal()), tls_keylog_remote_(config.tlsKeyLogRemote()) { + cert_provider_caps_(config.certProviderCaps()), tls_keylog_local_(config.tlsKeyLogLocal()), + tls_keylog_remote_(config.tlsKeyLogRemote()) { auto cert_validator_name = getCertValidatorName(config.certificateValidationContext()); auto cert_validator_factory = @@ -189,7 +192,6 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c } #endif - absl::node_hash_set cert_pkey_ids; if (!capabilities_.provides_certificates) { for (uint32_t i = 0; i < tls_certificates.size(); ++i) { auto& ctx = tls_contexts_[i]; @@ -214,11 +216,6 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c bssl::UniquePtr public_key(X509_get_pubkey(ctx.cert_chain_.get())); 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 chain from {}, at most one " - "certificate of a given type may be specified", - ctx.cert_chain_file_path_)); - } ctx.is_ecdsa_ = pkey_id == EVP_PKEY_EC; switch (pkey_id) { case EVP_PKEY_EC: { @@ -783,10 +780,19 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, TimeSource& time_source) : ContextImpl(scope, config, time_source), session_ticket_keys_(config.sessionTicketKeys()), ocsp_staple_policy_(config.ocspStaplePolicy()) { - if (config.tlsCertificates().empty() && !config.capabilities().provides_certificates) { + if (config.tlsCertificates().empty() && !config.capabilities().provides_certificates && + !config.certProviderCaps().provide_on_demand_identity_certs) { throw EnvoyException("Server TlsCertificates must have a certificate specified"); } + for (auto& ctx : tls_contexts_) { + bssl::UniquePtr public_key(X509_get_pubkey(ctx.cert_chain_.get())); + const int pkey_id = EVP_PKEY_id(public_key.get()); + // Load DNS SAN entries and Subject Common Name as server name patterns after certificate + // chain loaded, and populate ServerNamesMap which will be used to match SNI. + populateServerNamesMap(ctx, pkey_id); + } + // Compute the session context ID hash. We use all the certificate identities, // since we should have a common ID for session resumption no matter what cert // is used. We do this early because it can throw an EnvoyException. @@ -869,6 +875,61 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, } } +void ServerContextImpl::populateServerNamesMap(TlsContext& ctx, int pkey_id) { + if (ctx.cert_chain_ == nullptr) { + return; + } + + auto populate = [&](const std::string& sn) { + std::string sn_pattern = sn; + if (absl::StartsWith(sn, "*.")) { + sn_pattern = sn.substr(1); + } + PkeyTypesMap pkey_types_map; + auto sn_match = server_names_map_.try_emplace(sn_pattern, pkey_types_map).first; + auto pt_match = sn_match->second.find(pkey_id); + if (pt_match != sn_match->second.end()) { + throw EnvoyException(fmt::format( + "Failed to load certificate chain from {}, at most one " + "certificate of a given type may be specified for each DNS SAN entry or Subject CN: {}", + ctx.cert_chain_file_path_, sn_match->first)); + } + sn_match->second.emplace(std::pair>(pkey_id, ctx)); + }; + + bssl::UniquePtr san_names(static_cast( + X509_get_ext_d2i(ctx.cert_chain_.get(), NID_subject_alt_name, nullptr, nullptr))); + auto dns_sans = Utility::getSubjectAltNames(*ctx.cert_chain_, GEN_DNS); + // https://www.rfc-editor.org/rfc/rfc6066#section-3 + // Currently, the only server names supported are DNS hostnames, so we + // only save dns san entries to match SNI. + for (const auto& san : dns_sans) { + populate(san); + } + + // https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 + // As noted, a client MUST NOT seek a match for a reference identifier + // of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, + // URI-ID, or any application-specific identifier types supported by the + // client. + if (san_names.get() == nullptr) { + X509_NAME* cert_subject = X509_get_subject_name(ctx.cert_chain_.get()); + const int cn_index = X509_NAME_get_index_by_NID(cert_subject, NID_commonName, -1); + if (cn_index >= 0) { + X509_NAME_ENTRY* cn_entry = X509_NAME_get_entry(cert_subject, cn_index); + if (cn_entry) { + ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry); + if (ASN1_STRING_length(cn_asn1) > 0) { + std::string subject_cn; + subject_cn.assign(reinterpret_cast(ASN1_STRING_data(cn_asn1)), + ASN1_STRING_length(cn_asn1)); + populate(subject_cn); + } + } + } + } +} + ServerContextImpl::SessionContextID ServerContextImpl::generateHashForSessionContextId(const std::vector& server_names) { uint8_t hash_buffer[EVP_MAX_MD_SIZE]; @@ -1160,23 +1221,63 @@ enum ssl_select_cert_result_t ServerContextImpl::selectTlsContext(const SSL_CLIENT_HELLO* ssl_client_hello) { const bool client_ecdsa_capable = isClientEcdsaCapable(ssl_client_hello); const bool client_ocsp_capable = isClientOcspCapable(ssl_client_hello); + absl::string_view sni = absl::NullSafeStringView( + SSL_get_servername(ssl_client_hello->ssl, TLSEXT_NAMETYPE_host_name)); - // Fallback on first certificate. - const TlsContext* selected_ctx = &tls_contexts_[0]; - auto ocsp_staple_action = ocspStapleAction(*selected_ctx, client_ocsp_capable); - for (const auto& ctx : tls_contexts_) { + const TlsContext* selected_ctx = nullptr; + OcspStapleAction ocsp_staple_action; + + auto selected = [&](const TlsContext& ctx) -> bool { if (client_ecdsa_capable != ctx.is_ecdsa_) { - continue; + return false; } auto action = ocspStapleAction(ctx, client_ocsp_capable); if (action == OcspStapleAction::Fail) { - continue; + return false; } selected_ctx = &ctx; ocsp_staple_action = action; - break; + return true; + }; + + // Do SNI matching and pkey type matching if SNI exists. + if (!sni.empty()) { + // Match on exact server name, i.e. "www.example.com" for "www.example.com". + auto server_name_it = server_names_map_.find(sni); + if (server_name_it == server_names_map_.end()) { + // Match on wildcard domain, i.e. ".example.com" for "www.example.com". + // https://datatracker.ietf.org/doc/html/rfc6125#section-6.4 + size_t pos = sni.find('.', 1); + if (pos < sni.size() - 1 && pos != std::string::npos) { + absl::string_view wildcard = sni.substr(pos); + server_name_it = server_names_map_.find(static_cast(wildcard)); + } + } + + if (server_name_it != server_names_map_.end()) { + const auto& pkey_types_map = server_name_it->second; + // Fallback on first SNI-matched certificate. + selected_ctx = &pkey_types_map.begin()->second.get(); + ocsp_staple_action = ocspStapleAction(*selected_ctx, client_ocsp_capable); + for (const auto& entry : pkey_types_map) { + if (selected(entry.second.get())) { + break; + } + } + } + } + // Do pkey type matching if SNI does Not exist or no ctx matched for SNI. + if (selected_ctx == nullptr) { + // Fallback on first certificate. + selected_ctx = &tls_contexts_[0]; + ocsp_staple_action = ocspStapleAction(*selected_ctx, client_ocsp_capable); + for (const auto& ctx : tls_contexts_) { + if (selected(ctx)) { + break; + } + } } // Apply the selected context. This must be done before OCSP stapling below diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index f279229111..ef624e1f9b 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -131,11 +132,14 @@ class ContextImpl : public virtual Envoy::Ssl::Context, Envoy::Ssl::SslExtendedSocketInfo* extended_socket_info, const Network::TransportSocketOptionsConstSharedPtr& transport_socket_options, SSL* ssl); + void populateServerNamesMap(TlsContext& ctx, const int pkey_id); + // This is always non-empty, with the first context used for all new SSL // objects. For server contexts, once we have ClientHello, we // potentially switch to a different CertificateContext based on certificate // selection. std::vector tls_contexts_; + CertValidatorPtr cert_validator_; Stats::Scope& scope_; SslStats stats_; @@ -154,6 +158,7 @@ class ContextImpl : public virtual Envoy::Ssl::Context, const Stats::StatName ssl_curves_; const Stats::StatName ssl_sigalgs_; const Ssl::HandshakerCapabilities capabilities_; + const CertificateProvider::CertificateProvider::Capabilities cert_provider_caps_; const Network::Address::IpList tls_keylog_local_; const Network::Address::IpList tls_keylog_remote_; AccessLog::AccessLogFileSharedPtr tls_keylog_file_; @@ -194,6 +199,16 @@ class ServerContextImpl : public ContextImpl, public Envoy::Ssl::ServerContext { enum ssl_select_cert_result_t selectTlsContext(const SSL_CLIENT_HELLO* ssl_client_hello); private: + // Currently, at most one certificate of a given key type may be specified for each exact + // server name or wildcard domain name. + using PkeyTypesMap = absl::flat_hash_map>; + // Both exact server names and wildcard domains are part of the same map, in which wildcard + // domains are prefixed with "." (i.e. ".example.com" for "*.example.com") to differentiate + // between exact and wildcard entries. + using ServerNamesMap = absl::flat_hash_map; + + void populateServerNamesMap(TlsContext& ctx, const int pkey_id); + using SessionContextID = std::array; int alpnSelectCallback(const unsigned char** out, unsigned char* outlen, const unsigned char* in, @@ -208,6 +223,7 @@ class ServerContextImpl : public ContextImpl, public Envoy::Ssl::ServerContext { const std::vector session_ticket_keys_; const Ssl::ServerContextConfig::OcspStaplePolicy ocsp_staple_policy_; + ServerNamesMap server_names_map_; }; } // namespace Tls diff --git a/source/server/BUILD b/source/server/BUILD index a1d7ac686f..1f5bf53de3 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -599,6 +599,7 @@ envoy_cc_library( "//envoy/upstream:cluster_manager_interface", "//source/common/access_log:access_log_manager_lib", "//source/common/api:api_lib", + "//source/common/certificate_provider:certificate_provider_manager_impl_lib", "//source/common/common:cleanup_lib", "//source/common/common:logger_lib", "//source/common/common:mutex_tracer_lib", diff --git a/source/server/active_stream_listener_base.cc b/source/server/active_stream_listener_base.cc index c6aa7d1261..dea305895f 100644 --- a/source/server/active_stream_listener_base.cc +++ b/source/server/active_stream_listener_base.cc @@ -42,7 +42,7 @@ void ActiveStreamListenerBase::newConnection(Network::ConnectionSocketPtr&& sock stream_info->setFilterChainName(filter_chain->name()); auto transport_socket = filter_chain->transportSocketFactory().createDownstreamTransportSocket(); auto server_conn_ptr = dispatcher().createServerConnection( - std::move(socket), std::move(transport_socket), *stream_info); + std::move(socket), std::move(transport_socket), *stream_info, filter_chain->transportSocketFactory()); if (const auto timeout = filter_chain->transportSocketConnectTimeout(); timeout != std::chrono::milliseconds::zero()) { server_conn_ptr->setTransportSocketConnectTimeout( diff --git a/source/server/config_validation/cluster_manager.h b/source/server/config_validation/cluster_manager.h index c66d273542..9fb7a1ddce 100644 --- a/source/server/config_validation/cluster_manager.h +++ b/source/server/config_validation/cluster_manager.h @@ -29,12 +29,14 @@ class ValidationClusterManagerFactory : public ProdClusterManagerFactory { Http::Context& http_context, Grpc::Context& grpc_context, Router::Context& router_context, AccessLog::AccessLogManager& log_manager, Singleton::Manager& singleton_manager, const Server::Options& options, Quic::QuicStatNames& quic_stat_names, + CertificateProvider::CertificateProviderManager& certificate_provider_manager, Server::Instance& server) : ProdClusterManagerFactory( const_cast(server.serverFactoryContext()), admin, runtime, stats, tls, dns_resolver, ssl_context_manager, main_thread_dispatcher, local_info, secret_manager, validation_context, api, http_context, grpc_context, - router_context, log_manager, singleton_manager, options, quic_stat_names, server), + router_context, log_manager, singleton_manager, options, quic_stat_names, + certificate_provider_manager, server), grpc_context_(grpc_context), router_context_(router_context) {} ClusterManagerPtr diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 94f0413999..e8f67a741a 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -109,12 +109,14 @@ void ValidationInstance::initialize(const Options& options, } secret_manager_ = std::make_unique(admin().getConfigTracker()); + certificate_provider_manager_ = + std::make_unique(*api_); ssl_context_manager_ = createContextManager("ssl_context_manager", api_->timeSource()); cluster_manager_factory_ = std::make_unique( admin(), runtime(), stats(), threadLocal(), dnsResolver(), sslContextManager(), dispatcher(), localInfo(), *secret_manager_, messageValidationContext(), *api_, http_context_, grpc_context_, router_context_, accessLogManager(), singletonManager(), options, - quic_stat_names_, *this); + quic_stat_names_, *certificate_provider_manager_, *this); config_.initialize(bootstrap_, *this, *cluster_manager_factory_); runtime().initialize(clusterManager()); clusterManager().setInitializedCb([this]() -> void { init_manager_.initialize(init_watcher_); }); diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index 73cff10adf..b2a430811f 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -100,6 +100,9 @@ class ValidationInstance final : Logger::Loggable, } return *runtime_; } + CertificateProvider::CertificateProviderManager& certificateProviderManager() override { + return *certificate_provider_manager_; + } void shutdown() override; bool isShutdown() override { return false; } void shutdownAdmin() override {} @@ -209,6 +212,7 @@ class ValidationInstance final : Logger::Loggable, // - There may be active clusters referencing it in config_.cluster_manager_. // - There may be active connections referencing it. std::unique_ptr secret_manager_; + std::unique_ptr certificate_provider_manager_; const Options& options_; ProtobufMessage::ProdValidationContextImpl validation_context_; Stats::IsolatedStoreImpl& stats_store_; diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index a96840835a..297df3d23b 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -94,6 +94,13 @@ void MainImpl::initialize(const envoy::config::bootstrap::v3::Bootstrap& bootstr server.secretManager().addStaticSecret(secrets[i]); } + const auto& certificate_providers = bootstrap.certificate_provider_instances(); + for (auto iter = certificate_providers.begin(); iter != certificate_providers.end(); iter++) { + ENVOY_LOG(debug, "add certificate provider: {}", iter->first); + server.certificateProviderManager().addCertificateProvider( + iter->first, iter->second, server.transportSocketFactoryContext()); + } + ENVOY_LOG(info, "loading {} cluster(s)", bootstrap.static_resources().clusters().size()); cluster_manager_ = cluster_manager_factory.clusterManagerFromProto(bootstrap); diff --git a/source/server/server.cc b/source/server/server.cc index 9cd42ec3a5..7702556302 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -561,6 +561,8 @@ void InstanceImpl::initialize(Network::Address::InstanceConstSharedPtr local_add loadServerFlags(initial_config.flagsPath()); secret_manager_ = std::make_unique(admin_->getConfigTracker()); + certificate_provider_manager_ = + std::make_unique(*api_); // Initialize the overload manager early so other modules can register for actions. overload_manager_ = std::make_unique( @@ -686,7 +688,8 @@ void InstanceImpl::initialize(Network::Address::InstanceConstSharedPtr local_add serverFactoryContext(), *admin_, runtime(), stats_store_, thread_local_, dns_resolver_, *ssl_context_manager_, *dispatcher_, *local_info_, *secret_manager_, messageValidationContext(), *api_, http_context_, grpc_context_, router_context_, - access_log_manager_, *singleton_manager_, options_, quic_stat_names_, *this); + access_log_manager_, *singleton_manager_, options_, quic_stat_names_, + *certificate_provider_manager_, *this); // Now the configuration gets parsed. The configuration may start setting // thread local data per above. See MainImpl::initialize() for why ConfigImpl diff --git a/source/server/server.h b/source/server/server.h index 1be756114d..3da4e2356c 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -24,6 +24,7 @@ #include "envoy/tracing/http_tracer.h" #include "source/common/access_log/access_log_manager_impl.h" +#include "source/common/certificate_provider/certificate_provider_manager_impl.h" #include "source/common/common/assert.h" #include "source/common/common/cleanup.h" #include "source/common/common/logger_delegates.h" @@ -200,6 +201,9 @@ class ServerFactoryContextImpl : public Configuration::ServerFactoryContext, // Configuration::TransportSocketFactoryContext Ssl::ContextManager& sslContextManager() override { return server_.sslContextManager(); } Secret::SecretManager& secretManager() override { return server_.secretManager(); } + CertificateProvider::CertificateProviderManager& certificateProviderManager() override { + return server_.certificateProviderManager(); + } Stats::Store& stats() override { return server_.stats(); } Init::Manager& initManager() override { return server_.initManager(); } AccessLog::AccessLogManager& accessLogManager() override { return server_.accessLogManager(); } @@ -259,6 +263,9 @@ class InstanceImpl final : Logger::Loggable, ServerLifecycleNotifier& lifecycleNotifier() override { return *this; } ListenerManager& listenerManager() override { return *listener_manager_; } Secret::SecretManager& secretManager() override { return *secret_manager_; } + CertificateProvider::CertificateProviderManager& certificateProviderManager() override { + return *certificate_provider_manager_; + } Envoy::MutexTracer* mutexTracer() override { return mutex_tracer_; } OverloadManager& overloadManager() override { return *overload_manager_; } Runtime::Loader& runtime() override; @@ -331,6 +338,7 @@ class InstanceImpl final : Logger::Loggable, // - There may be active clusters referencing it in config_.cluster_manager_. // - There may be active connections referencing it. std::unique_ptr secret_manager_; + std::unique_ptr certificate_provider_manager_; bool workers_started_; std::atomic live_; bool shutdown_; diff --git a/source/server/transport_socket_config_impl.h b/source/server/transport_socket_config_impl.h index 390b23f8eb..c32c3195ca 100644 --- a/source/server/transport_socket_config_impl.h +++ b/source/server/transport_socket_config_impl.h @@ -53,6 +53,9 @@ class TransportSocketFactoryContextImpl : public TransportSocketFactoryContext { AccessLog::AccessLogManager& accessLogManager() override { return server_context_.accessLogManager(); } + CertificateProvider::CertificateProviderManager& certificateProviderManager() override { + return cluster_manager_.clusterManagerFactory().certificateProviderManager(); + } private: Server::Configuration::ServerFactoryContext& server_context_; diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index ad13eac2f5..7d5c540a42 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -111,6 +111,11 @@ class SslContextImplTest : public SslCertsTest { } }); } + void loadConfig(ServerContextConfigImpl& cfg) { + Envoy::Ssl::ServerContextSharedPtr server_ctx( + manager_.createSslServerContext(store_, cfg, std::vector{})); + auto cleanup = cleanUpHelper(server_ctx); + } protected: Event::SimulatedTimeSystem time_system_; @@ -449,8 +454,8 @@ TEST_F(SslContextImplTest, TestNoCert) { EXPECT_TRUE(context->getCertChainInformation().empty()); } -// Multiple RSA certificates are rejected. -TEST_F(SslContextImplTest, AtMostOneRsaCert) { +// Multiple RSA certificates with the same exact DNS SAN are rejected. +TEST_F(SslContextImplTest, AtMostOneRsaCertSameExactDNSSan) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( common_tls_context: @@ -468,10 +473,54 @@ TEST_F(SslContextImplTest, AtMostOneRsaCert) { ServerContextConfigImpl server_context_config(tls_context, factory_context_); EXPECT_THROW_WITH_REGEX(manager_.createSslServerContext(store_, server_context_config, {}), EnvoyException, - "at most one certificate of a given type may be specified"); + "at most one certificate of a given type may be specified for each DNS " + "SAN entry or Subject CN"); +} + +// Multiple RSA certificates with the same wildcard DNS SAN are rejected. +TEST_F(SslContextImplTest, AtMostOneRsaCertSameWildcardDNSSan) { + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + ServerContextConfigImpl server_context_config(tls_context, factory_context_); + EXPECT_THROW_WITH_REGEX(manager_.createSslServerContext(store_, server_context_config, {}), + EnvoyException, + "at most one certificate of a given type may be specified for each DNS " + "SAN entry or Subject CN"); } -// Multiple ECDSA certificates are rejected. +// Multiple RSA certificates with different exact DNS SAN are acceptable +TEST_F(SslContextImplTest, AcceptableMultipleRsaCerts) { + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_key.pem" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + ServerContextConfigImpl server_context_config(tls_context, factory_context_); + EXPECT_NO_THROW(loadConfig(server_context_config)); +} + +// Multiple ECDSA certificates with the same exact DNS SAN are rejected. TEST_F(SslContextImplTest, AtMostOneEcdsaCert) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( @@ -490,7 +539,51 @@ TEST_F(SslContextImplTest, AtMostOneEcdsaCert) { ServerContextConfigImpl server_context_config(tls_context, factory_context_); EXPECT_THROW_WITH_REGEX(manager_.createSslServerContext(store_, server_context_config, {}), EnvoyException, - "at most one certificate of a given type may be specified"); + "at most one certificate of a given type may be specified for each DNS " + "SAN entry or Subject CN"); +} + +// Multiple ECDSA certificates with different exact DNS SAN are acceptable +TEST_F(SslContextImplTest, AcceptableMultipleEcdsaCerts) { + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_key.pem" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + ServerContextConfigImpl server_context_config(tls_context, factory_context_); + EXPECT_NO_THROW(loadConfig(server_context_config)); +} + +// CN is not used if SANs are present +TEST_F(SslContextImplTest, CertSansAndCN) { + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + // no_san_cn_2_cert's CN: server2.example.com + // san_multiple_dns_1_cert's CN: server2.example.com + // san_multiple_dns_1_cert's SAN: DNS.1 = *.example.com DNS.2 = server1.example.com + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + ServerContextConfigImpl server_context_config(tls_context, factory_context_); + EXPECT_NO_THROW(loadConfig(server_context_config)); } // Certificates with no subject CN and no SANs are rejected. diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 6134d61b75..c3e28a9737 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -31,6 +31,10 @@ #include "test/extensions/transport_sockets/tls/test_data/san_dns3_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/san_dns4_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/san_dns_cert_info.h" +#include "test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert_info.h" +#include "test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_info.h" +#include "test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert_info.h" +#include "test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/san_uri_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert_info.h" #include "test/extensions/transport_sockets/tls/test_private_key_method_provider.h" @@ -1267,7 +1271,7 @@ TEST_P(SslSocketTest, NoCert) { // Prefer ECDSA certificate when multiple RSA certificates are present and the // client is RSA/ECDSA capable. We validate TLSv1.2 only here, since we validate // the e2e behavior on TLSv1.2/1.3 in ssl_integration_test. -TEST_P(SslSocketTest, MultiCertPreferEcdsa) { +TEST_P(SslSocketTest, MultiCertPreferEcdsaWithoutSni) { const std::string client_ctx_yaml = absl::StrCat(R"EOF( common_tls_context: tls_params: @@ -1283,6 +1287,10 @@ TEST_P(SslSocketTest, MultiCertPreferEcdsa) { 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/selfsigned_cert.pem" private_key: @@ -1297,6 +1305,185 @@ TEST_P(SslSocketTest, MultiCertPreferEcdsa) { testUtil(test_options); } +// When client supports SNI and is ecdsa capable, the ECDSA certificate +// that match SNI will be selected. +TEST_P(SslSocketTest, MultiCertPreferECDSAWithSni) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "server1.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SAN_DNS_ECDSA_1_CERT_256_HASH); + + 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + testUtil(test_options.setExpectedSni("server1.example.com")); +} + +// Multiple certs with multiple SANs, wildcard does not match if an exact match is present +TEST_P(SslSocketTest, MultiCertMultipleSANWithSni) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "server1.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SAN_DNS_RSA_1_CERT_256_HASH); + 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + testUtil(test_options.setExpectedSni("server1.example.com")); +} + +// Wildcard in SANs is matched properly, *.example.com matches server1.example.com +TEST_P(SslSocketTest, CertWildcardSANWithSni) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "server1.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SAN_MULTIPLE_DNS_CERT_256_HASH); + 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + testUtil(test_options.setExpectedSni("server1.example.com")); +} + +// Wildcard SAN *.example.com does not matches a.server1.example.com, validate that the +// default/first cert is used. +TEST_P(SslSocketTest, CertWildcardSanNotMatchWithSni) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "a.server1.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_NO_SAN_CERT_256_HASH); + 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + // The validation succeeds with default cert since Envoy does not define the criteria that + // how to validate cert SAN based on SNI . + testUtil(test_options.setExpectedSni("a.server1.example.com")); +} + +// EC cert is selected for a no-EC-capable client +TEST_P(SslSocketTest, CertWithNotECCapable) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "server1.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SAN_DNS_ECDSA_1_CERT_256_HASH); + 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem" +)EOF"; + + // handshake is expected to fail + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, false, version_); + // TODO(luyao): We might need to modify ssl socket to set proper stats for failed handshake + testUtil(test_options.setExpectedServerStats("") + .setExpectedSni("server1.example.com") + .setExpectedTransportFailureReasonContains("HANDSHAKE_FAILURE_ON_CLIENT_HELLO")); +} + TEST_P(SslSocketTest, GetUriWithLocalUriSan) { const std::string client_ctx_yaml = R"EOF( common_tls_context: diff --git a/test/extensions/transport_sockets/tls/test_data/certs.sh b/test/extensions/transport_sockets/tls/test_data/certs.sh index a19009d3ac..5027e34371 100755 --- a/test/extensions/transport_sockets/tls/test_data/certs.sh +++ b/test/extensions/transport_sockets/tls/test_data/certs.sh @@ -186,10 +186,38 @@ rm -f san_dns4_cert.cfg generate_rsa_key san_multiple_dns generate_x509_cert san_multiple_dns ca +# Generate san_multiple_dns_1_cert.pem +generate_rsa_key san_multiple_dns_1 +generate_x509_cert san_multiple_dns_1 ca + # Generate san_only_dns_cert.pem. generate_rsa_key san_only_dns generate_x509_cert san_only_dns ca +# Generate san_dns_rsa_1_cert.pem +cp san_dns_server1_cert.cfg san_dns_rsa_1_cert.cfg +generate_rsa_key san_dns_rsa_1 +generate_x509_cert san_dns_rsa_1 ca +rm -f san_dns_rsa_1_cert.cfg + +# Generate san_dns_rsa_2_cert.pem +cp san_dns_server2_cert.cfg san_dns_rsa_2_cert.cfg +generate_rsa_key san_dns_rsa_2 +generate_x509_cert san_dns_rsa_2 ca +rm -f san_dns_rsa_2_cert.cfg + +# Generate san_dns_ecdsa_1_cert.pem +cp san_dns_server1_cert.cfg san_dns_ecdsa_1_cert.cfg +generate_ecdsa_key san_dns_ecdsa_1 +generate_x509_cert san_dns_ecdsa_1 ca +rm -f san_dns_ecdsa_1_cert.cfg + +# Generate san_dns_ecdsa_2_cert.pem +cp san_dns_server2_cert.cfg san_dns_ecdsa_2_cert.cfg +generate_ecdsa_key san_dns_ecdsa_2 +generate_x509_cert san_dns_ecdsa_2 ca +rm -f san_dns_ecdsa_2_cert.cfg + # Generate san_uri_cert.pem. generate_rsa_key san_uri generate_x509_cert san_uri ca diff --git a/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert.pem b/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert.pem new file mode 100644 index 0000000000..d45471bb68 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUR8m6bvw3FZDfqgVrUZYYHa8XguUwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIwODA1MTAzMDUyWhcNMjQw +ODA0MTAzMDUyWjCBgjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx +FjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsM +EEx5ZnQgRW5naW5lZXJpbmcxHDAaBgNVBAMME3NlcnZlcjIuZXhhbXBsZS5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5ADc5tGh2w6DQaZpP++86 +guK/xnrpJEWax50pp6u38GBXaW4PegavSskEOA4JbL53ckXQrt9c0Fb1kCCnZHMx +Z5BTNokkfrkNUQIjOmDpwTSwp8HV6HYWaDC45f8/FGDI4IIdcopIqmveeNkQUAHy +F3J9mzLt60pP1c3sedOTSnPXCNaUh0DseT/dfVwFAe/nRPTBI4PPA9btWi3QY2hF +Ym7dXlQrzeMuMwUxMsz9XIXXZ1S6ZZX1VrFvE/xjvrlgxZKD+FeV4Wv3ygsyE/wV +gm7KnAWlooOLABs0Q+ekqBK/BB1+QKIglN9LW22ecZHzmJBQP7PfWn/CXKAcDa41 +AgMBAAGjfDB6MAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQG +CCsGAQUFBwMCBggrBgEFBQcDATAdBgNVHQ4EFgQUPE3JTaZH5CMZoZusBJ4X8yj8 +ZFIwHwYDVR0jBBgwFoAU8jl9JnKJHvzQxK9OFbMC1zJJzZMwDQYJKoZIhvcNAQEL +BQADggEBAGYIZpWhlCx3ko427hEi5lgGTfEPU10GHM+n8N3JwYetOpoGK9AEfNUv +0u7F22QO0KUkQzK64YDlfW1kGHX40ttAnt5G+9T4vbyhy7T7ghrQV55WlLHJ39WC +vRQq7BxCv563Pb3ZeA2wZohirNvnQaMARibCINA+QT1lu4QfrcYSF7PzcQGRm52s +xy/e3wg8RM1Kdjlg5gLnPDAc42GJIgg8XLH6K5r/TpCiG2c0GVc5PPfiKjVQKvcE +WMIJBof6iVb024uhn7GpcI7vgLlZhMX+6taA8v8AG/OOSy54dUWp8FeUTBrDVwJ6 +17E8ZUNq9IOOCGnv8OMbIK4FuomizNE= +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert_info.h b/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert_info.h new file mode 100644 index 0000000000..d98e4f01cb --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_NO_SAN_CN_2_CERT_256_HASH[] = + "9cacc6dd383d607a4d81eb728fff53bf955a8897490efbf3bbac217bdfa62731"; +constexpr char TEST_NO_SAN_CN_2_CERT_1_HASH[] = "896eeaf845a2d2858ef10d19eedc32d1f9dbbeb9"; +constexpr char TEST_NO_SAN_CN_2_CERT_SPKI[] = "SHvHIHvmXj3GGRzQN9+fhszys2vrkNpVifmU4W79kJE="; +constexpr char TEST_NO_SAN_CN_2_CERT_SERIAL[] = "47c9ba6efc371590dfaa056b5196181daf1782e5"; +constexpr char TEST_NO_SAN_CN_2_CERT_NOT_BEFORE[] = "Aug 5 10:30:52 2022 GMT"; +constexpr char TEST_NO_SAN_CN_2_CERT_NOT_AFTER[] = "Aug 4 10:30:52 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_key.pem b/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_key.pem new file mode 100644 index 0000000000..095572b329 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAuQA3ObRodsOg0GmaT/vvOoLiv8Z66SRFmsedKaert/BgV2lu +D3oGr0rJBDgOCWy+d3JF0K7fXNBW9ZAgp2RzMWeQUzaJJH65DVECIzpg6cE0sKfB +1eh2FmgwuOX/PxRgyOCCHXKKSKpr3njZEFAB8hdyfZsy7etKT9XN7HnTk0pz1wjW +lIdA7Hk/3X1cBQHv50T0wSODzwPW7Vot0GNoRWJu3V5UK83jLjMFMTLM/VyF12dU +umWV9VaxbxP8Y765YMWSg/hXleFr98oLMhP8FYJuypwFpaKDiwAbNEPnpKgSvwQd +fkCiIJTfS1ttnnGR85iQUD+z31p/wlygHA2uNQIDAQABAoIBAF8awEk2+HkNiq95 +im3R6yLeRGkoROlzzyCJu769RqbRuQVW2tCz/5m/zWUQ2A56S9Ql98QXEhapt/qX +dGH5XsqoHebZY1lSmBlDauDnUwj4XPjv1NL9pS9RsrJq8nPgKv05hUpUJsjCa5+7 +6oLbTpTA/weZcG4lBlfSZwFJg730qES6dC59FewDKIZuy3w/leAS508uyoyzn1XO +hdx9w/5DIQShbaU0uppzi8wXOqDMYr/qqW7sWJA0hMK56y0LvxACXAfSrZJcsDfX +Ey++aFVLwaUHnfE37ULc2BtMjWrn4DwR+sgaKh7HeaFLXTBf1DXObHucLFKFAP1g +W6Xon2UCgYEA8ydeaj7qCPs1PINUWWeJnyIqdJmAZtZ8ylhN5OYtxh72E8Y3+ILV +OOFbZyDTI91iVyHai7XYYCQy9vsWYboZCAfYqVZidYxWenuBkDsVTvEoLYWzDTqd +jByhrJTAomQTrwf0ztyVjTCnKX+oqb/FI0GmjbguwNCjH03GoFxxhn8CgYEAwsZV +hMGf1EueUwxL15uCUGxJZTuY+pWAw9LY79h0aIP3kqUIW3FFkCzd5FJ4+SjMirlt +/GjMxIjobCv7fhXKCwLauEXfVZgv015laFHOpVVBcFXxnh59FkpEi1wCtOKu4mHH +0mdOC2D3aQe2B15254N18K0zPemOrO5dK32nOUsCgYBp+UeSALeZkHrEU+yoRpLZ +c1eTl+85YtPikJfqYlOi3I2dUq/ENKlfcACZMtoy8GLS3ONWLrwOMtivggFgOG5+ +bIUGUKr61l4+ZaOzS7zeugYryPCfnGNb1IK2J+LJI9rcVxhgLA1WCtVgJc5JpVXz +JsLDU3m0+XmOxCQEciXcWwKBgQCCSGIZq946Bd7AVEBC1v1CyDwL3+KC8w+vbNC5 +FgM/ic5HI+bEK69JMgm2WMgceYLf5ArCxGL7r0wwVvchLnf4B4+/pNUvjt2mt8SD +E/1yr+VWu9YjWud2ynSw9vnEfsBWIuujPICCsfisI4FJypEkaNWyCFh3OEu8NrDR +j3oUowKBgDkI5/2ea0/FOMklP94Wq8dCzPCS0w8aLuC6z32JNv1M7TKIbSlSyrbQ +95nwdzotr8FyGx3HwvmcLvrcArr8KhLyi/W5PY+Vv8jBy+z+6f2DSdtgoAxxYdhV +AYVvNlNgXcWuFKnyrYHwJUUf+B7YyBPLXtOn4fgy2dajM04Vo65c +-----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/no_san_cn_server2_cert.cfg b/test/extensions/transport_sockets/tls/test_data/no_san_cn_server2_cert.cfg new file mode 100644 index 0000000000..d47490d3d8 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/no_san_cn_server2_cert.cfg @@ -0,0 +1,31 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +countryName = US +countryName_default = US +stateOrProvinceName = California +stateOrProvinceName_default = California +localityName = San Francisco +localityName_default = San Francisco +organizationName = Lyft +organizationName_default = Lyft +organizationalUnitName = Lyft Engineering +organizationalUnitName_default = Lyft Engineering +commonName = server2.example.com +commonName_default = server2.example.com +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectKeyIdentifier = hash + +[v3_ca] +basicConstraints = critical, CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem new file mode 100644 index 0000000000..9bf06783d4 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUjCCAjqgAwIBAgIUN3iWwapdoTGMcEIh7B93Kk/LSDgwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIwNzIyMTIzNTAzWhcNMjQw +NzIxMTIzNTAzWjB7MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW +MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQ +THlmdCBFbmdpbmVlcmluZzEVMBMGA1UEAwwMVGVzdCBTZXJ2ZXIxMFkwEwYHKoZI +zj0CAQYIKoZIzj0DAQcDQgAEVtJYgP0zQ8Ram/k30RegXD1+M/T3cFGo41zjv29J +C8D+ChPM5N++zs9hsizUuCSao78f2BZKA1HHsgDfG490FKOBnTCBmjAMBgNVHRMB +Af8EAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwHgYDVR0RBBcwFYITc2VydmVyMS5leGFtcGxlLmNvbTAdBgNVHQ4EFgQUAFY7 +zYshmTv1ybP38TER/AmOCqcwHwYDVR0jBBgwFoAUhuB2PRJdXF9BqZTxpANWh+Qv +NZswDQYJKoZIhvcNAQELBQADggEBAIuWeMJTg+nXTBY/wq02nis4AnXNRvt3ffI7 +zspUi/M8My7vMpdOQgFNopZmF/gsEONJlJaTXOK1C0HDJqnN6pcTKB8GWMPMb6UK +bD3ThvdLSs5e8TPzisXu3OMF5mwJgdofoiRFdQyCMEa5wRcolGiEo4iAXjdkjux2 +ik3oqYEAkXVX6k/kX017WHtFi9pPjI31G9vjj82xTp/ufzrRT48n7NEI9cxFGp62 +VLHL8ZG0HNiaDLuOOTE9zwkCjjci5209CYkcXnk5QgDxFlyOaNWQ8hZrNWsoHXMf +kdAgIbnTUx9ifc3z4wcUHsBGMVPhcBmDQAo3ERNo3jLlaC3l8bY= +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert_info.h b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert_info.h new file mode 100644 index 0000000000..aca8115a97 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_SAN_DNS_ECDSA_1_CERT_256_HASH[] = + "fd3e736f84d2606cde1afb4a354b6d8ec80551373d1f5cc6684887f3a9820283"; +constexpr char TEST_SAN_DNS_ECDSA_1_CERT_1_HASH[] = "6d032a0093fed164281c83706ac603c6e2aecb9e"; +constexpr char TEST_SAN_DNS_ECDSA_1_CERT_SPKI[] = "Lrk/9ngJ3wwT+/0tGsSI9RlSYW8ZUJQKC4W+cbmJQ08="; +constexpr char TEST_SAN_DNS_ECDSA_1_CERT_SERIAL[] = "377896c1aa5da1318c704221ec1f772a4fcb4838"; +constexpr char TEST_SAN_DNS_ECDSA_1_CERT_NOT_BEFORE[] = "Jul 22 12:35:03 2022 GMT"; +constexpr char TEST_SAN_DNS_ECDSA_1_CERT_NOT_AFTER[] = "Jul 21 12:35:03 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem new file mode 100644 index 0000000000..48121ac4a4 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIK08afVWSlwf4NKrcPEXY0tfp4lAb0+GrD9IAWRDWxxpoAoGCCqGSM49 +AwEHoUQDQgAEVtJYgP0zQ8Ram/k30RegXD1+M/T3cFGo41zjv29JC8D+ChPM5N++ +zs9hsizUuCSao78f2BZKA1HHsgDfG490FA== +-----END EC PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert.pem new file mode 100644 index 0000000000..e5eef34204 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDUjCCAjqgAwIBAgIUN3iWwapdoTGMcEIh7B93Kk/LSDkwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIwNzIyMTIzNTAzWhcNMjQw +NzIxMTIzNTAzWjB7MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW +MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQ +THlmdCBFbmdpbmVlcmluZzEVMBMGA1UEAwwMVGVzdCBTZXJ2ZXIyMFkwEwYHKoZI +zj0CAQYIKoZIzj0DAQcDQgAEgTkcrSUm4B4Sv3ifwtzTDE4juNNO+YHCovHtYp3G +lz0lrB3MTV29NEzFOtAfMvZH8vnnhLPgvFk17AZG1poye6OBnTCBmjAMBgNVHRMB +Af8EAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwEwHgYDVR0RBBcwFYITc2VydmVyMi5leGFtcGxlLmNvbTAdBgNVHQ4EFgQUuB4/ +jhYMMaUrEIsLnTXkX9/5tUwwHwYDVR0jBBgwFoAUhuB2PRJdXF9BqZTxpANWh+Qv +NZswDQYJKoZIhvcNAQELBQADggEBAA5B+Gnq8P8ww1JXaoJMdD3xT1otdjE4PGqh +zOfHfJCQRg8gpL0lMGvTbDQkjKVVqhmDBeqktuNAJq9HCAvbaaOSCuBKEB9ZUIF7 +e2sV/z7jc3oAuWn3a8fbfhBVKWqmvBomBHp3RVIvuhusIVIuLnYs1dBbKqqRjDR3 +cYLtPjwsCTcCO6kCntVsO3/+nZFl1n3OfWGoQ4z7b+Ra+F9vIbLrIdy+7ouiRuZW +clBBOSMyfFh7Grqu1SBilCJ4e64rianwMd4y6rZo4jMm4uh8FhPGGnp13lhLaMgN +8dEQLPOA51hHG64hJMIQ7cvDpfh5WAPlqDIJyqFskRZC+TK9GFo= +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert_info.h b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert_info.h new file mode 100644 index 0000000000..ea2125f570 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_SAN_DNS_ECDSA_2_CERT_256_HASH[] = + "8ff1f6f38244b1912162633914c538ddf048c6e4085681514bd54fc5f1e28c4a"; +constexpr char TEST_SAN_DNS_ECDSA_2_CERT_1_HASH[] = "e74d5203de9ced0af5ccca4ffe26e123889eb870"; +constexpr char TEST_SAN_DNS_ECDSA_2_CERT_SPKI[] = "GXUREIuVzB8QMuleN2IAxwDIIPImuBfqYMv4s7J2nkA="; +constexpr char TEST_SAN_DNS_ECDSA_2_CERT_SERIAL[] = "377896c1aa5da1318c704221ec1f772a4fcb4839"; +constexpr char TEST_SAN_DNS_ECDSA_2_CERT_NOT_BEFORE[] = "Jul 22 12:35:03 2022 GMT"; +constexpr char TEST_SAN_DNS_ECDSA_2_CERT_NOT_AFTER[] = "Jul 21 12:35:03 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_key.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_key.pem new file mode 100644 index 0000000000..b421792a48 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_key.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEID4JjbrZHcZu282UmgKv4hSNecLoBPtnDcxkUcFqKOyHoAoGCCqGSM49 +AwEHoUQDQgAEgTkcrSUm4B4Sv3ifwtzTDE4juNNO+YHCovHtYp3Glz0lrB3MTV29 +NEzFOtAfMvZH8vnnhLPgvFk17AZG1poyew== +-----END EC PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem new file mode 100644 index 0000000000..88606726b7 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIUN3iWwapdoTGMcEIh7B93Kk/LSDYwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIwNzIyMTIzNTAzWhcNMjQw +NzIxMTIzNTAzWjB7MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW +MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQ +THlmdCBFbmdpbmVlcmluZzEVMBMGA1UEAwwMVGVzdCBTZXJ2ZXIxMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAygB/Z1opW0+b6WFSM3KjjC3BnZ80jWeH +cJ+RPLr/nhpLCdNAcGGSf+d5OptE5E4zX7ho5oL3cP0RpXEfAdYdtZyoQTHzWObG +mV5Rw8y+++nY+UaicGQVAOBZaP4IkG1xXHGNHlLLejAM6nRBrIozzJ1XnQ2BGzQz +aGv4L1IglH5YT1ANElkqvjOJldCO2HmdSKK8/iP0/mWkuD3LUCsmfTv27Dfna82h +Uj272sRPULkOS7vG4capgK8yj7Dg+gQjQ6zYQ7+YOdn5oEI4pHRQPkhXYY9PZ4II +mtYG66wrpKExgN2CEcCJWSok3rWBrHcQXpVLXpRrab966MvmHuDBXwIDAQABo4Gd +MIGaMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUF +BwMCBggrBgEFBQcDATAeBgNVHREEFzAVghNzZXJ2ZXIxLmV4YW1wbGUuY29tMB0G +A1UdDgQWBBS0LQEPCuVAvxbD4lI9lG9sHntJ9jAfBgNVHSMEGDAWgBSG4HY9El1c +X0GplPGkA1aH5C81mzANBgkqhkiG9w0BAQsFAAOCAQEAm6kqfeIYTUcTM+f/liFT +FHfKogb94ly9IFCTNsOvQhWSkTUc3UahDQ02HWQ7M+31f3uShdsUcqmNrouKPa9r +1kmma6/CJizVKeUNIIRZ93IjDUAV2guUIeTgONjWyBQWFXwApBgQzYqmX2vj4GD4 +EpmMBREfhXzbxMRwwNtgW6bkAsy8YRyPur+K758F5/TVa9Jqa+bcnruhiKR2J5kN +ojCnf5HQ5LHAR0HJjffWtE1oeJPITinBIIHmF+yAKitE7s+0OcYEV/QIwyPpqvKg +Q8KWGuSSzvVJvnrMdqqWSfKjoYaYKmPheLmmtrLoOFbH2AB04IiM21wSQ9gTxSRE +tA== +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_info.h b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_info.h new file mode 100644 index 0000000000..612192fc76 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_SAN_DNS_RSA_1_CERT_256_HASH[] = + "eefe9d41f984a25ad90c4f772819f2b068c4f7ac554d87c54f608fdf326487cb"; +constexpr char TEST_SAN_DNS_RSA_1_CERT_1_HASH[] = "ff3123ce14e97cdbbe24e44ff05221bae2bb3324"; +constexpr char TEST_SAN_DNS_RSA_1_CERT_SPKI[] = "LiXVsWYX8LOCG70Yss1yF4bcz5dQ/xyxpg22qrKCWqk="; +constexpr char TEST_SAN_DNS_RSA_1_CERT_SERIAL[] = "377896c1aa5da1318c704221ec1f772a4fcb4836"; +constexpr char TEST_SAN_DNS_RSA_1_CERT_NOT_BEFORE[] = "Jul 22 12:35:03 2022 GMT"; +constexpr char TEST_SAN_DNS_RSA_1_CERT_NOT_AFTER[] = "Jul 21 12:35:03 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_key.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_key.pem new file mode 100644 index 0000000000..10bf6d1754 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEApKl+M1Z3KBMAk2WZyBZDk38olMFpDQ86L/P8Z6NWpu3vIzpT +/RID/0+h5cjsJG8lvDKQiXv86mvSAs9993HTWTh5/vS+JQOpK3aUTaGZty8C6i// +trJ/KoB0F51XcFC35k7JPPt5ZSUL3bJENmLRN3gzqZug/OqkIKQoo1wpdaY7erst +Mp6vU1g9t7jw0dfvRKsi93JjqNwFpW38TDfq8Xv6JyEcrhYp+yvEbozgu6ALv+HD +srHNuI0LMeymmDUyEBwO0mgB9uklb+aXbcM2HEg9Heh4Vehfsrj5TZdnc8W2RLg+ +bwaKV19mTNhdMymdb/S+xm3tlTiPsHhP5UbhEQIDAQABAoIBAHcMK7fGLcQFaQco +D7S/k//RkW9rHh5ZAgKlbbkoOH/bymc3xn1TRxyRYEWXrFLzIN8d163uvH5nkaCC +j1oVK254DIuuOsLcELThuHsf+Nid3oeeSEy4ZIQ/9q8UDKGhWjPihk/WqH/d9uRD +/FktvIFUIFXhtNTmd985kD/7Y+YwLfkkpFNRo5k3nXmX/40wNsw8KTZIsMCyB9u8 +vQ1mCthquHcfRPfPFGQsEyIrmaV068K2hGa2iyq4rnchGSahX17z2dnCIsIeF4Op ++J6FKEPMUw7P8wzEY4tk1y+9MpZSrt5T82VlP0ucdip+1ODCHcTwgQd/yHLurQWh +2j+jA3kCgYEA1YQLZT+Ut1CGYXc5qq3HHE3WMA4JI4Yn1OCpiUvtx4uEk+4iq+Aw +278Ay1Mreu1c39YPz/FY49Y7XySTul+OX2ZjwL6NV3k22/7nFmrI2C76T5gBeoBG +kgrskM3beLeD7hVBDXgKW6CRMd/MImQMfEW1ILOqvNAUrXn2w5WSjlsCgYEAxWz2 +qLtNiE6SlrXL/+3jAfIB3FjMOdmTrEIqVS/tKfc7ItPoO/gMggf+t1++pxPp2CNW +fW+eNjhbBgLpTCKNDRPLNfYNRws/aZnK7qfEfCgKn+5r2XgZo0xFdlwSqzvz88VA +RFURUJtyUERvKP4UyN1+VZ7VY0L9OIRg79XfggMCgYEArDOq3SXCU3ZmawU1RSqS +g4ljTa6EQFpalZ38HfdW9rDXZFeTLvvJa/FIOrQBRUtatEx4xETqZBbMG5fsbUvU +D60Mls8YP4xLanLS/YlyIqkAyGDYEGyUfgTBdqgQcfPQO5x4stw1vmBpuce9Y589 +Up0F0DIXaeCITMEo++UOJjECgYEArZH44auapKX/tsSqT53DoOg1vpXLJ2kkraOW +OELkjaqwsRQA7whLZt7EkE96/MVDKCUZWzqQrDarwAM7PRZ7T+4BELX8QmOfIqZ1 +hZC17Dn/7U/ePdmbILhjsYWNXEJfXggjUnA/wtCHlg40dKO9fkNNWNVF4fj5S4EH +AJTV5iUCgYBE8WGcFVqSfbkkZNoWb2t4Keq9B1kzQe6HCEpXJr+vCu6D8AdU/eQV +27FgBtlhi3CfgnYB5RtY5zX2XFmsK2jULKh5O0Nfp9ANuRnxDd9rD/daxOEUFFuT ++X5p0ksq8ENBFrtL1Sa2zxbHSOtX+VcseFuN/oKCX49m425cH9M5Mg== +-----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem new file mode 100644 index 0000000000..94460f0a81 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAygB/Z1opW0+b6WFSM3KjjC3BnZ80jWeHcJ+RPLr/nhpLCdNA +cGGSf+d5OptE5E4zX7ho5oL3cP0RpXEfAdYdtZyoQTHzWObGmV5Rw8y+++nY+Uai +cGQVAOBZaP4IkG1xXHGNHlLLejAM6nRBrIozzJ1XnQ2BGzQzaGv4L1IglH5YT1AN +ElkqvjOJldCO2HmdSKK8/iP0/mWkuD3LUCsmfTv27Dfna82hUj272sRPULkOS7vG +4capgK8yj7Dg+gQjQ6zYQ7+YOdn5oEI4pHRQPkhXYY9PZ4IImtYG66wrpKExgN2C +EcCJWSok3rWBrHcQXpVLXpRrab966MvmHuDBXwIDAQABAoIBACXpaf/vwAtPKafn +iBH31hIdFFWM58QSRi0xBYUsY4U80hCKAXIWHfS7cNk7AvcGZkX0yltZAS/Hj2RJ +ktQ5Z2IKgNmbklyBE4z9UsM4zi9heENwqlQFtJtxf9ZgtyLEJ55+AUpLp7zK/+GQ +6DKFzar9bNAhFLYSWgOjH/VhqJO1vOkEXf2z/CaDfOVuZ5zHerKdhBC3QBe9GG9i +f18tWmDXVzdYhFEfZ4sI8MCCyaIWLiikRZje55lr2dcamNA7J9QONX34ZOIdQL5u +X1rqIgHA8eqKb+k4DSqyPTp20cbGXmAJzAla7chknNG4en6kGI+4zcI1NrBmXG6s +KRbK3gECgYEA/WdFvNoyXk0EahMc3pdm0EmXBULo6yNZAy80cXkPuW0xC0RTTlor +xPp3KlddZ8FjPRx/2Ef1UR+cvZrGTrfsUQ+3Nh+5LRR5cXIB+w9MPooysV+v5QV6 +VeQS47YInw/+atI5bv/k8wBFfcaZNKBsvpeWtAUOy5UeLXWxf6ZF7B8CgYEAzBJj +lIazShnG52Eqd6joWmXbis/kMJbCCJdgypymLL8V/epVaVTSjJ6yQmTGWZjUcDhs +q88WrzRLQAe43x/LOl4gOefLaq1zHvTon0syn3vpSDFm+2pF1nSBiVhanYflL/In +nHQIj7OBOXxm+TsGFxajZkBmuBtKCX6UXpV+gsECgYBQDbAaWVRQNkSOvjsAlxQD ++TucjebHYgNc3izTLEuraLN1u8AVoKbzM60x0yfO/2k6hSZeTV1So4oA3ESJ2dDu +Sui2UI9PjohPLpUxm0Oa/k3Fe1U2JOTg/Yx6I6fVmFfnp0I9rtnFavCJK5R667Nv +ArS5al+iMRm3gtbwXLb6QQKBgQCU/bgGcHHl3/wAYkiHJKK/nS40oqOPdgIkE6Fv +S/9nG/wKblP9mxg94n9zSfAm6hXv+MIejguZzyxBi2HGRYgzDRWqMTmqoQgzycss +wdIr7mPbeXYeiPCEKnuIsyE1cko6Pyn8aBr4D66BMfpcbGN95+FUfiCAuivL0saw +Z6T+AQKBgH1Ujf2PU64O+5eqFXuIjz6ApSlQ1X5cRQhqof5OS4/PW1CLm7oiGNA0 +kJzwHvBVPOV0kjez7jV/wk+jlZgt3VhBpu0CwjiOo0U61jKaikHSvyv3BMfM89th +2juTsXzK/l2kBW/eRz/nOJQVVqNa7eBj3yVOZseZ9P9SmArndX/f +-----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert.pem new file mode 100644 index 0000000000..998e54e49a --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIUN3iWwapdoTGMcEIh7B93Kk/LSDcwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIwNzIyMTIzNTAzWhcNMjQw +NzIxMTIzNTAzWjB7MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW +MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQ +THlmdCBFbmdpbmVlcmluZzEVMBMGA1UEAwwMVGVzdCBTZXJ2ZXIyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4vT0uFxWSEAoxjpvpYBhZoxdhXGQGm7d +e4C3Q7nwFT6p5uhgYk8L6IpDcz8ONoICS9sP3yCtAiqbn2ckK2dnsXlqXFvpOm8E +OuPEu9yTDs+PZ+pMqriL8btY1wTlHrFA5slSg7IFKjuAKLi1b/DzYI+8B7utZwtG +jOJhHOLc0Gmi/zVAAoMr9oaDXFGV/GX1iba1BpGwxO6vxFWznbOB9RUsUZRvqcYn +gQmjppetjiY03pRbr3uEosh86yxrQvdFWhFf3MM4W5sD/2eOZv8ZoI5x1WYDGBs+ +ZmxLkLEFtE89io4Oa+gjfnJRa4isJ9VhxTuGsuzmRNxLFaRCEWB5nQIDAQABo4Gd +MIGaMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUF +BwMCBggrBgEFBQcDATAeBgNVHREEFzAVghNzZXJ2ZXIyLmV4YW1wbGUuY29tMB0G +A1UdDgQWBBQnEA/imIabg7imaM/PFT9nu+6mUjAfBgNVHSMEGDAWgBSG4HY9El1c +X0GplPGkA1aH5C81mzANBgkqhkiG9w0BAQsFAAOCAQEAbIlmHEeog9y/foKxIZW7 +a94uuPUwgydI4XuC1tQYbPYk3/tK7Eom69+guqIAKRxU0v7tsRAt9aA04BR1xv/S +ofO3YRCVxbSw+VKunSuoxrF7vkPzviMIbTF/7rFmjc+ppvW+D4Wis4w5OQV1TQgg +HP+WrrEYQH+iko/T7NGcAD94SSXPrI78TuSb1Yl4/y6JUUhp/ly3ilYGfJaFxth+ +uAvBZeWOdTk9IhxQwtoKhURRX75gcI+yvQAvaLgr4VcHDMVUxGjCfTSvP6P4AMfU +qWaoMeuKkMRYKPlCVgo06k7zQbUNhFtaHIqPFCOuPFh1zNx8cOrrRED3e0vJgGgI +Nw== +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert_info.h b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert_info.h new file mode 100644 index 0000000000..55dfa51ab0 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_SAN_DNS_RSA_2_CERT_256_HASH[] = + "90ca80781b0e54ae256d7c427c858ec1a32167a1dfbafbebe1c76831e1136ea4"; +constexpr char TEST_SAN_DNS_RSA_2_CERT_1_HASH[] = "2e876ac711e47b895bcfde70412ff15b83eed962"; +constexpr char TEST_SAN_DNS_RSA_2_CERT_SPKI[] = "SHPkbABvYY0s3Xa2c5hwPYD+0e2LVvH5Q3SwZ1Gj6Tw="; +constexpr char TEST_SAN_DNS_RSA_2_CERT_SERIAL[] = "377896c1aa5da1318c704221ec1f772a4fcb4837"; +constexpr char TEST_SAN_DNS_RSA_2_CERT_NOT_BEFORE[] = "Jul 22 12:35:03 2022 GMT"; +constexpr char TEST_SAN_DNS_RSA_2_CERT_NOT_AFTER[] = "Jul 21 12:35:03 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_key.pem b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_key.pem new file mode 100644 index 0000000000..1b50943ae4 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA4vT0uFxWSEAoxjpvpYBhZoxdhXGQGm7de4C3Q7nwFT6p5uhg +Yk8L6IpDcz8ONoICS9sP3yCtAiqbn2ckK2dnsXlqXFvpOm8EOuPEu9yTDs+PZ+pM +qriL8btY1wTlHrFA5slSg7IFKjuAKLi1b/DzYI+8B7utZwtGjOJhHOLc0Gmi/zVA +AoMr9oaDXFGV/GX1iba1BpGwxO6vxFWznbOB9RUsUZRvqcYngQmjppetjiY03pRb +r3uEosh86yxrQvdFWhFf3MM4W5sD/2eOZv8ZoI5x1WYDGBs+ZmxLkLEFtE89io4O +a+gjfnJRa4isJ9VhxTuGsuzmRNxLFaRCEWB5nQIDAQABAoIBAQCu4+Bo6Q68ImTJ +TGKYgHOOGIBAwqAvHluiPi/7Cp+NX3qBq3QKjas5iMItXtvO+SRX2/lBEecAEmFV +1p8yqz5mro9dmJ4DH2V9E+OYeIMCGMQDabOmowuEPcBaATWmDyVaMq1ZOqdsQemi +d6OpHV4OfPSCaQdYroGxMwluvws1tZ4Fd2YJf0l97Qo50zyo2BLDEq5kpxOyTBgZ +ClPNEhK/R/lz074LCpG/Tula7mjV3DTCdU0C7chdJJhdqvT/8grJmN0c2BpdqVh+ +sPDtLtVZYkMgBqbDiuqwihliUFJTCEYqU7Dr9wK1u2gxbfCqT0Ycms8f8aR8E16I +SIb9TauBAoGBAPZ9BtJyn9quhtdhmsjeCU42s/7AHn7BFgrVZZg1A/28qcBgX5CJ +UQ59bUcFPtrx0TreQBR/Sdm5OdHi7fKQQ9CnryKeRMtU13eKgFiL6uxNuvLwfoeY +9DLdxxmWdFPdHA/Ypc3pBsu49xaDkm5u8CFTLNUZtbrBsAUvpJDPM8/tAoGBAOu2 +++6bWPoBX4xQNluXKkdha3kNzOLo48tXXpbLRS19wuaLbEcPTOp9j1x9Ny9vTYgv +tGvjLhzZ28WNbEr1wo7diKOho7AU7awRQe3VUk+7RaEMX5iP3KZRDbCuyj29uDS/ +g3EBCl5ofjs8hicNS+OMufMl+w7vJ+FPENwvPjpxAoGBAON+u3gqkP1djVhmXly8 +CWylsnUSTlMA1N5E3JKhg8CBi07h4/HbuvMbsmsVHVEoVMr3a78/SCLqAMKFpsX0 +fpKq4UqbNrVP5doP+6s94CIfD8e/TZjLA7+az3L/WPg6H/dEJquoQjxux6aA4vqH +65tmT/+SzhbDQzlWIpYAlzp1AoGBAK9CJp+KseVxD/Hr0f7Xrn/UV2xyblxofLa8 +B4BnMtBYKYnxMFjFb5MKOIW0NEv4ndUcYzG0gJzC/z0rodE6WaaP4QCJNoO9i9nc +GwETE1hlDaho4ss0bALdUSiFTaoKXeQaYUisVWFMoE66+aQfpsVR5RB8tOatuyEz +AH42QjaBAoGAXekFyp6NA+ZgsZR4jmMqm/2lRv64YQOkzwwdkwCVoJLP9Tn34rGg +uJcNypzQQoFssnLABVhFyTQDi6n1p5XXmLvjwh1lv04FiXl1LnOllLaHUB8Mb3gI +6h+FjqjgZg/Zov3gSrKeG5K7DkwHea11A8PONjPYB4+hpZ+qerBSYJ0= +-----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_server1_cert.cfg b/test/extensions/transport_sockets/tls/test_data/san_dns_server1_cert.cfg new file mode 100644 index 0000000000..27ed534ae1 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_server1_cert.cfg @@ -0,0 +1,36 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +countryName = US +countryName_default = US +stateOrProvinceName = California +stateOrProvinceName_default = California +localityName = San Francisco +localityName_default = San Francisco +organizationName = Lyft +organizationName_default = Lyft +organizationalUnitName = Lyft Engineering +organizationalUnitName_default = Lyft Engineering +commonName = Test Server1 +commonName_default = Test Server1 +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectAltName = @alt_names +subjectKeyIdentifier = hash + +[v3_ca] +basicConstraints = critical, CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectAltName = @alt_names +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always + +[alt_names] +DNS.1 = server1.example.com diff --git a/test/extensions/transport_sockets/tls/test_data/san_dns_server2_cert.cfg b/test/extensions/transport_sockets/tls/test_data/san_dns_server2_cert.cfg new file mode 100644 index 0000000000..279801783b --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_dns_server2_cert.cfg @@ -0,0 +1,36 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +countryName = US +countryName_default = US +stateOrProvinceName = California +stateOrProvinceName_default = California +localityName = San Francisco +localityName_default = San Francisco +organizationName = Lyft +organizationName_default = Lyft +organizationalUnitName = Lyft Engineering +organizationalUnitName_default = Lyft Engineering +commonName = Test Server2 +commonName_default = Test Server2 +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectAltName = @alt_names +subjectKeyIdentifier = hash + +[v3_ca] +basicConstraints = critical, CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectAltName = @alt_names +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always + +[alt_names] +DNS.1 = server2.example.com diff --git a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.cfg b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.cfg new file mode 100644 index 0000000000..d5532d54a4 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.cfg @@ -0,0 +1,37 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +countryName = US +countryName_default = US +stateOrProvinceName = California +stateOrProvinceName_default = California +localityName = San Francisco +localityName_default = San Francisco +organizationName = Lyft +organizationName_default = Lyft +organizationalUnitName = Lyft Engineering +organizationalUnitName_default = Lyft Engineering +commonName = server2.example.com +commonName_default = server2.example.com +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectAltName = @alt_names +subjectKeyIdentifier = hash + +[v3_ca] +basicConstraints = critical, CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectAltName = @alt_names +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always + +[alt_names] +DNS.1 = *.example.com +DNS.2 = server1.example.com diff --git a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem new file mode 100644 index 0000000000..fbbf71d776 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENDCCAxygAwIBAgIUR8m6bvw3FZDfqgVrUZYYHa8XgucwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIwODA1MTAzMDUyWhcNMjQw +ODA0MTAzMDUyWjCBgjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx +FjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsM +EEx5ZnQgRW5naW5lZXJpbmcxHDAaBgNVBAMME3NlcnZlcjIuZXhhbXBsZS5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDl6HmgvSL66DUZV3sGizED +j5pz40DRhFfGPtXhYEJirjPY/1THjLKXXiTXC9WU7vmYnzr/SoHSYJkYC5z/VVPw +lXjMZkoq0ff6q3mysWNmuvDKInw8/CHXt0EZ9fs4u7Uj/gfvKN9dHMennsgY3pzy +ZxxGTR4c3QJxCYDw7OS3dkqwXq4mVafGC9Sf6sKuYEMAiSGlVyC4KM9b6hyczr7B +sMaWn2Jc6okVvYbedymMmly8KrGuV/a8ev8wA7uYQXaQLq8lsRg4RJu5r1tCIs/M +fVGRDpUClAT5k4wlwSbg3cW/ElulOYGP2RkxKWp16heVsT7QCP9j4sm3AvP2mhb3 +AgMBAAGjgawwgakwDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYw +FAYIKwYBBQUHAwIGCCsGAQUFBwMBMC0GA1UdEQQmMCSCDSouZXhhbXBsZS5jb22C +E3NlcnZlcjEuZXhhbXBsZS5jb20wHQYDVR0OBBYEFNtjqZr7arX5xNkSO6DHFmdN +NLoDMB8GA1UdIwQYMBaAFPI5fSZyiR780MSvThWzAtcySc2TMA0GCSqGSIb3DQEB +CwUAA4IBAQDEQ8skVqZ2Yh92sY+YNeMIDjuQBTnWTIYKvOuA761iMgMFxdXerICO +9jils5FhshCFgAtoL3hYaEx4ot4vXvGw27T9lpIwJPN4XpJZ3nJ0GHU/cYdbeLmR +3RZ85alW2YEUh2ObQArb7Ce0H84x0w/oaMuqc/33w9qCXD2gNyKmHbJ6TnRPMPS3 +7lK3vVSga4K6TJfGShKGGC/c5PbLp7QZnCane3vVJh7S3wcXoa2K2cS2HlmpWMDQ +9CDJaCqNg06RWXLfVLZD/cglf1HY4i9EtaPxoVQWo6xzWtqzGsMDcbJkeUdQuc8D +7ul6DhzD9AdBZZhSfndm2BYx0jkeH+0V +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h new file mode 100644 index 0000000000..2581393206 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_256_HASH[] = + "5f9f44b7cff504eb174c1f0951b2c194635018bc96f79d307543cd94fb3c7ea9"; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_1_HASH[] = "83f72dbab8c7d0de34e5339b8af8f8c721ad6b6f"; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_SPKI[] = "Ox2VaAfYGt0G4jjFg4XSJGRIA3Fk4WQfMgH296xTTuI="; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_SERIAL[] = "47c9ba6efc371590dfaa056b5196181daf1782e7"; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_NOT_BEFORE[] = "Aug 5 10:30:52 2022 GMT"; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_NOT_AFTER[] = "Aug 4 10:30:52 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem new file mode 100644 index 0000000000..85249aeb49 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA5eh5oL0i+ug1GVd7BosxA4+ac+NA0YRXxj7V4WBCYq4z2P9U +x4yyl14k1wvVlO75mJ86/0qB0mCZGAuc/1VT8JV4zGZKKtH3+qt5srFjZrrwyiJ8 +PPwh17dBGfX7OLu1I/4H7yjfXRzHp57IGN6c8mccRk0eHN0CcQmA8Ozkt3ZKsF6u +JlWnxgvUn+rCrmBDAIkhpVcguCjPW+ocnM6+wbDGlp9iXOqJFb2G3ncpjJpcvCqx +rlf2vHr/MAO7mEF2kC6vJbEYOESbua9bQiLPzH1RkQ6VApQE+ZOMJcEm4N3FvxJb +pTmBj9kZMSlqdeoXlbE+0Aj/Y+LJtwLz9poW9wIDAQABAoIBABSaVbP63xSfFIsV +1NeMR7Mq/kteVSOxeXAxl1cgGjLfq5PJpd5QFH0OGb0PuW8kT0fHFTFliubAc1e9 +Sq9zBRetPWsLy5+0u0VCcqB51w+Qtx1WI84PRjUfnfzml0HJSH1WOX3MKuAcQSQb +TZE0+dmbNeRy6rzpbVuV33E3uXNbQe4ocXPZtNV3KFX47CPZeBPrN6XZPdVPDp4g +oGzB5YKSwRPFfHDarBfyuFxs8VQq7AUYgNGp91a/QWBsX65F/lW1THVRFDSMiBXn +09ZXhq5ZxUluPKrIw9E/4HG/oDcsg9oR65CWjYXC02LiWdW/Pe79DIERB/XyQgI6 +dTmM8xkCgYEA+AcaNzkvDk7/eJRKzip/JAHQOuzyQkdGFgKwg3Kc9fXkGlotFzwF +SJ4v18Jmu6kKE/Oz94mt4b4CYaliTZfME/u/h0WYOj4xL+/VzJhm+kRIrdLPGqcW +p43WRIvY44d9YLFyU8ELYyP1jLpJNFa5zVP5SjzKFlKmgE3vAXe73i0CgYEA7UxG +cWvbo76fTsnhgjC6X5PUE4b38/5hnZ4hCI/2JHzN3Wzv0ki7ML9wuizKCxEw0Mfq ++8gL5XNaDchJdkFrua8roPCM7DQEfGB/jns1Rd34PZrJejnGvjtpg68AXpnmhJYc ++URKGS1SseuTa1cRGBNL9pNEGKr3J/TODcg4pDMCgYEA8Vgf51qKclbmelN6EvEB +NG1bTOthtKKcDTDix5WQLDnvksDqnXA3B/l9PmqZAsQy3UVHeniLLV1x/cwPrscl +utA+B4ft38bCoA21MdeeZKprOlEmW535CmOW+q5GBujBVIR2Zg8zYG4OMrjWIMq/ +E5BBxD4wutYD1FeBWSFEOqUCgYBUzLkvtxeagHh1s3c/CyLPHQjgoY7iRrmjlerA +IJIZn/ABfPiG8S7T0NX78h2Rrub+9TJoH6kAqiQ4YKa/98kDZjH1JYF2t3AS8nki +9ayn9xbLDRGK+DKpsJmYUWWUaPMT1oEsItdIAAZZRpI7/bVCIUM4LpBbMF16jeVE +z5ROOQKBgGr+j4Upowy7dg0wJN12wn3Wxi8gVCZ2arPeb47XzEXWnBTSYzolrve+ +LFVmqrsQ6DOM5Ek+ga2TWGII1qibUx12wOQugw7vKFIE0j8DnrdMDHw34zHp5Il8 +HSnozjMy4QTKv2/Gqh8R9bjD9gsdOFZvTI4vJgNoqHX44U+n6PeJ +-----END RSA PRIVATE KEY----- diff --git a/tools/extensions/extensions_schema.yaml b/tools/extensions/extensions_schema.yaml index cc28bda0c3..452bdd429b 100644 --- a/tools/extensions/extensions_schema.yaml +++ b/tools/extensions/extensions_schema.yaml @@ -114,6 +114,7 @@ categories: - envoy.path.match - envoy.path.rewrite - envoy.tls_handshakers +- envoy.certificate_providers status_values: - name: stable From 6e62821764ef584fb2ebfb73ba4ef9902f3a18b4 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Mon, 26 Sep 2022 05:08:19 +0000 Subject: [PATCH 02/35] Add docs for building TLS bumping Signed-off-by: Luyao Zhong --- tls_bumping/README.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tls_bumping/README.rst diff --git a/tls_bumping/README.rst b/tls_bumping/README.rst new file mode 100644 index 0000000000..5e4a9640a3 --- /dev/null +++ b/tls_bumping/README.rst @@ -0,0 +1,21 @@ +How to Verify Envoy TLS Splicing/Bumping Functionalities +======================================================== + +Download and Build Envoy +------------------------ +#. Download the code to local host:: + + $ git clone git@github.com:envoyproxy/envoy.git; cd envoy/ + $ git fetch origin pull/23192/head:tls_splicing_bumping_test + $ git checkout tls_splicing_bumping_test + +#. Build Envoy (Follow the official documentation): + + compile the Envoy binary locally (https://github.com/envoyproxy/envoy/blob/main/bazel/README.md#linux):: + + $ bazel build -c opt envoy + + alternatively, build the Envoy docker image:: + + $ ENVOY_DOCKER_BUILD_DIR=~/build ./ci/run_envoy_docker.sh './ci/do_ci.sh bazel.release.server_only' + $ docker build --build-arg TARGETPLATFORM="linux/amd64" -f ci/Dockerfile-envoy -t envoy . From ed842b04a59c484a5d8250eee4dd85e5088f176c Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Mon, 26 Sep 2022 05:33:39 +0000 Subject: [PATCH 03/35] Add docs for testing TLS bumping without CONNECT Signed-off-by: Luyao Zhong --- tls_bumping/bumping_wo_connect.yaml | 95 +++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tls_bumping/bumping_wo_connect.yaml diff --git a/tls_bumping/bumping_wo_connect.yaml b/tls_bumping/bumping_wo_connect.yaml new file mode 100644 index 0000000000..68fdd28dac --- /dev/null +++ b/tls_bumping/bumping_wo_connect.yaml @@ -0,0 +1,95 @@ +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 1234 + listener_filters: + - name: envoy.filters.listener.tls_inspector + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: ["www.google.com", "www.linkedin.com"] # bumping w/ connect or w/o connect + filters: + - name: envoy.filters.network.sni_dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig + port_value: 443 + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.network.bumping + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.bumping.v3.Bumping + stat_prefix: destination + cluster: dynamic_forward_proxy_cluster_bumping + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: dynamic_forward_proxy_cluster_bumping + http_filters: + - name: envoy.filters.http.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + common_tls_context: + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + clusters: + - name: dynamic_forward_proxy_cluster_bumping + connect_timeout: 1s + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + validation_context: + trusted_ca: + filename: /etc/ssl/certs/ca-certificates.crt +certificate_provider_instances: + local_cert_provider: + name: envoy.certificate_providers.local_certificate + typed_config: + "@type": type.googleapis.com/envoy.extensions.certificate_providers.local_certificate.v3.LocalCertificate + rootca_cert: + filename: root-ca.pem + rootca_key: + filename: root-ca.key + default_identity_cert: + filename: baidu.com.pem + default_identity_key: + filename: baidu.com.key From c7503ab08d15e4362c5f41f8ac9ced2cfb029db5 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Mon, 26 Sep 2022 16:20:52 +0800 Subject: [PATCH 04/35] Add config for bumping with http connect case (#308) Signed-off-by: LeiZhang --- tls_bumping/bumping_w_connect.yaml | 146 +++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 tls_bumping/bumping_w_connect.yaml diff --git a/tls_bumping/bumping_w_connect.yaml b/tls_bumping/bumping_w_connect.yaml new file mode 100644 index 0000000000..7d92dfc72d --- /dev/null +++ b/tls_bumping/bumping_w_connect.yaml @@ -0,0 +1,146 @@ +# This configuration terminates h2 CONNECT on port 10001 and then chains an HTTP filter that always responds with 200 using +# an internal listener. +bootstrap_extensions: + - name: envoy.bootstrap.internal_listener + typed_config: + "@type": type.googleapis.com/envoy.extensions.bootstrap.internal_listener.v3.InternalListener +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 127.0.0.1 + port_value: 1234 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + connect_matcher: + {} + route: + cluster: decap_cluster + upgrade_configs: + - upgrade_type: CONNECT + connect_config: + {} + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + http2_protocol_options: + allow_connect: true + upgrade_configs: + - upgrade_type: CONNECT + + - name: bumping + internal_listener: {} + listener_filters: + - name: envoy.filters.listener.tls_inspector + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: ["www.baidu.com", "www.google.com", "www.linkedin.com"] # bumping w/ connect or w/o connect + filters: + - name: envoy.filters.network.sni_dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig + port_value: 443 + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.network.bumping + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.bumping.v3.Bumping + stat_prefix: destination + cluster: dynamic_forward_proxy_cluster_bumping + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: dynamic_forward_proxy_cluster_bumping + http_filters: + - name: envoy.filters.http.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + common_tls_context: + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + clusters: + - name: decap_cluster + load_assignment: + cluster_name: decap_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + envoy_internal_address: + server_listener_name: bumping + - name: dynamic_forward_proxy_cluster_bumping + connect_timeout: 1s + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + validation_context: + trusted_ca: + filename: /etc/ssl/certs/ca-certificates.crt +certificate_provider_instances: + local_cert_provider: + name: envoy.certificate_providers.local_certificate + typed_config: + "@type": type.googleapis.com/envoy.extensions.certificate_providers.local_certificate.v3.LocalCertificate + rootca_cert: + filename: root-ca.pem + rootca_key: + filename: root-ca.key + default_identity_cert: + filename: baidu.com.pem + default_identity_key: + filename: baidu.com.key + \ No newline at end of file From df331ecefcf142b6dc9b7c107b44e4bb887a4e74 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Mon, 26 Sep 2022 09:40:51 +0000 Subject: [PATCH 05/35] Add config for TLS splicing without HTTP CONNECT case Signed-off-by: Luyao Zhong --- tls_bumping/splicing_wo_connect.yaml | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tls_bumping/splicing_wo_connect.yaml diff --git a/tls_bumping/splicing_wo_connect.yaml b/tls_bumping/splicing_wo_connect.yaml new file mode 100644 index 0000000000..08a83d393e --- /dev/null +++ b/tls_bumping/splicing_wo_connect.yaml @@ -0,0 +1,38 @@ +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 1234 + listener_filters: + - name: envoy.filters.listener.tls_inspector + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: ["www.usbank.com"] # splicing w/ connect or w/o connect + filters: + - name: envoy.filters.network.sni_dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig + port_value: 443 + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: tcp + cluster: dynamic_forward_proxy_cluster_splicing + + clusters: + - name: dynamic_forward_proxy_cluster_splicing + connect_timeout: 1s + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY From cb9d0870eff15da79fc4237a9006b96c8341648f Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Mon, 26 Sep 2022 10:13:56 +0000 Subject: [PATCH 06/35] Add config for TLS splicing with HTTP CONNECT case Signed-off-by: Luyao Zhong --- tls_bumping/bumping_w_connect.yaml | 2 +- tls_bumping/splicing_w_connect.yaml | 87 ++++++++++++++++++++++++++++ tls_bumping/splicing_wo_connect.yaml | 2 +- 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 tls_bumping/splicing_w_connect.yaml diff --git a/tls_bumping/bumping_w_connect.yaml b/tls_bumping/bumping_w_connect.yaml index 7d92dfc72d..ec45c775d0 100644 --- a/tls_bumping/bumping_w_connect.yaml +++ b/tls_bumping/bumping_w_connect.yaml @@ -1,4 +1,4 @@ -# This configuration terminates h2 CONNECT on port 10001 and then chains an HTTP filter that always responds with 200 using +# This configuration terminates h2 CONNECT on port 1234 and then chains an HTTP filter that always responds with 200 using # an internal listener. bootstrap_extensions: - name: envoy.bootstrap.internal_listener diff --git a/tls_bumping/splicing_w_connect.yaml b/tls_bumping/splicing_w_connect.yaml new file mode 100644 index 0000000000..4601afcae9 --- /dev/null +++ b/tls_bumping/splicing_w_connect.yaml @@ -0,0 +1,87 @@ +# This configuration terminates h2 CONNECT on port 1234 and then chains an HTTP filter that always responds with 200 using +# an internal listener +bootstrap_extensions: + - name: envoy.bootstrap.internal_listener + typed_config: + "@type": type.googleapis.com/envoy.extensions.bootstrap.internal_listener.v3.InternalListener +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 127.0.0.1 + port_value: 1234 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + connect_matcher: + {} + route: + cluster: decap_cluster + upgrade_configs: + - upgrade_type: CONNECT + connect_config: + {} + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + http2_protocol_options: + allow_connect: true + upgrade_configs: + - upgrade_type: CONNECT + - name: splicing + internal_listener: {} + listener_filters: + - name: envoy.filters.listener.tls_inspector + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: ["www.usbank.com"] + filters: + - name: envoy.filters.network.sni_dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig + port_value: 443 + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: tcp + cluster: dynamic_forward_proxy_cluster_splicing + clusters: + - name: decap_cluster + load_assignment: + cluster_name: decap_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + envoy_internal_address: + server_listener_name: splicing + - name: dynamic_forward_proxy_cluster_splicing + connect_timeout: 1s + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + \ No newline at end of file diff --git a/tls_bumping/splicing_wo_connect.yaml b/tls_bumping/splicing_wo_connect.yaml index 08a83d393e..618a74bd9e 100644 --- a/tls_bumping/splicing_wo_connect.yaml +++ b/tls_bumping/splicing_wo_connect.yaml @@ -10,7 +10,7 @@ static_resources: "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector filter_chains: - filter_chain_match: - server_names: ["www.usbank.com"] # splicing w/ connect or w/o connect + server_names: ["www.usbank.com"] filters: - name: envoy.filters.network.sni_dynamic_forward_proxy typed_config: From ab29125ec21234d7e7f3490d2e3f1f520eb56186 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 27 Sep 2022 02:23:37 +0000 Subject: [PATCH 07/35] Add config for TLS splicing/bumping w/wo HTTP CONNECT Signed-off-by: Luyao Zhong --- tls_bumping/splicing_bumping_all.yaml | 239 ++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 tls_bumping/splicing_bumping_all.yaml diff --git a/tls_bumping/splicing_bumping_all.yaml b/tls_bumping/splicing_bumping_all.yaml new file mode 100644 index 0000000000..c25f0244f6 --- /dev/null +++ b/tls_bumping/splicing_bumping_all.yaml @@ -0,0 +1,239 @@ +bootstrap_extensions: + - name: envoy.bootstrap.internal_listener + typed_config: + "@type": type.googleapis.com/envoy.extensions.bootstrap.internal_listener.v3.InternalListener +static_resources: + listeners: + - name: listener_0 + address: + socket_address: + protocol: TCP + address: 127.0.0.1 + port_value: 1234 + listener_filters: + - name: envoy.filters.listener.tls_inspector + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + transport_protocol: tls # without HTTP CONNECT + filters: + - name: envoy.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: tcp + cluster: decap_cluster + - filter_chain_match: + transport_protocol: raw_buffer # with HTTP CONNECT + filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + connect_matcher: + {} + route: + cluster: decap_cluster + upgrade_configs: + - upgrade_type: CONNECT + connect_config: + {} + http_filters: + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + http2_protocol_options: + allow_connect: true + upgrade_configs: + - upgrade_type: CONNECT + - name: splicing_and_bumping + internal_listener: {} + listener_filters: + - name: envoy.filters.listener.tls_inspector + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector + filter_chains: + - filter_chain_match: + server_names: ["www.usbank.com"] # splicing + filters: + - name: envoy.filters.network.sni_dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig + port_value: 443 + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: tcp + cluster: dynamic_forward_proxy_cluster_splicing + - filter_chain_match: + server_names: ["www.google.com", "www.linkedin.com"] # bumping + filters: + - name: envoy.filters.network.sni_dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig + port_value: 443 + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.network.bumping + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.bumping.v3.Bumping + stat_prefix: destination + cluster: dynamic_forward_proxy_cluster_bumping + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: dynamic_forward_proxy_cluster_bumping + http_filters: + - name: envoy.filters.http.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + common_tls_context: + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + + clusters: + - name: decap_cluster + load_assignment: + cluster_name: decap_cluster + endpoints: + - lb_endpoints: + - endpoint: + address: + envoy_internal_address: + server_listener_name: splicing_and_bumping + - name: dynamic_forward_proxy_cluster_splicing + connect_timeout: 1s + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: dynamic_forward_proxy_cluster_bumping + connect_timeout: 1s + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig + dns_cache_config: + name: dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + validation_context: + trusted_ca: + filename: /etc/ssl/certs/ca-certificates.crt + +certificate_provider_instances: + local_cert_provider: + name: envoy.certificate_providers.local_certificate + typed_config: + "@type": type.googleapis.com/envoy.extensions.certificate_providers.local_certificate.v3.LocalCertificate + rootca_cert: + filename: root-ca.pem + rootca_key: + filename: root-ca.key + + # The following self-signed certificate pair is generated using: + # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem -days 3650 -nodes -subj '/CN=front-envoy' + # + # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy + # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. + # + # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via + # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. + default_identity_cert: + inline_string: | + -----BEGIN CERTIFICATE----- + MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm + cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS + BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC + AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou + oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ + VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt + ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh + 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX + 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg + vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N + v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ + Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 + zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe + 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I + m+/R4XnmL4cKQ+5Z + -----END CERTIFICATE----- + default_identity_key: + inline_string: | + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD + tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 + VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg + Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj + zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ + oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw + tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB + NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx + lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx + DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 + 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO + eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL + xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 + e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO + mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR + nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB + xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO + EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R + JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 + XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg + +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK + 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 + DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 + o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 + ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU + q5sGxGrC1RECGB5Zwx2S2ZY= + -----END PRIVATE KEY----- + + \ No newline at end of file From 81f77b0972976d119d574df811ba4d041d3c663f Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 27 Sep 2022 15:25:05 +0800 Subject: [PATCH 08/35] Improve doc with testing details (#309) Signed-off-by: LeiZhang --- tls_bumping/README.rst | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tls_bumping/README.rst b/tls_bumping/README.rst index 5e4a9640a3..6cece3053e 100644 --- a/tls_bumping/README.rst +++ b/tls_bumping/README.rst @@ -19,3 +19,59 @@ Download and Build Envoy $ ENVOY_DOCKER_BUILD_DIR=~/build ./ci/run_envoy_docker.sh './ci/do_ci.sh bazel.release.server_only' $ docker build --build-arg TARGETPLATFORM="linux/amd64" -f ci/Dockerfile-envoy -t envoy . + +Prepare Test Environment +------------------------ +#. Create new user “test” (assume there is already a default user “ubuntu”):: + + ubuntu@node1:~/envoy$ sudo useradd test -m -s /bin/bash -u 10000 + +#. Set up iptables rule, redirect the traffic from user “test” to envoy:: + + ubuntu@node1:~/envoy$ sudo iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports 1234 -m owner --uid-owner 10000 + +#. Add a CA to Ubuntu:: + + ubuntu@node1:~/envoy$ sudo cp root-ca.pem /etc/ssl/certs/ + + There are a root CA certificate and a root CA private key we have generated in advance under envoy directory, copy the .crt file into /etc/ssl/certs/. Envoy uses this CA cert/key to mimic server certificates, this makes the curl client trust the certs signed by the specified CA. + +#. Start Envoy listening at port 1234(The path of envoy executable file may vary depending on the way of building Envoy):: + + ubuntu@node1:~/envoy$ bazel-bin/source/exe/envoy-static -c splicing_bumping_all.yaml --concurrency 1 --log-level trace + +Test TLS splicing and bumping +----------------------------- +Five sample configurations are provided, splicing_bumping_all.yaml is an all-in-one configuration covering all splicing and bumping scenarios. + +#. TLS splicing without HTTP CONNECT:: + + test@node1:~/envoy$ curl -v https://www.usbank.com/ + + The traffic will be redirect to Envoy since we have iptables rule applied, Envoy works like a TCP proxy. + + Expected result: receive a server certificate issued by the upstream. + +#. TLS splicing with HTTP CONNECT:: + + ubuntu@node1:~/envoy$ curl -v -x 127.0.0.1:1234 https://www.usbank.com/ + + "-x" specify the front proxy(Envoy) when accessing the website, Envoy handles the HTTP CONNECT first and let the traffic go through a TCP proxy without decryption. + + Expected result: receive a server certificate issued by the upstream. + +#. TLS bumping without HTTP CONNECT:: + + test@node1:~/envoy$ curl -v https://www.google.com/ + + The traffic will be redirect to envoy since we have iptables rule applied, Envoy mimics the server certificate and does TLS handshake with downstream. + + Expected result: receive a mimic server cert issued by MyRootCA. + +#. TLS bumping with HTTP CONNECT:: + + ubuntu@node1:~/envoy$ curl -v -x 127.0.0.1:1234 https://www.google.com/ + + "-x" specify the front proxy(Envoy) when accessing the website, Envoy handles the HTTP CONNECT first and mimics the server certificate afterwards. + + Expected result: receive a mimic server cert issued by MyRootCA. From 638f3b51355be50e5819ed8af4f72ad7dcd99d1d Mon Sep 17 00:00:00 2001 From: lei zhang Date: Wed, 12 Oct 2022 15:54:54 +0800 Subject: [PATCH 09/35] Expiration time check (#320) Add config for setting cert expiration time Bumping filter refactor Signed-off-by: LeiZhang Signed-off-by: lei zhang --- .../v3/local_certificate.proto | 5 +++ .../local_certificate/local_certificate.cc | 19 +++++++++- .../local_certificate/local_certificate.h | 1 + .../filters/network/bumping/bumping.cc | 36 ++++++------------- .../filters/network/bumping/bumping.h | 6 ---- tls_bumping/splicing_bumping_all.yaml | 3 +- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto index d477b0ab10..67bcab5286 100644 --- a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto +++ b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto @@ -4,6 +4,8 @@ package envoy.extensions.certificate_providers.local_certificate.v3; import "envoy/config/core/v3/base.proto"; +import "google/protobuf/timestamp.proto"; + import "udpa/annotations/status.proto"; option java_package = "io.envoyproxy.envoy.extensions.certificate_providers.local_certificate.v3"; @@ -25,4 +27,7 @@ message LocalCertificate { config.core.v3.DataSource default_identity_cert = 3; config.core.v3.DataSource default_identity_key = 4; + + // Indicates the time at which the certificate expires. + google.protobuf.Timestamp expiration_time = 5; } diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index a9f579f164..4b17b5e1dd 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -23,6 +23,10 @@ Provider::Provider(const envoy::config::core::v3::TypedExtensionConfig& config, ca_key_ = Config::DataSource::read(message.rootca_key(), true, api); default_identity_cert_ = Config::DataSource::read(message.default_identity_cert(), true, api); default_identity_key_ = Config::DataSource::read(message.default_identity_key(), true, api); + + auto seconds = google::protobuf::util::TimeUtil::TimestampToSeconds(message.expiration_time()); + expiration_config_ = std::chrono::system_clock::from_time_t(static_cast(seconds)); + // Generate TLSCertificate envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); tls_certificate->mutable_certificate_chain()->set_inline_string(default_identity_cert_); @@ -167,7 +171,20 @@ void Provider::signCertificate(const std::string sni, X509_set_issuer_name(crt, X509_get_subject_name(ca_cert.get())); X509_gmtime_adj(X509_get_notBefore(crt), 0); - X509_gmtime_adj(X509_get_notAfter(crt), 2 * 365 * 24 * 3600); + + + // Compare expiration_time config with upstream cert expiration. Use smaller + // value of those two dates as expiration time of mimic cert. + auto now = std::chrono::system_clock::now(); + uint64_t valid_seconds = std::chrono::duration_cast(expiration_config_ - now).count(); + if (metadata->connectionInfo()->expirationPeerCertificate()) { + if (metadata->connectionInfo()->expirationPeerCertificate().value() <= expiration_config_) { + valid_seconds = + std::chrono::duration_cast(metadata->connectionInfo()->expirationPeerCertificate().value() - now).count(); + } + } + + X509_gmtime_adj(X509_get_notAfter(crt), valid_seconds); X509_set_subject_name(crt, X509_REQ_get_subject_name(req)); EVP_PKEY* req_pubkey = X509_REQ_get_pubkey(req); X509_set_pubkey(crt, req_pubkey); diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index d013a9e501..7df5def7ff 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -64,6 +64,7 @@ class Provider : public CertificateProvider::CertificateProvider, std::string ca_key_; std::string default_identity_cert_; std::string default_identity_key_; + SystemTime expiration_config_; Common::CallbackManager<> update_callback_manager_; absl::flat_hash_map> diff --git a/source/extensions/filters/network/bumping/bumping.cc b/source/extensions/filters/network/bumping/bumping.cc index d46232ffc3..3e0fbfe696 100644 --- a/source/extensions/filters/network/bumping/bumping.cc +++ b/source/extensions/filters/network/bumping/bumping.cc @@ -65,12 +65,7 @@ BumpingStats Config::generateStats(Stats::Scope& scope) { } RouteConstSharedPtr Config::getRoute() { - if (default_route_ != nullptr) { - return default_route_; - } - - // no match, no more routes to try - return nullptr; + return default_route_; } Filter::Filter(ConfigSharedPtr config, Upstream::ClusterManager& cluster_manager) @@ -83,6 +78,9 @@ Filter::~Filter() { for (const auto& access_log : config_->accessLogs()) { access_log->log(nullptr, nullptr, nullptr, getStreamInfo()); } + + ASSERT(generic_conn_pool_ == nullptr); + //ASSERT(upstream_ == nullptr); } void Filter::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) { @@ -94,6 +92,8 @@ void Filter::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbac // TODO, needs refactoring read_callbacks_->connection().readDisable(true); read_callbacks_->connection().write_disable = true; + + getStreamInfo().setUpstreamInfo(std::make_shared()); } StreamInfo::StreamInfo& Filter::getStreamInfo() { @@ -218,14 +218,14 @@ void Filter::onGenericPoolFailure(ConnectionPool::PoolFailureReason reason, } void Filter::onGenericPoolReady(StreamInfo::StreamInfo*, - std::unique_ptr&& upstream, + std::unique_ptr&&, Upstream::HostDescriptionConstSharedPtr&, const Network::ConnectionInfoProvider&, Ssl::ConnectionInfoConstSharedPtr info) { // Request mimick cert from local certificate provider. requestCertificate(info); - upstream_ = std::move(upstream); + //upstream_ = std::move(upstream); generic_conn_pool_.reset(); onUpstreamConnection(); } @@ -253,7 +253,7 @@ Network::FilterStatus Filter::onData(Buffer::Instance&, bool) { } Network::FilterStatus Filter::onNewConnection() { - ASSERT(upstream_ == nullptr); + //ASSERT(upstream_ == nullptr); route_ = pickRoute(); return establishUpstreamConnection(); } @@ -269,10 +269,8 @@ void Filter::onUpstreamEvent(Network::ConnectionEvent event) { if (event == Network::ConnectionEvent::RemoteClose || event == Network::ConnectionEvent::LocalClose) { - upstream_.reset(); - disableIdleTimer(); + //upstream_.reset(); - // happens in on onPoolFailure if (connecting) { if (event == Network::ConnectionEvent::RemoteClose) { getStreamInfo().setResponseFlag(StreamInfo::ResponseFlag::UpstreamConnectionFailure); @@ -290,20 +288,6 @@ void Filter::onUpstreamConnection() { getStreamInfo().downstreamAddressProvider().requestedServerName()); } -void Filter::resetIdleTimer() { - if (idle_timer_ != nullptr) { - ASSERT(config_->idleTimeout()); - idle_timer_->enableTimer(config_->idleTimeout().value()); - } -} - -void Filter::disableIdleTimer() { - if (idle_timer_ != nullptr) { - idle_timer_->disableTimer(); - idle_timer_.reset(); - } -} - void Filter::onCacheHit(const std::string) const { // Re-enable downstream reads and writes read_callbacks_->connection().readDisable(false); diff --git a/source/extensions/filters/network/bumping/bumping.h b/source/extensions/filters/network/bumping/bumping.h index 6374d11f1f..09b87f75ed 100644 --- a/source/extensions/filters/network/bumping/bumping.h +++ b/source/extensions/filters/network/bumping/bumping.h @@ -211,17 +211,11 @@ class Filter : public Network::ReadFilter, void onConnectTimeout(); void onUpstreamEvent(Network::ConnectionEvent event); void onUpstreamConnection(); - void onIdleTimeout(); - void resetIdleTimer(); - void disableIdleTimer(); const ConfigSharedPtr config_; Upstream::ClusterManager& cluster_manager_; Network::ReadFilterCallbacks* read_callbacks_{}; - Event::TimerPtr idle_timer_; - Event::TimerPtr connection_duration_timer_; - std::shared_ptr upstream_callbacks_; // shared_ptr required for passing as a // read filter. // The upstream handle (either TCP or HTTP). This is set in onGenericPoolReady and should persist diff --git a/tls_bumping/splicing_bumping_all.yaml b/tls_bumping/splicing_bumping_all.yaml index c25f0244f6..1d9ebf6a32 100644 --- a/tls_bumping/splicing_bumping_all.yaml +++ b/tls_bumping/splicing_bumping_all.yaml @@ -235,5 +235,4 @@ certificate_provider_instances: ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU q5sGxGrC1RECGB5Zwx2S2ZY= -----END PRIVATE KEY----- - - \ No newline at end of file + expiration_time: "2025-11-06T01:31:46Z" From 4709f58d294aab2c3a77016225b90ebf0aeeb3f5 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Wed, 12 Oct 2022 09:02:26 +0000 Subject: [PATCH 10/35] Update splicing/bumping config to cover more test cases Signed-off-by: Luyao Zhong --- tls_bumping/splicing_bumping_all.yaml | 143 ++++++++++++++++++++++---- 1 file changed, 124 insertions(+), 19 deletions(-) diff --git a/tls_bumping/splicing_bumping_all.yaml b/tls_bumping/splicing_bumping_all.yaml index 1d9ebf6a32..1197605631 100644 --- a/tls_bumping/splicing_bumping_all.yaml +++ b/tls_bumping/splicing_bumping_all.yaml @@ -22,9 +22,9 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy stat_prefix: tcp - cluster: decap_cluster + cluster: decap_cluster_tls - filter_chain_match: - transport_protocol: raw_buffer # with HTTP CONNECT + transport_protocol: raw_buffer # with HTTP CONNECT or pure HTTP filters: - name: envoy.filters.network.http_connection_manager typed_config: @@ -38,14 +38,18 @@ static_resources: - "*" routes: - match: - connect_matcher: + connect_matcher: # with HTTP CONNECT {} route: - cluster: decap_cluster + cluster: decap_cluster_tls upgrade_configs: - upgrade_type: CONNECT connect_config: {} + - match: + prefix: "/" # pure HTTP + route: + cluster: decap_cluster_http http_filters: - name: envoy.filters.http.router typed_config: @@ -54,7 +58,7 @@ static_resources: allow_connect: true upgrade_configs: - upgrade_type: CONNECT - - name: splicing_and_bumping + - name: tls_splicing_and_bumping internal_listener: {} listener_filters: - name: envoy.filters.listener.tls_inspector @@ -62,14 +66,68 @@ static_resources: "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector filter_chains: - filter_chain_match: - server_names: ["www.usbank.com"] # splicing + server_names: ["www.google.com"] # bumping list, beats splicing + transport_protocol: tls + filters: + - name: envoy.filters.network.sni_dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig + port_value: 443 + dns_cache_config: + name: tls_dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.network.bumping + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.bumping.v3.Bumping + stat_prefix: destination + cluster: dynamic_forward_proxy_cluster_bumping + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: dynamic_forward_proxy_cluster_bumping + http_filters: + - name: envoy.filters.http.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig + dns_cache_config: + name: tls_dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext + common_tls_context: + tls_certificate_provider_instance: + instance_name: "local_cert_provider" + certificate_name: "ALL_IDENTITY_CERTS" + - filter_chain_match: + server_names: ["www.usbank.com", "*.google.com"] # splicing list + transport_protocol: tls filters: - name: envoy.filters.network.sni_dynamic_forward_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig port_value: 443 dns_cache_config: - name: dynamic_forward_proxy_cache_config + name: tls_dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY - name: envoy.tcp_proxy typed_config: @@ -77,14 +135,14 @@ static_resources: stat_prefix: tcp cluster: dynamic_forward_proxy_cluster_splicing - filter_chain_match: - server_names: ["www.google.com", "www.linkedin.com"] # bumping - filters: + transport_protocol: tls # bumping by default + filters: # bumping by default - name: envoy.filters.network.sni_dynamic_forward_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig port_value: 443 dns_cache_config: - name: dynamic_forward_proxy_cache_config + name: tls_dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY - name: envoy.filters.network.bumping typed_config: @@ -114,12 +172,11 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig dns_cache_config: - name: dynamic_forward_proxy_cache_config + name: tls_dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - transport_socket: name: envoy.transport_sockets.tls typed_config: @@ -127,18 +184,66 @@ static_resources: common_tls_context: tls_certificate_provider_instance: instance_name: "local_cert_provider" - certificate_name: "ALL_IDENTITY_CERTS" + + - name: http + internal_listener: {} + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: dynamic_forward_proxy_cluster_http + http_filters: + - name: envoy.filters.http.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig + dns_cache_config: + name: http_dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - - name: decap_cluster + - name: decap_cluster_http + load_assignment: + cluster_name: decap_cluster_http + endpoints: + - lb_endpoints: + - endpoint: + address: + envoy_internal_address: + server_listener_name: http + - name: dynamic_forward_proxy_cluster_http + connect_timeout: 1s + lb_policy: CLUSTER_PROVIDED + cluster_type: + name: envoy.clusters.dynamic_forward_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig + dns_cache_config: + name: http_dynamic_forward_proxy_cache_config + dns_lookup_family: V4_ONLY + - name: decap_cluster_tls load_assignment: - cluster_name: decap_cluster + cluster_name: decap_cluster_tls endpoints: - lb_endpoints: - endpoint: address: envoy_internal_address: - server_listener_name: splicing_and_bumping + server_listener_name: tls_splicing_and_bumping - name: dynamic_forward_proxy_cluster_splicing connect_timeout: 1s lb_policy: CLUSTER_PROVIDED @@ -147,7 +252,7 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig dns_cache_config: - name: dynamic_forward_proxy_cache_config + name: tls_dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY - name: dynamic_forward_proxy_cluster_bumping connect_timeout: 1s @@ -157,7 +262,7 @@ static_resources: typed_config: "@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig dns_cache_config: - name: dynamic_forward_proxy_cache_config + name: tls_dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY transport_socket: name: envoy.transport_sockets.tls @@ -235,4 +340,4 @@ certificate_provider_instances: ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU q5sGxGrC1RECGB5Zwx2S2ZY= -----END PRIVATE KEY----- - expiration_time: "2025-11-06T01:31:46Z" + expiration_time: "2022-10-30T08:42:46Z" # UTC time From 3fa175004f9440f5c6adf860d26aa4c1053d7ef8 Mon Sep 17 00:00:00 2001 From: lei zhang Date: Thu, 13 Oct 2022 20:40:16 +0800 Subject: [PATCH 11/35] Fix issues of subject and expiration time (#322) Fix issue of incorrectly copy cert subject to mimic cert. Use cert expiration time when expiration_time config is absent. Signed-off-by: LeiZhang Signed-off-by: lei zhang --- .../local_certificate/local_certificate.cc | 50 ++++++++++--------- .../local_certificate/local_certificate.h | 4 +- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 4b17b5e1dd..85f1fc5b50 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -24,8 +24,10 @@ Provider::Provider(const envoy::config::core::v3::TypedExtensionConfig& config, default_identity_cert_ = Config::DataSource::read(message.default_identity_cert(), true, api); default_identity_key_ = Config::DataSource::read(message.default_identity_key(), true, api); - auto seconds = google::protobuf::util::TimeUtil::TimestampToSeconds(message.expiration_time()); - expiration_config_ = std::chrono::system_clock::from_time_t(static_cast(seconds)); + if (message.has_expiration_time()) { + auto seconds = google::protobuf::util::TimeUtil::TimestampToSeconds(message.expiration_time()); + expiration_config_ = std::chrono::system_clock::from_time_t(static_cast(seconds)); + } // Generate TLSCertificate envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); @@ -176,11 +178,15 @@ void Provider::signCertificate(const std::string sni, // Compare expiration_time config with upstream cert expiration. Use smaller // value of those two dates as expiration time of mimic cert. auto now = std::chrono::system_clock::now(); - uint64_t valid_seconds = std::chrono::duration_cast(expiration_config_ - now).count(); - if (metadata->connectionInfo()->expirationPeerCertificate()) { - if (metadata->connectionInfo()->expirationPeerCertificate().value() <= expiration_config_) { + auto cert_expiration = metadata->connectionInfo()->expirationPeerCertificate(); + uint64_t valid_seconds; + if (expiration_config_) { + valid_seconds = std::chrono::duration_cast(expiration_config_.value() - now).count(); + } + if (cert_expiration) { + if (!expiration_config_ || cert_expiration.value() <= expiration_config_.value()) { valid_seconds = - std::chrono::duration_cast(metadata->connectionInfo()->expirationPeerCertificate().value() - now).count(); + std::chrono::duration_cast(cert_expiration.value() - now).count(); } } @@ -218,27 +224,25 @@ void Provider::signCertificate(const std::string sni, runOnDemandUpdateCallback(sni, thread_local_dispatcher, false); } -void Provider::setSubject(const std::string& subject, X509_NAME* x509_name) { - const std::string delim = ", "; +void Provider::setSubject(absl::string_view subject, X509_NAME* x509_name) { + // Parse the RFC 2253 format output of subjectPeerCertificate and set back to mimic cert. + const std::string delim = ","; std::string item; - size_t start = 0, end = subject.find(delim), pos = 0; - for ( ; end != std::string::npos; start = end + 2, end = subject.find(delim, start)) { - item = subject.substr(start, end - start); - if ((pos = item.find("=")) != std::string::npos) { - //X509_NAME_add_entry_by_txt(x509_name, "CN", MBSTRING_ASC, - // reinterpret_cast(szCommon), -1, -1, 0); - // reference: https://github.com/openssl/openssl/blob/master/test/v3nametest.c - X509_NAME_add_entry_by_txt(x509_name, item.substr(0, pos).c_str(), MBSTRING_ASC, - reinterpret_cast(item.substr(pos + 1).c_str()), + for (absl::string_view v: absl::StrSplit(subject, delim)) { + // This happens when peer subject from connectioninfo contains escaped comma, + // like O=Technology Co.\\, Ltd, have to remove the double backslash. + if (v.back() == '\\') { + absl::StrAppend(&item, v.substr(0, v.length() - 1), delim); + } + else { + absl::StrAppend(&item, v.substr(0, v.length())); + std::vector entries = absl::StrSplit(item, "="); + X509_NAME_add_entry_by_txt(x509_name, entries[0].c_str(), MBSTRING_ASC, + reinterpret_cast(entries[1].c_str()), -1, -1, 0); + item.clear(); } } - item = subject.substr(start, subject.length() - start); - if ((pos = item.find("=")) != std::string::npos) { - X509_NAME_add_entry_by_txt(x509_name, item.substr(0, pos).c_str(), MBSTRING_ASC, - reinterpret_cast(item.substr(pos + 1).c_str()), - -1, -1, 0); - } } } // namespace LocalCertificate } // namespace CertificateProviders diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index 7df5def7ff..1ecc851ab8 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -58,13 +58,13 @@ class Provider : public CertificateProvider::CertificateProvider, void signCertificate(const std::string sni, Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, Event::Dispatcher& thread_local_dispatcher); - void setSubject(const std::string& subject, X509_NAME* x509_name); + void setSubject(absl::string_view subject, X509_NAME* x509_name); Event::Dispatcher& main_thread_dispatcher_; std::string ca_cert_; std::string ca_key_; std::string default_identity_cert_; std::string default_identity_key_; - SystemTime expiration_config_; + absl::optional expiration_config_; Common::CallbackManager<> update_callback_manager_; absl::flat_hash_map> From de16d668fb397e7f22667bb01e85085b648ea852 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Thu, 13 Oct 2022 12:45:09 +0000 Subject: [PATCH 12/35] Update config to cover http2 test case Signed-off-by: Luyao Zhong --- tls_bumping/splicing_bumping_all.yaml | 31 +++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/tls_bumping/splicing_bumping_all.yaml b/tls_bumping/splicing_bumping_all.yaml index 1197605631..fa58471d3f 100644 --- a/tls_bumping/splicing_bumping_all.yaml +++ b/tls_bumping/splicing_bumping_all.yaml @@ -8,7 +8,7 @@ static_resources: address: socket_address: protocol: TCP - address: 127.0.0.1 + address: 0.0.0.0 port_value: 1234 listener_filters: - name: envoy.filters.listener.tls_inspector @@ -136,7 +136,7 @@ static_resources: cluster: dynamic_forward_proxy_cluster_splicing - filter_chain_match: transport_protocol: tls # bumping by default - filters: # bumping by default + filters: - name: envoy.filters.network.sni_dynamic_forward_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig @@ -235,6 +235,15 @@ static_resources: dns_cache_config: name: http_dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + upstream_http_protocol_options: + auto_sni: true + auto_san_validation: true + use_downstream_protocol_config: + http_protocol_options: + http2_protocol_options: - name: decap_cluster_tls load_assignment: cluster_name: decap_cluster_tls @@ -254,6 +263,15 @@ static_resources: dns_cache_config: name: tls_dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + upstream_http_protocol_options: + auto_sni: true + auto_san_validation: true + use_downstream_protocol_config: + http_protocol_options: + http2_protocol_options: - name: dynamic_forward_proxy_cluster_bumping connect_timeout: 1s lb_policy: CLUSTER_PROVIDED @@ -264,6 +282,15 @@ static_resources: dns_cache_config: name: tls_dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + upstream_http_protocol_options: + auto_sni: true + auto_san_validation: true + use_downstream_protocol_config: + http_protocol_options: + http2_protocol_options: transport_socket: name: envoy.transport_sockets.tls typed_config: From a588d0609d33f92f7a39c267fad79b147da68e1d Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 18 Oct 2022 08:07:32 +0000 Subject: [PATCH 13/35] skip duplicate certs check Current local cert provider might generates multiple certs with the same SANs/CN and pkey type which is not our expectation, we need to fix the bug in cert provider and then restore this check Signed-off-by: Luyao Zhong --- .../extensions/transport_sockets/tls/context_impl.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index af51b590bd..165fd5fc84 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -889,12 +889,13 @@ void ServerContextImpl::populateServerNamesMap(TlsContext& ctx, int pkey_id) { auto sn_match = server_names_map_.try_emplace(sn_pattern, pkey_types_map).first; auto pt_match = sn_match->second.find(pkey_id); if (pt_match != sn_match->second.end()) { - throw EnvoyException(fmt::format( - "Failed to load certificate chain from {}, at most one " - "certificate of a given type may be specified for each DNS SAN entry or Subject CN: {}", - ctx.cert_chain_file_path_, sn_match->first)); +// throw EnvoyException(fmt::format( +// "Failed to load certificate chain from {}, at most one " +// "certificate of a given type may be specified for each DNS SAN entry or Subject CN: {}", +// ctx.cert_chain_file_path_, sn_match->first)); + } else { + sn_match->second.emplace(std::pair>(pkey_id, ctx)); } - sn_match->second.emplace(std::pair>(pkey_id, ctx)); }; bssl::UniquePtr san_names(static_cast( From 553755f8a21f12ee9a9afe36e4a108999b5cb0a5 Mon Sep 17 00:00:00 2001 From: lei zhang Date: Tue, 18 Oct 2022 17:19:26 +0800 Subject: [PATCH 14/35] Fix doc (#325) Add more detail on how to import CA. Signed-off-by: LeiZhang Signed-off-by: lei zhang --- tls_bumping/README.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tls_bumping/README.rst b/tls_bumping/README.rst index 6cece3053e..a20be3e166 100644 --- a/tls_bumping/README.rst +++ b/tls_bumping/README.rst @@ -32,9 +32,10 @@ Prepare Test Environment #. Add a CA to Ubuntu:: - ubuntu@node1:~/envoy$ sudo cp root-ca.pem /etc/ssl/certs/ + ubuntu@node1:~/envoy$ sudo cp root-ca.pem /usr/local/share/ca-certificates/root-ca.crt + ubuntu@node1:~/envoy$ sudo update-ca-certificates - There are a root CA certificate and a root CA private key we have generated in advance under envoy directory, copy the .crt file into /etc/ssl/certs/. Envoy uses this CA cert/key to mimic server certificates, this makes the curl client trust the certs signed by the specified CA. + There are a root CA certificate and a root CA private key we have generated in advance under envoy directory. Envoy uses this CA cert/key to mimic server certificates, this makes the curl client trust the certs signed by the specified CA. #. Start Envoy listening at port 1234(The path of envoy executable file may vary depending on the way of building Envoy):: From 00a857a5215b02acb9f2bf14d435560deeda5fa9 Mon Sep 17 00:00:00 2001 From: lei zhang Date: Thu, 27 Oct 2022 14:04:19 +0800 Subject: [PATCH 15/35] Fix lacking sni of tls handshake in bumping filter (#329) Signed-off-by: LeiZhang Signed-off-by: lei zhang --- .../common/network/transport_socket_options_impl.cc | 13 ++++++++++--- .../common/network/transport_socket_options_impl.h | 2 +- .../extensions/filters/network/bumping/bumping.cc | 5 ++++- source/extensions/filters/network/bumping/bumping.h | 8 ++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/source/common/network/transport_socket_options_impl.cc b/source/common/network/transport_socket_options_impl.cc index 7af368b765..42921a9f90 100644 --- a/source/common/network/transport_socket_options_impl.cc +++ b/source/common/network/transport_socket_options_impl.cc @@ -54,7 +54,8 @@ void CommonUpstreamTransportSocketFactory::hashKey( } TransportSocketOptionsConstSharedPtr -TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& filter_state) { +TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& filter_state, + absl::string_view sni) { absl::string_view server_name; std::vector application_protocols; std::vector subject_alt_names; @@ -63,8 +64,14 @@ TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& fi std::unique_ptr proxy_info; bool needs_transport_socket_options = false; - if (auto typed_data = filter_state.getDataReadOnly(UpstreamServerName::key()); - typed_data != nullptr) { + + // sni parameter prevails over value from filterstate + if (sni.size()) { + server_name = sni; + needs_transport_socket_options = true; + } + else if (auto typed_data = filter_state.getDataReadOnly(UpstreamServerName::key()); + typed_data != nullptr) { server_name = typed_data->value(); needs_transport_socket_options = true; } diff --git a/source/common/network/transport_socket_options_impl.h b/source/common/network/transport_socket_options_impl.h index 2390abface..322c514d42 100644 --- a/source/common/network/transport_socket_options_impl.h +++ b/source/common/network/transport_socket_options_impl.h @@ -107,7 +107,7 @@ class TransportSocketOptionsUtility { * nullptr if nothing is in the filter state. */ static TransportSocketOptionsConstSharedPtr - fromFilterState(const StreamInfo::FilterState& stream_info); + fromFilterState(const StreamInfo::FilterState& stream_info, absl::string_view sni = absl::string_view("")); }; class CommonUpstreamTransportSocketFactory : public UpstreamTransportSocketFactory { diff --git a/source/extensions/filters/network/bumping/bumping.cc b/source/extensions/filters/network/bumping/bumping.cc index 3e0fbfe696..dd5ed04bbb 100644 --- a/source/extensions/filters/network/bumping/bumping.cc +++ b/source/extensions/filters/network/bumping/bumping.cc @@ -145,8 +145,11 @@ Network::FilterStatus Filter::establishUpstreamConnection() { auto& downstream_connection = read_callbacks_->connection(); auto& filter_state = downstream_connection.streamInfo().filterState(); + // Set transport socket SNI of upstream with downstream SNI transport_socket_options_ = - Network::TransportSocketOptionsUtility::fromFilterState(*filter_state); + Network::TransportSocketOptionsUtility::fromFilterState( + *filter_state, + downstream_connection.requestedServerName()); if (auto typed_state = filter_state->getDataReadOnly( Network::UpstreamSocketOptionsFilterState::key()); diff --git a/source/extensions/filters/network/bumping/bumping.h b/source/extensions/filters/network/bumping/bumping.h index 09b87f75ed..dbbe43c01f 100644 --- a/source/extensions/filters/network/bumping/bumping.h +++ b/source/extensions/filters/network/bumping/bumping.h @@ -166,6 +166,14 @@ class Filter : public Network::ReadFilter, return &read_callbacks_->connection(); } + Network::TransportSocketOptionsConstSharedPtr upstreamTransportSocketOptions() const override { + return transport_socket_options_; + } + + Network::Socket::OptionsSharedPtr upstreamSocketOptions() const override { + return upstream_options_; + } + // CertificateProvider::OnDemandUpdateCallbacks void onCacheHit(const std::string host) const override; void onCacheMiss(const std::string host) const override; From b3f0189ef0eba0377307db0fd88894d624b11d94 Mon Sep 17 00:00:00 2001 From: lei zhang Date: Tue, 22 Nov 2022 13:42:55 +0800 Subject: [PATCH 16/35] Refactor local certificate provider config (#337) Signed-off-by: LeiZhang Signed-off-by: lei zhang --- .../local_certificate/BUILD | 6 ++++-- .../local_certificate/config.cc | 14 +++++++++---- .../local_certificate/local_certificate.cc | 21 +++++++------------ .../local_certificate/local_certificate.h | 4 +++- tls_bumping/splicing_bumping_all.yaml | 2 +- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/source/extensions/certificate_providers/local_certificate/BUILD b/source/extensions/certificate_providers/local_certificate/BUILD index e19b545033..077366ebbe 100644 --- a/source/extensions/certificate_providers/local_certificate/BUILD +++ b/source/extensions/certificate_providers/local_certificate/BUILD @@ -20,9 +20,9 @@ envoy_cc_library( "//envoy/server:transport_socket_config_interface", "//source/common/common:callback_impl_lib", "//source/common/common:logger_lib", - "//source/common/common:utility_lib", + "//source/common/config:datasource_lib", - "//source/common/protobuf:message_validator_lib", + "@envoy_api//envoy/extensions/certificate_providers/local_certificate/v3:pkg_cc_proto", ], ) @@ -35,5 +35,7 @@ envoy_cc_extension( ":local_certificate_provider", "//envoy/certificate_provider:certificate_provider_factory_lib", "//envoy/certificate_provider:certificate_provider_interface", + "//source/common/common:utility_lib", + "//source/common/protobuf:message_validator_lib", ], ) diff --git a/source/extensions/certificate_providers/local_certificate/config.cc b/source/extensions/certificate_providers/local_certificate/config.cc index e0a4f200ce..8ea101f396 100644 --- a/source/extensions/certificate_providers/local_certificate/config.cc +++ b/source/extensions/certificate_providers/local_certificate/config.cc @@ -1,10 +1,12 @@ -#include "source/extensions/certificate_providers/local_certificate/config.h" - #include "envoy/certificate_provider/certificate_provider.h" +#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" + +#include "source/extensions/certificate_providers/local_certificate/config.h" #include "source/extensions/certificate_providers/local_certificate/local_certificate.h" -#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" +#include "source/common/config/utility.h" +#include "source/common/protobuf/message_validator_impl.h" namespace Envoy { namespace Extensions { @@ -14,7 +16,11 @@ namespace LocalCertificate { CertificateProvider::CertificateProviderSharedPtr LocalCertificateFactory::createCertificateProviderInstance( const envoy::config::core::v3::TypedExtensionConfig& config, Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) { - return std::make_shared(config, factory_context, api); + auto message = + std::make_unique(); + Config::Utility::translateOpaqueConfig(config.typed_config(), + ProtobufMessage::getStrictValidationVisitor(), *message); + return std::make_shared(*message, factory_context, api); } ProtobufTypes::MessagePtr LocalCertificateFactory::createEmptyConfigProto() { diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 85f1fc5b50..e6fd67e84f 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -4,28 +4,23 @@ #include "source/common/common/logger.h" #include "source/common/config/datasource.h" -#include "source/common/config/utility.h" -#include "source/common/protobuf/message_validator_impl.h" namespace Envoy { namespace Extensions { namespace CertificateProviders { namespace LocalCertificate { -Provider::Provider(const envoy::config::core::v3::TypedExtensionConfig& config, +Provider::Provider(const envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate& config, Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) : main_thread_dispatcher_(factory_context.mainThreadDispatcher()) { - envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate message; - Config::Utility::translateOpaqueConfig(config.typed_config(), - ProtobufMessage::getStrictValidationVisitor(), message); - ca_cert_ = Config::DataSource::read(message.rootca_cert(), true, api); - ca_key_ = Config::DataSource::read(message.rootca_key(), true, api); - default_identity_cert_ = Config::DataSource::read(message.default_identity_cert(), true, api); - default_identity_key_ = Config::DataSource::read(message.default_identity_key(), true, api); - - if (message.has_expiration_time()) { - auto seconds = google::protobuf::util::TimeUtil::TimestampToSeconds(message.expiration_time()); + ca_cert_ = Config::DataSource::read(config.rootca_cert(), true, api); + ca_key_ = Config::DataSource::read(config.rootca_key(), true, api); + default_identity_cert_ = Config::DataSource::read(config.default_identity_cert(), true, api); + default_identity_key_ = Config::DataSource::read(config.default_identity_key(), true, api); + + if (config.has_expiration_time()) { + auto seconds = google::protobuf::util::TimeUtil::TimestampToSeconds(config.expiration_time()); expiration_config_ = std::chrono::system_clock::from_time_t(static_cast(seconds)); } diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index 1ecc851ab8..3bd546e075 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -5,6 +5,8 @@ #include "envoy/event/dispatcher.h" #include "envoy/server/transport_socket_config.h" +#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" + #include "source/common/common/callback_impl.h" #include "source/common/common/logger.h" @@ -17,7 +19,7 @@ namespace LocalCertificate { class Provider : public CertificateProvider::CertificateProvider, Logger::Loggable { public: - Provider(const envoy::config::core::v3::TypedExtensionConfig& config, + Provider(const envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate& config, Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api); // CertificateProvider::CertificateProvider diff --git a/tls_bumping/splicing_bumping_all.yaml b/tls_bumping/splicing_bumping_all.yaml index fa58471d3f..0bfcc92b7a 100644 --- a/tls_bumping/splicing_bumping_all.yaml +++ b/tls_bumping/splicing_bumping_all.yaml @@ -367,4 +367,4 @@ certificate_provider_instances: ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU q5sGxGrC1RECGB5Zwx2S2ZY= -----END PRIVATE KEY----- - expiration_time: "2022-10-30T08:42:46Z" # UTC time + expiration_time: "2023-10-30T08:42:46Z" # UTC time From 108d4026c9ffea6c404e50643edf246724d0900e Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 22 Nov 2022 15:19:54 +0800 Subject: [PATCH 17/35] Add support for different pkey types and sizes (#342) Currently only RSA_2048, RSA_3072, RSA_4096, ECDSA_P256 is supported. If the pkey type is not specified in config file, original server pkey type will be copied. The default value is RSA_2048. Signed-off-by: Luyao Zhong --- .../v3/local_certificate.proto | 13 +- envoy/ssl/connection.h | 10 + .../local_certificate/local_certificate.cc | 220 ++++++++++++------ .../local_certificate/local_certificate.h | 14 +- .../tls/connection_info_impl_base.cc | 25 ++ .../tls/connection_info_impl_base.h | 2 + 6 files changed, 207 insertions(+), 77 deletions(-) diff --git a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto index 67bcab5286..93a41ec2e1 100644 --- a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto +++ b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto @@ -17,7 +17,16 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Local Certificate Provider] // [#extension: envoy.certificate_providers.local_certificate] +// [#next-free-field: 7] message LocalCertificate { + enum Pkey { + UNSPECIFIED = 0; + RSA_2048 = 1; + RSA_3072 = 2; + RSA_4096 = 3; + ECDSA_P256 = 4; + } + // Key and cert of root ca used to sign certificates. config.core.v3.DataSource rootca_cert = 1; @@ -28,6 +37,8 @@ message LocalCertificate { config.core.v3.DataSource default_identity_key = 4; - // Indicates the time at which the certificate expires. + // Indicates the time at which the certificate expires. google.protobuf.Timestamp expiration_time = 5; + + Pkey pkey = 6; } diff --git a/envoy/ssl/connection.h b/envoy/ssl/connection.h index 42d8cf3f29..2109798725 100644 --- a/envoy/ssl/connection.h +++ b/envoy/ssl/connection.h @@ -147,6 +147,16 @@ class ConnectionInfo { * @return std::string the SNI used to establish the connection. **/ virtual const std::string& sni() const PURE; + + /** + * @return pkey id, EVP_PKEY_EC or EVP_PKEY_RSA + */ + virtual int pkeyTypePeerCertificate() const PURE; + + /** + * @return EC curve name or RSA size + */ + virtual int pkeySizePeerCertificate() const PURE; }; using ConnectionInfoConstSharedPtr = std::shared_ptr; diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index e6fd67e84f..2de721e478 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -1,6 +1,6 @@ #include "source/extensions/certificate_providers/local_certificate/local_certificate.h" -#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" +#include #include "source/common/common/logger.h" #include "source/common/config/datasource.h" @@ -18,6 +18,7 @@ Provider::Provider(const envoy::extensions::certificate_providers::local_certifi ca_key_ = Config::DataSource::read(config.rootca_key(), true, api); default_identity_cert_ = Config::DataSource::read(config.default_identity_cert(), true, api); default_identity_key_ = Config::DataSource::read(config.default_identity_key(), true, api); + pkey_ = config.pkey(); if (config.has_expiration_time()) { auto seconds = google::protobuf::util::TimeUtil::TimestampToSeconds(config.expiration_time()); @@ -108,8 +109,6 @@ void Provider::runOnDemandUpdateCallback(const std::string& host, } } -// TODO: we meed to copy more information of original cert, such as SANs, the whole subject, -// expiration time, etc. void Provider::signCertificate(const std::string sni, ::Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, Event::Dispatcher& thread_local_dispatcher) { @@ -124,71 +123,21 @@ void Provider::signCertificate(const std::string sni, ca_key.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); /********* generate identity certificate locally *****/ - EVP_PKEY* key = EVP_PKEY_new(); - X509* crt = X509_new(); - + // create a new CSR X509_REQ* req = X509_REQ_new(); - BIGNUM* bne = BN_new(); - BN_set_word(bne, RSA_F4); - RSA* rsa = RSA_new(); - RSA_generate_key_ex(rsa, 2048, bne, nullptr); X509_REQ_set_version(req, 0); - - X509_NAME* x509_name = X509_REQ_get_subject_name(req); - setSubject(metadata->connectionInfo()->subjectPeerCertificate().data(), x509_name); - X509_set_subject_name(crt, x509_name); - - EVP_PKEY_assign_RSA(key, rsa); - X509_REQ_set_pubkey(req, key); - X509_REQ_sign(req, key, EVP_sha1()); // return x509_req->signature->length - - X509_set_version(crt, 2); - auto gens = sk_GENERAL_NAME_new_null(); - - for (auto& dns : metadata->connectionInfo()->dnsSansPeerCertificate()) { - auto ia5 = ASN1_IA5STRING_new(); - ASN1_STRING_set(ia5, dns.c_str(), -1); - auto gen = GENERAL_NAME_new(); - GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); - sk_GENERAL_NAME_push(gens, gen); - } - - for (auto& uri : metadata->connectionInfo()->uriSanPeerCertificate()) { - auto ia5 = ASN1_IA5STRING_new(); - ASN1_STRING_set(ia5, uri.c_str(), -1); - auto gen = GENERAL_NAME_new(); - GENERAL_NAME_set0_value(gen, GEN_URI, ia5); - sk_GENERAL_NAME_push(gens, gen); - } - - X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, 0); - //X509_EXTENSION* ext = - // X509V3_EXT_nconf_nid(nullptr, nullptr, NID_subject_alt_name, subAltName.c_str()); - //X509_add_ext(crt, ext, -1); - + setSubjectToCSR(metadata->connectionInfo()->subjectPeerCertificate().data(), req); + // creates a new, empty public-key object + EVP_PKEY* key = EVP_PKEY_new(); + setPkeyToCSR(metadata, key, req); + // create a new certificate, + X509* crt = X509_new(); + X509_set_version(crt, X509_VERSION_3); X509_set_issuer_name(crt, X509_get_subject_name(ca_cert.get())); - X509_gmtime_adj(X509_get_notBefore(crt), 0); - - - // Compare expiration_time config with upstream cert expiration. Use smaller - // value of those two dates as expiration time of mimic cert. - auto now = std::chrono::system_clock::now(); - auto cert_expiration = metadata->connectionInfo()->expirationPeerCertificate(); - uint64_t valid_seconds; - if (expiration_config_) { - valid_seconds = std::chrono::duration_cast(expiration_config_.value() - now).count(); - } - if (cert_expiration) { - if (!expiration_config_ || cert_expiration.value() <= expiration_config_.value()) { - valid_seconds = - std::chrono::duration_cast(cert_expiration.value() - now).count(); - } - } - - X509_gmtime_adj(X509_get_notAfter(crt), valid_seconds); X509_set_subject_name(crt, X509_REQ_get_subject_name(req)); - EVP_PKEY* req_pubkey = X509_REQ_get_pubkey(req); - X509_set_pubkey(crt, req_pubkey); + X509_set_pubkey(crt, X509_REQ_get_pubkey(req)); + setSANs(metadata, crt); + setExpirationTime(metadata, crt); X509_sign(crt, ca_key.get(), EVP_sha256()); /********* generate identity certificate locally *****/ @@ -204,40 +153,165 @@ void Provider::signCertificate(const std::string sni, std::string key_pem(reinterpret_cast(output), length); // Generate TLSCertificate - envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = + new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); tls_certificate->mutable_certificate_chain()->set_inline_string(cert_pem); tls_certificate->mutable_private_key()->set_inline_string(key_pem); // Update certificates_ map { absl::WriterMutexLock writer_lock{&certificates_lock_}; - certificates_.try_emplace( - sni, tls_certificate); + certificates_.try_emplace(sni, tls_certificate); } runAddUpdateCallback(); runOnDemandUpdateCallback(sni, thread_local_dispatcher, false); } -void Provider::setSubject(absl::string_view subject, X509_NAME* x509_name) { +void Provider::setSubjectToCSR(absl::string_view subject, X509_REQ* req) { + X509_NAME* x509_name = X509_NAME_new(); // Parse the RFC 2253 format output of subjectPeerCertificate and set back to mimic cert. const std::string delim = ","; std::string item; - for (absl::string_view v: absl::StrSplit(subject, delim)) { + for (absl::string_view v : absl::StrSplit(subject, delim)) { // This happens when peer subject from connectioninfo contains escaped comma, // like O=Technology Co.\\, Ltd, have to remove the double backslash. if (v.back() == '\\') { absl::StrAppend(&item, v.substr(0, v.length() - 1), delim); - } - else { + } else { absl::StrAppend(&item, v.substr(0, v.length())); std::vector entries = absl::StrSplit(item, "="); X509_NAME_add_entry_by_txt(x509_name, entries[0].c_str(), MBSTRING_ASC, - reinterpret_cast(entries[1].c_str()), - -1, -1, 0); + reinterpret_cast(entries[1].c_str()), -1, -1, + 0); item.clear(); } } + X509_REQ_set_subject_name(req, x509_name); +} + +void Provider::setPkeyToCSR(Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + EVP_PKEY* key, X509_REQ* req) { + auto pkey_rsa = [&](const int size) { + // BN provides support for working with arbitrary sized integers + BIGNUM* bne = BN_new(); + BN_set_word(bne, RSA_F4); + RSA* rsa = RSA_new(); + // generates a new RSA key where the modulus has size |bits|=2048 and the public exponent is + // |e|=bne + RSA_generate_key_ex(rsa, size, bne, nullptr); + + // set the underlying public key in an |EVP_PKEY| object + EVP_PKEY_assign_RSA(key, rsa); + ENVOY_LOG(debug, "Generating RSA key"); + }; + + auto pkey_ecdsa = [&]() { + EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + EC_KEY_generate_key(ec_key); + EVP_PKEY_assign_EC_KEY(key, ec_key); + ENVOY_LOG(debug, "Generating ECDSA key"); + }; + + auto pkey = pkey_; + if (pkey == envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_UNSPECIFIED) { + if (metadata->connectionInfo()->pkeyTypePeerCertificate() == EVP_PKEY_RSA) { + switch (auto size = metadata->connectionInfo()->pkeySizePeerCertificate()) { + case 2048: + pkey = envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_RSA_2048; + break; + case 3072: + pkey = envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_RSA_3072; + break; + case 4096: + pkey = envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_RSA_4096; + break; + default: + ENVOY_LOG(debug, "Not supported PKEY RSA size '{}'", size); + } + } + if (metadata->connectionInfo()->pkeyTypePeerCertificate() == EVP_PKEY_EC) { + switch (auto size = metadata->connectionInfo()->pkeySizePeerCertificate()) { + case NID_X9_62_prime256v1: + pkey = envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_ECDSA_P256; + break; + default: + ENVOY_LOG(debug, "Not supported PKEY ECDSA nid '{}'", size); + } + } + } + + switch (pkey) { + case envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_RSA_2048: + pkey_rsa(2048); + break; + case envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_RSA_3072: + pkey_rsa(3072); + break; + case envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_RSA_4096: + pkey_rsa(4096); + break; + case envoy::extensions::certificate_providers::local_certificate::v3:: + LocalCertificate_Pkey_ECDSA_P256: + pkey_ecdsa(); + break; + default: + pkey_rsa(2048); + break; + } + + X509_REQ_set_pubkey(req, key); + // signs |req| with |pkey| and replaces the signature algorithm and signature fields + X509_REQ_sign(req, key, EVP_sha256()); // return x509_req->signature->length +} + +void Provider::setExpirationTime(Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + X509* crt) { + X509_gmtime_adj(X509_get_notBefore(crt), 0); + // Compare expiration_time config with upstream cert expiration. Use smaller + // value of those two dates as expiration time of mimic cert. + auto now = std::chrono::system_clock::now(); + auto cert_expiration = metadata->connectionInfo()->expirationPeerCertificate(); + uint64_t valid_seconds; + if (expiration_config_) { + valid_seconds = + std::chrono::duration_cast(expiration_config_.value() - now).count(); + } + if (cert_expiration) { + if (!expiration_config_ || cert_expiration.value() <= expiration_config_.value()) { + valid_seconds = + std::chrono::duration_cast(cert_expiration.value() - now).count(); + } + } + X509_gmtime_adj(X509_get_notAfter(crt), valid_seconds); +} + +void Provider::setSANs(Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, X509* crt) { + auto gens = sk_GENERAL_NAME_new_null(); + for (auto& dns : metadata->connectionInfo()->dnsSansPeerCertificate()) { + auto ia5 = ASN1_IA5STRING_new(); + ASN1_STRING_set(ia5, dns.c_str(), -1); + auto gen = GENERAL_NAME_new(); + GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); + sk_GENERAL_NAME_push(gens, gen); + } + + for (auto& uri : metadata->connectionInfo()->uriSanPeerCertificate()) { + auto ia5 = ASN1_IA5STRING_new(); + ASN1_STRING_set(ia5, uri.c_str(), -1); + auto gen = GENERAL_NAME_new(); + GENERAL_NAME_set0_value(gen, GEN_URI, ia5); + sk_GENERAL_NAME_push(gens, gen); + } + X509_add1_ext_i2d(crt, NID_subject_alt_name, gens, 0, 0); } } // namespace LocalCertificate } // namespace CertificateProviders diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index 3bd546e075..3d9743dc46 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -3,6 +3,7 @@ #include "envoy/certificate_provider/certificate_provider.h" #include "envoy/common/callback.h" #include "envoy/event/dispatcher.h" +#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" #include "envoy/server/transport_socket_config.h" #include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" @@ -55,18 +56,25 @@ class Provider : public CertificateProvider::CertificateProvider, void runAddUpdateCallback(); void runOnDemandUpdateCallback(const std::string& host, Event::Dispatcher& thread_local_dispatcher, bool in_cache = true); - //void signCertificate(std::string sni, absl::Span dns_sans, const std::string subject, - // Event::Dispatcher& thread_local_dispatcher); + // void signCertificate(std::string sni, absl::Span dns_sans, const std::string + // subject, + // Event::Dispatcher& thread_local_dispatcher); void signCertificate(const std::string sni, Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, Event::Dispatcher& thread_local_dispatcher); - void setSubject(absl::string_view subject, X509_NAME* x509_name); + + void setSubjectToCSR(absl::string_view subject, X509_REQ* req); + void setPkeyToCSR(Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, EVP_PKEY* key, + X509_REQ* req); + void setExpirationTime(Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, X509* crt); + void setSANs(Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, X509* crt); Event::Dispatcher& main_thread_dispatcher_; std::string ca_cert_; std::string ca_key_; std::string default_identity_cert_; std::string default_identity_key_; absl::optional expiration_config_; + envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate_Pkey pkey_; Common::CallbackManager<> update_callback_manager_; absl::flat_hash_map> diff --git a/source/extensions/transport_sockets/tls/connection_info_impl_base.cc b/source/extensions/transport_sockets/tls/connection_info_impl_base.cc index 8c1ea7b697..c93f71c20e 100644 --- a/source/extensions/transport_sockets/tls/connection_info_impl_base.cc +++ b/source/extensions/transport_sockets/tls/connection_info_impl_base.cc @@ -1,5 +1,7 @@ #include "source/extensions/transport_sockets/tls/connection_info_impl_base.h" +#include + #include "source/common/common/hex.h" #include "absl/strings/str_replace.h" @@ -296,6 +298,29 @@ const std::string& ConnectionInfoImplBase::sessionId() const { return cached_session_id_; } +int ConnectionInfoImplBase::pkeyTypePeerCertificate() const { + bssl::UniquePtr cert(SSL_get_peer_certificate(ssl())); + bssl::UniquePtr public_key(X509_get_pubkey(cert.get())); + const int pkey_id = EVP_PKEY_id(public_key.get()); + return pkey_id; +} + +int ConnectionInfoImplBase::pkeySizePeerCertificate() const { + bssl::UniquePtr cert(SSL_get_peer_certificate(ssl())); + bssl::UniquePtr public_key(X509_get_pubkey(cert.get())); + const int pkey_id = EVP_PKEY_id(public_key.get()); + switch (pkey_id) { + case EVP_PKEY_EC: { + const EC_GROUP* ecdsa_group = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(public_key.get())); + return EC_GROUP_get_curve_name(ecdsa_group); + } break; + case EVP_PKEY_RSA: { + return RSA_bits(EVP_PKEY_get0_RSA(public_key.get())); + } break; + } + return -1; +} + } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/source/extensions/transport_sockets/tls/connection_info_impl_base.h b/source/extensions/transport_sockets/tls/connection_info_impl_base.h index 8334ee174a..65b998c826 100644 --- a/source/extensions/transport_sockets/tls/connection_info_impl_base.h +++ b/source/extensions/transport_sockets/tls/connection_info_impl_base.h @@ -40,6 +40,8 @@ class ConnectionInfoImplBase : public Ssl::ConnectionInfo { const std::string& tlsVersion() const override; const std::string& alpn() const override; const std::string& sni() const override; + int pkeyTypePeerCertificate() const override; + int pkeySizePeerCertificate() const override; virtual SSL* ssl() const PURE; From b3c56d3b2eccfccf03387e02eb8d553760f82291 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Thu, 1 Dec 2022 07:42:56 +0000 Subject: [PATCH 18/35] tls: SNI-based cert selection during TLS handshake ** Align with upstream ** 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 --- .../transport_sockets/tls/v3/tls.proto | 7 +- changelogs/current.yaml | 9 +- .../root/intro/arch_overview/security/ssl.rst | 55 ++++-- envoy/ssl/context_config.h | 6 + source/common/runtime/runtime_features.cc | 1 + .../tls/context_config_impl.cc | 6 +- .../tls/context_config_impl.h | 3 + .../transport_sockets/tls/context_impl.cc | 130 ++++++++----- .../transport_sockets/tls/context_impl.h | 2 + .../tls/context_impl_test.cc | 41 ++++- .../transport_sockets/tls/ssl_socket_test.cc | 172 +++++++++++++++--- .../transport_sockets/tls/test_data/certs.sh | 8 + .../tls/test_data/no_san_cn_cert.cfg | 31 ++++ .../tls/test_data/no_san_cn_cert.pem | 24 +++ .../tls/test_data/no_san_cn_cert_info.h | 8 + .../tls/test_data/no_san_cn_key.pem | 27 +++ .../tls/test_data/san_multiple_dns_1_cert.cfg | 4 +- .../tls/test_data/san_multiple_dns_1_cert.pem | 36 ++-- .../test_data/san_multiple_dns_1_cert_info.h | 12 +- .../tls/test_data/san_multiple_dns_1_key.pem | 50 ++--- .../tls/test_data/san_wildcard_dns_cert.cfg | 36 ++++ .../tls/test_data/san_wildcard_dns_cert.pem | 25 +++ .../test_data/san_wildcard_dns_cert_info.h | 8 + .../tls/test_data/san_wildcard_dns_key.pem | 27 +++ test/mocks/ssl/mocks.h | 1 + 25 files changed, 564 insertions(+), 165 deletions(-) create mode 100644 test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.cfg create mode 100644 test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/no_san_cn_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/no_san_cn_key.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.cfg create mode 100644 test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.pem create mode 100644 test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert_info.h create mode 100644 test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_key.pem diff --git a/api/envoy/extensions/transport_sockets/tls/v3/tls.proto b/api/envoy/extensions/transport_sockets/tls/v3/tls.proto index a908a2065e..e69cd4e1fb 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/tls.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/tls.proto @@ -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"; @@ -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 `. + google.protobuf.BoolValue full_scan_certs_on_sni_mismatch = 9; } // TLS key log configuration. diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 237cd15724..e8eb387a1a 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -13,6 +13,12 @@ removed_config_or_runtime: # *Normally occurs at the end of the* :ref:`deprecation period ` new_features: +- area: tls + change: | + added support for SNI-based cert selection in tls downstream transport socket. Detailed documentation is available :ref:`cert selection`. + New config option :ref:`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: header_formatters change: | all access log formatters can be used as custom request/response headers. Custom header's syntax is parsed using access logger's parser and @@ -118,8 +124,5 @@ new_features: - area: generic_proxy change: | added an new network filter :ref:`generic_proxy filter `. -- area: tls - change: | - added support for SNI-based cert selection in tls downstream transport socket. Detailed documentation is available :ref:`cert selection`. deprecated: diff --git a/docs/root/intro/arch_overview/security/ssl.rst b/docs/root/intro/arch_overview/security/ssl.rst index 4fe028cbc6..982abf648f 100644 --- a/docs/root/intro/arch_overview/security/ssl.rst +++ b/docs/root/intro/arch_overview/security/ssl.rst @@ -114,35 +114,50 @@ Certificate selection --------------------- :ref:`DownstreamTlsContexts ` support multiple TLS -certificates. These may be a mix of RSA and P-256 ECDSA certificates for multiple SANs. +certificates. These may be a mix of RSA and P-256 ECDSA certificates for multiple server name patterns. Certificate config/loading rules: -* DNS SANs or Subject Common Name is extracted as server name pattern to match SNI during handshake. +* 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 pattern. + 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. * Static and SDS certificates may not be mixed in a given :ref:`DownstreamTlsContext `. -Certificate selection rules, including SNI matching, Public Key Type matching and OCSP: - -* If the client supports SNI, a certificate with proper DNS SANs or Subject Common Name should be selected -* It matches on exact server name first, then matches on wildcard server name if an exact name match isn't found, e.g. if SNI is - "test.example.com", a certificate with "test.example.com" will be preferred over for "\*.example.com". -* If no certificate is matched to SNI or the client does not support SNI, subsequent particular type (RSA or ECDSA) - matching will be executed with all certificates as candidates. -* Otherwise, particular type (RSA or ECDSA) matching will be executed with SNI-matched certificates as candidates. -* Key type matching is based on SNI matching results -* If the client supports P-256 ECDSA, the first P-256 ECDSA certificate is selected if it is present and the OCSP check is passed. -* If the client only supports RSA, the first RSA certificate is selected if it is present. -* If no exact match, fallback to the first certificate in the candidates. -* The certificate that it fallbacks to might result in a failed handshake. For instance, a client only supports - RSA certificates and the certificate only support ECDSA, or a client only supports ECDSA certificate and the - certificate only support RSA. -* After key type matching, one certificate is selected, and 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 `, + 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 ` + 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 ` + 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 ` + 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 `. + 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 `. diff --git a/envoy/ssl/context_config.h b/envoy/ssl/context_config.h index b25e9a3b18..0a0be8091e 100644 --- a/envoy/ssl/context_config.h +++ b/envoy/ssl/context_config.h @@ -189,6 +189,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; diff --git a/source/common/runtime/runtime_features.cc b/source/common/runtime/runtime_features.cc index 6827574efe..ff4179f13f 100644 --- a/source/common/runtime/runtime_features.cc +++ b/source/common/runtime/runtime_features.cc @@ -63,6 +63,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_original_dst_rely_on_idle_timeout); RUNTIME_GUARD(envoy_reloadable_features_override_request_timeout_by_gateway_timeout); RUNTIME_GUARD(envoy_reloadable_features_postpone_h3_client_connect_to_next_loop); diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index 9619bb1158..fe5e176c6e 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -424,7 +424,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. diff --git a/source/extensions/transport_sockets/tls/context_config_impl.h b/source/extensions/transport_sockets/tls/context_config_impl.h index 6078e84734..b434163b79 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.h +++ b/source/extensions/transport_sockets/tls/context_config_impl.h @@ -175,6 +175,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; @@ -197,6 +199,7 @@ class ServerContextConfigImpl : public ContextConfigImpl, public Envoy::Ssl::Ser absl::optional session_timeout_; const bool disable_stateless_session_resumption_; + bool full_scan_certs_on_sni_mismatch_; }; } // namespace Tls diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 165fd5fc84..7f26f4cd7c 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -779,7 +779,8 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, const std::vector& server_names, TimeSource& time_source) : ContextImpl(scope, config, time_source), session_ticket_keys_(config.sessionTicketKeys()), - ocsp_staple_policy_(config.ocspStaplePolicy()) { + ocsp_staple_policy_(config.ocspStaplePolicy()), has_rsa_(false), + full_scan_certs_on_sni_mismatch_(config.fullScanCertsOnSNIMismatch()) { if (config.tlsCertificates().empty() && !config.capabilities().provides_certificates && !config.certProviderCaps().provide_on_demand_identity_certs) { throw EnvoyException("Server TlsCertificates must have a certificate specified"); @@ -790,6 +791,7 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, const int pkey_id = EVP_PKEY_id(public_key.get()); // Load DNS SAN entries and Subject Common Name as server name patterns after certificate // chain loaded, and populate ServerNamesMap which will be used to match SNI. + has_rsa_ |= (pkey_id == EVP_PKEY_RSA); populateServerNamesMap(ctx, pkey_id); } @@ -886,6 +888,7 @@ void ServerContextImpl::populateServerNamesMap(TlsContext& ctx, int pkey_id) { sn_pattern = sn.substr(1); } PkeyTypesMap pkey_types_map; + // Multiple certs with different key type are allowed for one server name pattern. auto sn_match = server_names_map_.try_emplace(sn_pattern, pkey_types_map).first; auto pt_match = sn_match->second.find(pkey_id); if (pt_match != sn_match->second.end()) { @@ -893,27 +896,26 @@ void ServerContextImpl::populateServerNamesMap(TlsContext& ctx, int pkey_id) { // "Failed to load certificate chain from {}, at most one " // "certificate of a given type may be specified for each DNS SAN entry or Subject CN: {}", // ctx.cert_chain_file_path_, sn_match->first)); - } else { - sn_match->second.emplace(std::pair>(pkey_id, ctx)); } + sn_match->second.emplace(std::pair>(pkey_id, ctx)); }; bssl::UniquePtr san_names(static_cast( X509_get_ext_d2i(ctx.cert_chain_.get(), NID_subject_alt_name, nullptr, nullptr))); - auto dns_sans = Utility::getSubjectAltNames(*ctx.cert_chain_, GEN_DNS); - // https://www.rfc-editor.org/rfc/rfc6066#section-3 - // Currently, the only server names supported are DNS hostnames, so we - // only save dns san entries to match SNI. - for (const auto& san : dns_sans) { - populate(san); - } - - // https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 - // As noted, a client MUST NOT seek a match for a reference identifier - // of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, - // URI-ID, or any application-specific identifier types supported by the - // client. - if (san_names.get() == nullptr) { + if (san_names != nullptr) { + auto dns_sans = Utility::getSubjectAltNames(*ctx.cert_chain_, GEN_DNS); + // https://www.rfc-editor.org/rfc/rfc6066#section-3 + // Currently, the only server names supported are DNS hostnames, so we + // only save dns san entries to match SNI. + for (const auto& san : dns_sans) { + populate(san); + } + } else { + // https://www.rfc-editor.org/rfc/rfc6125#section-6.4.4 + // As noted, a client MUST NOT seek a match for a reference identifier + // of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, + // URI-ID, or any application-specific identifier types supported by the + // client. X509_NAME* cert_subject = X509_get_subject_name(ctx.cert_chain_.get()); const int cn_index = X509_NAME_get_index_by_NID(cert_subject, NID_commonName, -1); if (cn_index >= 0) { @@ -921,9 +923,8 @@ void ServerContextImpl::populateServerNamesMap(TlsContext& ctx, int pkey_id) { if (cn_entry) { ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry); if (ASN1_STRING_length(cn_asn1) > 0) { - std::string subject_cn; - subject_cn.assign(reinterpret_cast(ASN1_STRING_data(cn_asn1)), - ASN1_STRING_length(cn_asn1)); + std::string subject_cn(reinterpret_cast(ASN1_STRING_data(cn_asn1)), + ASN1_STRING_length(cn_asn1)); populate(subject_cn); } } @@ -1225,60 +1226,91 @@ ServerContextImpl::selectTlsContext(const SSL_CLIENT_HELLO* ssl_client_hello) { absl::string_view sni = absl::NullSafeStringView( SSL_get_servername(ssl_client_hello->ssl, TLSEXT_NAMETYPE_host_name)); + // selected_ctx represents the final selected certificate, it should meet all requirements or pick + // a candidate const TlsContext* selected_ctx = nullptr; + const TlsContext* candicate_ctx = nullptr; OcspStapleAction ocsp_staple_action; auto selected = [&](const TlsContext& ctx) -> bool { - if (client_ecdsa_capable != ctx.is_ecdsa_) { + auto action = ocspStapleAction(ctx, client_ocsp_capable); + if (action == OcspStapleAction::Fail) { + // The selected ctx must adhere to OCSP policy return false; } - auto action = ocspStapleAction(ctx, client_ocsp_capable); - if (action == OcspStapleAction::Fail) { + if (client_ecdsa_capable == ctx.is_ecdsa_) { + selected_ctx = &ctx; + ocsp_staple_action = action; + return true; + } + + if (client_ecdsa_capable && !ctx.is_ecdsa_ && candicate_ctx == nullptr) { + // ECDSA cert is preferred if client is ECDSA capable, so RSA cert is marked as a candidate, + // searching will continue until exhausting all certs or find a exact match. + candicate_ctx = &ctx; + ocsp_staple_action = action; return false; } - selected_ctx = &ctx; - ocsp_staple_action = action; - return true; + return false; + }; + + auto select_from_map = [this, &selected](absl::string_view server_name) -> void { + auto it = server_names_map_.find(server_name); + if (it == server_names_map_.end()) { + return; + } + const auto& pkey_types_map = it->second; + for (const auto& entry : pkey_types_map) { + if (selected(entry.second.get())) { + break; + } + } }; - // Do SNI matching and pkey type matching if SNI exists. + auto tail_select = [&](bool go_to_next_phase) { + if (selected_ctx == nullptr) { + selected_ctx = candicate_ctx; + } + + if (selected_ctx == nullptr && !go_to_next_phase) { + selected_ctx = &tls_contexts_[0]; + ocsp_staple_action = ocspStapleAction(*selected_ctx, client_ocsp_capable); + } + }; + + // Select cert based on SNI if SNI is provided by client. if (!sni.empty()) { // Match on exact server name, i.e. "www.example.com" for "www.example.com". - auto server_name_it = server_names_map_.find(sni); - if (server_name_it == server_names_map_.end()) { + select_from_map(sni); + tail_select(true); + + if (selected_ctx == nullptr) { // Match on wildcard domain, i.e. ".example.com" for "www.example.com". // https://datatracker.ietf.org/doc/html/rfc6125#section-6.4 size_t pos = sni.find('.', 1); if (pos < sni.size() - 1 && pos != std::string::npos) { absl::string_view wildcard = sni.substr(pos); - server_name_it = server_names_map_.find(static_cast(wildcard)); - } - } - - if (server_name_it != server_names_map_.end()) { - const auto& pkey_types_map = server_name_it->second; - // Fallback on first SNI-matched certificate. - selected_ctx = &pkey_types_map.begin()->second.get(); - ocsp_staple_action = ocspStapleAction(*selected_ctx, client_ocsp_capable); - for (const auto& entry : pkey_types_map) { - if (selected(entry.second.get())) { - break; - } + select_from_map(wildcard); } } + tail_select(full_scan_certs_on_sni_mismatch_); } - // Do pkey type matching if SNI does Not exist or no ctx matched for SNI. + // Full scan certs if SNI is not provided by client; + // Full scan certs if client provides SNI but no cert matches to it, + // it requires full_scan_certs_on_sni_mismatch is enabled. if (selected_ctx == nullptr) { - // Fallback on first certificate. - selected_ctx = &tls_contexts_[0]; - ocsp_staple_action = ocspStapleAction(*selected_ctx, client_ocsp_capable); - for (const auto& ctx : tls_contexts_) { - if (selected(ctx)) { - break; + candicate_ctx = nullptr; + // Skip loop when there is no cert compatible to key type + if (client_ecdsa_capable || (!client_ecdsa_capable && has_rsa_)) { + for (const auto& ctx : tls_contexts_) { + if (selected(ctx)) { + break; + } } } + tail_select(false); } // Apply the selected context. This must be done before OCSP stapling below diff --git a/source/extensions/transport_sockets/tls/context_impl.h b/source/extensions/transport_sockets/tls/context_impl.h index ef624e1f9b..fdf72cc1c2 100644 --- a/source/extensions/transport_sockets/tls/context_impl.h +++ b/source/extensions/transport_sockets/tls/context_impl.h @@ -224,6 +224,8 @@ class ServerContextImpl : public ContextImpl, public Envoy::Ssl::ServerContext { const std::vector session_ticket_keys_; const Ssl::ServerContextConfig::OcspStaplePolicy ocsp_staple_policy_; ServerNamesMap server_names_map_; + bool has_rsa_; + bool full_scan_certs_on_sni_mismatch_; }; } // namespace Tls diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 7d5c540a42..6cb75dd650 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -500,7 +500,8 @@ TEST_F(SslContextImplTest, AtMostOneRsaCertSameWildcardDNSSan) { "SAN entry or Subject CN"); } -// Multiple RSA certificates with different exact DNS SAN are acceptable +// Multiple RSA certificates with different exact DNS SAN are acceptable. +>>>>>>> 9653024634... tls: SNI-based cert selection during TLS handshake (#22036) TEST_F(SslContextImplTest, AcceptableMultipleRsaCerts) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( @@ -543,7 +544,7 @@ TEST_F(SslContextImplTest, AtMostOneEcdsaCert) { "SAN entry or Subject CN"); } -// Multiple ECDSA certificates with different exact DNS SAN are acceptable +// Multiple ECDSA certificates with different exact DNS SAN are acceptable. TEST_F(SslContextImplTest, AcceptableMultipleEcdsaCerts) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( @@ -563,19 +564,15 @@ TEST_F(SslContextImplTest, AcceptableMultipleEcdsaCerts) { EXPECT_NO_THROW(loadConfig(server_context_config)); } -// CN is not used if SANs are present -TEST_F(SslContextImplTest, CertSansAndCN) { +// One cert which contains one of the SAN values in the CN is acceptable, because CN is not used if +// SANs are present. +TEST_F(SslContextImplTest, CertDuplicatedSansAndCN) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; - // no_san_cn_2_cert's CN: server2.example.com - // san_multiple_dns_1_cert's CN: server2.example.com + // san_multiple_dns_1_cert's CN: server1.example.com // san_multiple_dns_1_cert's SAN: DNS.1 = *.example.com DNS.2 = server1.example.com const std::string tls_context_yaml = R"EOF( common_tls_context: tls_certificates: - - certificate_chain: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_cert.pem" - private_key: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_cn_2_key.pem" - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem" private_key: @@ -586,6 +583,30 @@ TEST_F(SslContextImplTest, CertSansAndCN) { EXPECT_NO_THROW(loadConfig(server_context_config)); } +// Multiple certificates with duplicated CN is acceptable, because CN is not used if SANs are +// present. +TEST_F(SslContextImplTest, MultipleCertsSansAndCN) { + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; + // no_san_cn_cert's CN: server1.example.com + // san_wildcard_dns_cert's CN: server1.example.com + // san_wildcard_dns_cert's SAN: DNS.1 = *.example.com + const std::string tls_context_yaml = R"EOF( + common_tls_context: + tls_certificates: + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_cn_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_key.pem" + )EOF"; + TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); + ServerContextConfigImpl server_context_config(tls_context, factory_context_); + EXPECT_NO_THROW(loadConfig(server_context_config)); +} + // Certificates with no subject CN and no SANs are rejected. TEST_F(SslContextImplTest, MustHaveSubjectOrSAN) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index c3e28a9737..13841dcd93 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -1305,9 +1305,109 @@ TEST_P(SslSocketTest, MultiCertPreferEcdsaWithoutSni) { testUtil(test_options); } -// When client supports SNI and is ecdsa capable, the ECDSA certificate -// that match SNI will be selected. -TEST_P(SslSocketTest, MultiCertPreferECDSAWithSni) { +// When client supports SNI, exact match is preferred over wildcard match. +TEST_P(SslSocketTest, MultiCertPreferExactSniMatch) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "server1.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SAN_DNS_RSA_1_CERT_256_HASH); + 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + testUtil(test_options.setExpectedSni("server1.example.com")); +} + +// When client supports SNI and there is no exact match, validate that wildcard "*.example.com" +// matches to "wildcardonlymatch.example.com". +TEST_P(SslSocketTest, MultiCertWildcardSniMatch) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "wildcardonlymatch.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SAN_MULTIPLE_DNS_CERT_256_HASH); + 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + testUtil(test_options.setExpectedSni("wildcardonlymatch.example.com")); +} + +// When client supports SNI and there is no exact match, validate that wildcard SAN *.example.com +// does not matches to a.wildcardonlymatch.example.com, so that the default/first cert is used. +TEST_P(SslSocketTest, MultiCertWildcardSniMismatch) { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "a.wildcardonlymatch.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_NO_SAN_CERT_256_HASH); + 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/no_san_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + // The validation succeeds with default cert that does not match to SNI since Envoy does not + // define the criteria that how to validate cert SAN based on SNI . + testUtil(test_options.setExpectedSni("a.wildcardonlymatch.example.com")); +} + +// On SNI match, the ECDSA certificate is preferred over RSA. +TEST_P(SslSocketTest, MultiCertPreferEcdsaOnSniMatch) { const std::string client_ctx_yaml = absl::StrCat(R"EOF( sni: "server1.example.com" common_tls_context: @@ -1350,8 +1450,8 @@ TEST_P(SslSocketTest, MultiCertPreferECDSAWithSni) { testUtil(test_options.setExpectedSni("server1.example.com")); } -// Multiple certs with multiple SANs, wildcard does not match if an exact match is present -TEST_P(SslSocketTest, MultiCertMultipleSANWithSni) { +// On SNI match, the RSA certificate will be selected if ECDSA cert is not present. +TEST_P(SslSocketTest, MultiCertPickRSAOnSniMatch) { const std::string client_ctx_yaml = absl::StrCat(R"EOF( sni: "server1.example.com" common_tls_context: @@ -1376,19 +1476,23 @@ TEST_P(SslSocketTest, MultiCertMultipleSANWithSni) { private_key: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" - certificate_chain: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert.pem" private_key: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_2_key.pem" )EOF"; TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); testUtil(test_options.setExpectedSni("server1.example.com")); } -// Wildcard in SANs is matched properly, *.example.com matches server1.example.com -TEST_P(SslSocketTest, CertWildcardSANWithSni) { +// On SNI mismatch, if full scan is disabled, validate that the first cert is used. +TEST_P(SslSocketTest, MultiCertWithFullScanDisabledOnSniMismatch) { const std::string client_ctx_yaml = absl::StrCat(R"EOF( - sni: "server1.example.com" + sni: "nomatch.example.com" common_tls_context: tls_params: tls_minimum_protocol_version: TLSv1_2 @@ -1398,7 +1502,7 @@ TEST_P(SslSocketTest, CertWildcardSANWithSni) { - ECDHE-RSA-AES128-GCM-SHA256 validation_context: verify_certificate_hash: )EOF", - TEST_SAN_MULTIPLE_DNS_CERT_256_HASH); + TEST_NO_SAN_CERT_256_HASH); const std::string server_ctx_yaml = R"EOF( common_tls_context: tls_certificates: @@ -1407,20 +1511,29 @@ TEST_P(SslSocketTest, CertWildcardSANWithSni) { private_key: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" - certificate_chain: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem" private_key: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem" )EOF"; TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); - testUtil(test_options.setExpectedSni("server1.example.com")); + // The validation succeeds with the default cert that does not match to SNI, because Envoy does + // not define the criteria that how to validate cert SAN based on SNI . + testUtil(test_options.setExpectedSni("nomatch.example.com")); } -// Wildcard SAN *.example.com does not matches a.server1.example.com, validate that the -// default/first cert is used. -TEST_P(SslSocketTest, CertWildcardSanNotMatchWithSni) { +// On SNI mismatch, full scan will be executed if it is enabled, validate that ECDSA cert is +// preferred over RSA cert. +TEST_P(SslSocketTest, MultiCertPreferEcdsaWithFullScanEnabledOnSniMismatch) { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues( + {{"envoy.reloadable_features.no_full_scan_certs_on_sni_mismatch", "false"}}); const std::string client_ctx_yaml = absl::StrCat(R"EOF( - sni: "a.server1.example.com" + sni: "nomatch.example.com" common_tls_context: tls_params: tls_minimum_protocol_version: TLSv1_2 @@ -1430,7 +1543,7 @@ TEST_P(SslSocketTest, CertWildcardSanNotMatchWithSni) { - ECDHE-RSA-AES128-GCM-SHA256 validation_context: verify_certificate_hash: )EOF", - TEST_NO_SAN_CERT_256_HASH); + TEST_SAN_DNS_ECDSA_1_CERT_256_HASH); const std::string server_ctx_yaml = R"EOF( common_tls_context: tls_certificates: @@ -1439,18 +1552,22 @@ TEST_P(SslSocketTest, CertWildcardSanNotMatchWithSni) { private_key: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" - certificate_chain: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert.pem" + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem" private_key: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_key.pem" + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem" )EOF"; TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); - // The validation succeeds with default cert since Envoy does not define the criteria that - // how to validate cert SAN based on SNI . - testUtil(test_options.setExpectedSni("a.server1.example.com")); + // The validation succeeds with the certificate that does not match to SNI, because Envoy does not + // define the criteria that how to validate cert SAN based on SNI . + testUtil(test_options.setExpectedSni("nomatch.example.com")); } -// EC cert is selected for a no-EC-capable client +// EC cert is selected for a no-EC-capable client. TEST_P(SslSocketTest, CertWithNotECCapable) { const std::string client_ctx_yaml = absl::StrCat(R"EOF( sni: "server1.example.com" @@ -1466,17 +1583,12 @@ TEST_P(SslSocketTest, CertWithNotECCapable) { 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/no_san_cert.pem" - private_key: - filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/no_san_key.pem" - certificate_chain: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert.pem" private_key: filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_key.pem" )EOF"; - // handshake is expected to fail TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, false, version_); // TODO(luyao): We might need to modify ssl socket to set proper stats for failed handshake testUtil(test_options.setExpectedServerStats("") diff --git a/test/extensions/transport_sockets/tls/test_data/certs.sh b/test/extensions/transport_sockets/tls/test_data/certs.sh index 5027e34371..75a63ba416 100755 --- a/test/extensions/transport_sockets/tls/test_data/certs.sh +++ b/test/extensions/transport_sockets/tls/test_data/certs.sh @@ -154,6 +154,10 @@ generate_x509_cert no_san ca # Concatenate no_san_cert.pem and Test Intermediate CA (intermediate_ca_cert.pem) to create valid certificate chain. cat no_san_cert.pem intermediate_ca_cert.pem > no_san_chain.pem +# Generate no_san_cn_cert.pem +generate_rsa_key no_san_cn +generate_x509_cert no_san_cn ca + # Generate san_dns_cert.pem. generate_rsa_key san_dns generate_x509_cert san_dns ca @@ -182,6 +186,10 @@ generate_rsa_key san_dns4 generate_x509_cert san_dns4 intermediate_ca rm -f san_dns4_cert.cfg +# Generate san_wildcard_dns_cert.pem +generate_rsa_key san_wildcard_dns +generate_x509_cert san_wildcard_dns ca + # Generate san_multiple_dns_cert.pem. generate_rsa_key san_multiple_dns generate_x509_cert san_multiple_dns ca diff --git a/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.cfg b/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.cfg new file mode 100644 index 0000000000..774230ad07 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.cfg @@ -0,0 +1,31 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +countryName = US +countryName_default = US +stateOrProvinceName = California +stateOrProvinceName_default = California +localityName = San Francisco +localityName_default = San Francisco +organizationName = Lyft +organizationName_default = Lyft +organizationalUnitName = Lyft Engineering +organizationalUnitName_default = Lyft Engineering +commonName = server1.example.com +commonName_default = server1.example.com +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectKeyIdentifier = hash + +[v3_ca] +basicConstraints = critical, CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always diff --git a/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.pem b/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.pem new file mode 100644 index 0000000000..48f9d0e598 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUeOS06HNSZkEe/HG0DYLTrjfLVq0wDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIxMTI5MDg1NDIzWhcNMjQx +MTI4MDg1NDIzWjCBgjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx +FjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsM +EEx5ZnQgRW5naW5lZXJpbmcxHDAaBgNVBAMME3NlcnZlcjEuZXhhbXBsZS5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVp5SeYoYpGj70QRdpw7vh +FQVrlExL9H5iUEULarmSB/gM1PiDhMfdWSDd6JDbvwHSRy8kMuDiolbK7J9bMRJb +FigO2AES7gyDYBrXCS+ML4gCScF9B4FqEaYbK6l6wBLRDhbZh/H5F0vn6q9Zo1wT +3bUBeXaX5bhaP3kQYWq1nf8PSv3OLAawcKZbs6VMCjJHOcNINoJn/BBtMXVei0y0 +mV8TE5r3zi1q8zlzNX1HAnVSytyTNtxi48AMPARqbI/yon5yswGIET078X9MFDvw +iNr6hnkNnTkkp371a7hBxHS3FjbHni22j1sK9LYH6cdgmExydg1xgDIAOF6DteZt +AgMBAAGjfDB6MAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdJQQWMBQG +CCsGAQUFBwMCBggrBgEFBQcDATAdBgNVHQ4EFgQUgtZxmYkWcRj29ugdpUavlBoo +M+cwHwYDVR0jBBgwFoAUEB5B9l/TpnxF0Q5zQYC15zI4SfUwDQYJKoZIhvcNAQEL +BQADggEBAKTXuisv9jakJ3fs12Y1NVm+IjOftoCn0LbC8pwmrP0zCV9qFtF+GbcR +3lzfDIn6Q9PEZMCz7M2RNBxWiMM8DBmzZZEvrqOkiHIelqLBDyB7HFqdZuxqF69W +IzD5V8GTPHgJdno4cNXjEUt3GSlE7xqHXLKvvivlwvCgKtwleTQZMOLu+SJqtCrI +lf2lmDu6v1hupQ5i0zHxM8mbitlPwT5CsA2hhA/quzLAoL7KsykyzwXoqLl58ma1 +M4xNdGz7CYeuWx8mSksawLtLmOcGrch01tmt3ntrWI4qI133h2Ewys5ItZaAXcXZ +9uyd9ELdT0KpnFDbBUE4h6kqr9PExOs= +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert_info.h b/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert_info.h new file mode 100644 index 0000000000..74ca7020b9 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/no_san_cn_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_NO_SAN_CN_CERT_256_HASH[] = + "941fda164f16bd6f92fb39513bcffb306619fa90b85b764d09b491f5cca802f0"; +constexpr char TEST_NO_SAN_CN_CERT_1_HASH[] = "6748d234ee7122e2d67aa6007a5df8bdd147a612"; +constexpr char TEST_NO_SAN_CN_CERT_SPKI[] = "5TC7nnhfLAYZdPR5Hx7vhZ7W+1sNu6CKzTFbnH9SYrw="; +constexpr char TEST_NO_SAN_CN_CERT_SERIAL[] = "78e4b4e8735266411efc71b40d82d3ae37cb56ad"; +constexpr char TEST_NO_SAN_CN_CERT_NOT_BEFORE[] = "Nov 29 08:54:23 2022 GMT"; +constexpr char TEST_NO_SAN_CN_CERT_NOT_AFTER[] = "Nov 28 08:54:23 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/no_san_cn_key.pem b/test/extensions/transport_sockets/tls/test_data/no_san_cn_key.pem new file mode 100644 index 0000000000..547438384d --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/no_san_cn_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA1aeUnmKGKRo+9EEXacO74RUFa5RMS/R+YlBFC2q5kgf4DNT4 +g4TH3Vkg3eiQ278B0kcvJDLg4qJWyuyfWzESWxYoDtgBEu4Mg2Aa1wkvjC+IAknB +fQeBahGmGyupesAS0Q4W2Yfx+RdL5+qvWaNcE921AXl2l+W4Wj95EGFqtZ3/D0r9 +ziwGsHCmW7OlTAoyRznDSDaCZ/wQbTF1XotMtJlfExOa984tavM5czV9RwJ1Usrc +kzbcYuPADDwEamyP8qJ+crMBiBE9O/F/TBQ78Ija+oZ5DZ05JKd+9Wu4QcR0txY2 +x54tto9bCvS2B+nHYJhMcnYNcYAyADheg7XmbQIDAQABAoIBACsTP05zdLMCvKCI +Iw1Kzi1CDLa0znEd2QE/R7xWhxVfD+6eXYA+nAEPgCXdRp9XMinP+jwlGdv3eOIu +tK4xBY9hty/O8uB7KACwtpiWHIT57ETx09W+VqQo7MFbmy7JXMHTQl4XH3zl6oLW +eQXsfpruvxiOAY/8W5+uXl0kFtyIoMHz/z48Ocv3PY7EJsU4P4XkYmu6Wbc8ZJDY +NszsAFBvA+cYRBocmVW0bE9RxTArCIhGz7PC3gJrUpJs9xOGj0/nCQPYnrgt8W/5 +YN7965OIO/FWR/6tfKb57O00aifYZSO51OhCW4KT1dOqNvS9FeGmBfBi2vjsTy/2 +pUnQpwECgYEA9akgBIE7uOkOaEB6bMt53vRjnUBIHg/PyoCRQjQZWw74Q/zvV9fo +OcaaEJ4JtuonD50zvHYupYJR5+Lzgu6ZG6ekf+ME+9VedJEgmdQbJwJiXiAw8//J +IZNPwEUpE+/EPGFKXdQzD7MZNsjV4j5Qvtrd9NufLCYE+Xr3ZRLUFkECgYEA3qWb +HJjXHarYCBbPyq2mEa3IrU3XE06yT++/2IwFTfWzAGyvlT9PiTq4o3YO6nWTNl7c +n5u2l7AWXls9lojfUJhyNNUgV6NhIFIaIuYuY+6jbU2cMVr9POTlM915gSZgnDrE +10NzagW4vpurktgIAB5sSfD+nGbzDVkaC6AAvS0CgYAabh7GvUy6oUBukPla+S1c +gnixM3hO266kf96g+8/6jwrA7damYdFXXfm3bflnl5uqP2SQkzqDjhjX1QAUBy2r +3waAx+ECYzttWSvjwQFG2ifRLq91oQB3v0ymSAqaXYKEaIdCvxDbUI6W6Vaxp7iy +QAP/Ux2190Uzvjodwr/UwQKBgAwWZZNp4NPlgJD/3Awe3VM8Yha2kSSlMx0P4RR6 +lZcj7bHqNKYfp8vFely1fh3QgEuFXujzZThbAPvibDeN+sG/ZQPlRjo7znwYuP6I +DKC0Rf8bJY2waOszahYkwgH2+WvB7QnayZtJ8Du469mvDNAk1rA0+z22gR+KZNWY +vDGJAoGAPMzrlzL/JPYNlTfMWdCH/MQlJDaSMO4L6Rb0V8yoHSuhkRpp2iwTSS9j +d4i5avF9igL6VHoUWof9Gwj40KgochYo6mYMwmDlhja/yT26TdpOAj7GQp8ymEOX +HMiziJTwwjd8XwuEMwLNQliEJao+2BUTwEuOwjM9pQZyGhEJEPs= +-----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.cfg b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.cfg index d5532d54a4..de49816473 100644 --- a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.cfg +++ b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.cfg @@ -13,8 +13,8 @@ organizationName = Lyft organizationName_default = Lyft organizationalUnitName = Lyft Engineering organizationalUnitName_default = Lyft Engineering -commonName = server2.example.com -commonName_default = server2.example.com +commonName = server1.example.com +commonName_default = server1.example.com commonName_max = 64 [v3_req] diff --git a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem index fbbf71d776..709afaa936 100644 --- a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem +++ b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem @@ -1,25 +1,25 @@ -----BEGIN CERTIFICATE----- -MIIENDCCAxygAwIBAgIUR8m6bvw3FZDfqgVrUZYYHa8XgucwDQYJKoZIhvcNAQEL +MIIENDCCAxygAwIBAgIUZkQqW/H3LYutwbMjXabEtYyTTtkwDQYJKoZIhvcNAQEL BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n -aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIwODA1MTAzMDUyWhcNMjQw -ODA0MTAzMDUyWjCBgjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIxMTI5MDgxNDUxWhcNMjQx +MTI4MDgxNDUxWjCBgjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx FjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsM -EEx5ZnQgRW5naW5lZXJpbmcxHDAaBgNVBAMME3NlcnZlcjIuZXhhbXBsZS5jb20w -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDl6HmgvSL66DUZV3sGizED -j5pz40DRhFfGPtXhYEJirjPY/1THjLKXXiTXC9WU7vmYnzr/SoHSYJkYC5z/VVPw -lXjMZkoq0ff6q3mysWNmuvDKInw8/CHXt0EZ9fs4u7Uj/gfvKN9dHMennsgY3pzy -ZxxGTR4c3QJxCYDw7OS3dkqwXq4mVafGC9Sf6sKuYEMAiSGlVyC4KM9b6hyczr7B -sMaWn2Jc6okVvYbedymMmly8KrGuV/a8ev8wA7uYQXaQLq8lsRg4RJu5r1tCIs/M -fVGRDpUClAT5k4wlwSbg3cW/ElulOYGP2RkxKWp16heVsT7QCP9j4sm3AvP2mhb3 +EEx5ZnQgRW5naW5lZXJpbmcxHDAaBgNVBAMME3NlcnZlcjEuZXhhbXBsZS5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZQFunKrw9u0zeTWeVVhT0 +UqiQ+P+UQsO8oumeLLph9ANjOThjRQlsugUeiMZGTE6LBLg16MQeB4Y86A146tko +SoOvB0HXUfmF3+NryJFh8Otqa+Us6BJKl8L6B5ZfIimbAD3cc0jzZttDwFzMx5Hd +Dhr1cJuQ7tPOUEpbUa14rZf79ZrO6U1NRTcjwqCI0m/Lo8RLFVI3vVPq8LuBxynm +Ba4A61dH/+zFyk33qzKe2FDD4wBSYnJJiQRB5eXu3OYN+IthDek0XexFik9mBSq7 +zzp6cNhj/nKsK9x+YkweNHYEhJUiXJaXjAoC8N00CCKV3zKmK3tcsOx4rO+IwGkx AgMBAAGjgawwgakwDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYw FAYIKwYBBQUHAwIGCCsGAQUFBwMBMC0GA1UdEQQmMCSCDSouZXhhbXBsZS5jb22C -E3NlcnZlcjEuZXhhbXBsZS5jb20wHQYDVR0OBBYEFNtjqZr7arX5xNkSO6DHFmdN -NLoDMB8GA1UdIwQYMBaAFPI5fSZyiR780MSvThWzAtcySc2TMA0GCSqGSIb3DQEB -CwUAA4IBAQDEQ8skVqZ2Yh92sY+YNeMIDjuQBTnWTIYKvOuA761iMgMFxdXerICO -9jils5FhshCFgAtoL3hYaEx4ot4vXvGw27T9lpIwJPN4XpJZ3nJ0GHU/cYdbeLmR -3RZ85alW2YEUh2ObQArb7Ce0H84x0w/oaMuqc/33w9qCXD2gNyKmHbJ6TnRPMPS3 -7lK3vVSga4K6TJfGShKGGC/c5PbLp7QZnCane3vVJh7S3wcXoa2K2cS2HlmpWMDQ -9CDJaCqNg06RWXLfVLZD/cglf1HY4i9EtaPxoVQWo6xzWtqzGsMDcbJkeUdQuc8D -7ul6DhzD9AdBZZhSfndm2BYx0jkeH+0V +E3NlcnZlcjEuZXhhbXBsZS5jb20wHQYDVR0OBBYEFNM73im6qZ8+Nm6Os1aE6Zhp +IufDMB8GA1UdIwQYMBaAFMC8HqKJWYd6GREJEhvULao1CmbcMA0GCSqGSIb3DQEB +CwUAA4IBAQC3AyBzLObgp68YZtM36zSQ0MdqMOfJBXv+9TAs2OcliLbGhmmk0RbK +UPmYdgUrj8D73WOGqaw3fKLhx8U8L311+2nQvwcTI5DXVRWULpXvtOljWFSCZuR3 +udwGmwsqgA/dV1lDqHVdVGz8i8HEMJ07qVUWXcnzTPCfGLODn3sv+qiHCEfglpns +5HDXmFYd8toObVMzdve8O3a2DBElCmUu8xCU+SA8OLyEDGUitEjAE4P5KIKaAKCt +GczUt9C4Ob9ysbxGzXXoTu1EQyYonZ1PtKNG7RIW5kAXmQ2k49JTi79RA/qjGgyO +EzbbOtjxjehfdDoS6m9++9uxysRhMHWc -----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h index 2581393206..e31001f219 100644 --- a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h +++ b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h @@ -1,8 +1,8 @@ // NOLINT(namespace-envoy) constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_256_HASH[] = - "5f9f44b7cff504eb174c1f0951b2c194635018bc96f79d307543cd94fb3c7ea9"; -constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_1_HASH[] = "83f72dbab8c7d0de34e5339b8af8f8c721ad6b6f"; -constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_SPKI[] = "Ox2VaAfYGt0G4jjFg4XSJGRIA3Fk4WQfMgH296xTTuI="; -constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_SERIAL[] = "47c9ba6efc371590dfaa056b5196181daf1782e7"; -constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_NOT_BEFORE[] = "Aug 5 10:30:52 2022 GMT"; -constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_NOT_AFTER[] = "Aug 4 10:30:52 2024 GMT"; + "763cfed09cc42f850274dd714b9fc3ac47c65f9faf6d826458f9fdd205be8afd"; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_1_HASH[] = "1ab5a4d31c7e5fb6b55297cbb7de8da9af2028da"; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_SPKI[] = "FO7glfFvznptz70v1xLJm28xCfZpVXzYbWgoLxBMguA="; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_SERIAL[] = "66442a5bf1f72d8badc1b3235da6c4b58c934ed9"; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_NOT_BEFORE[] = "Nov 29 08:14:51 2022 GMT"; +constexpr char TEST_SAN_MULTIPLE_DNS_1_CERT_NOT_AFTER[] = "Nov 28 08:14:51 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem index 85249aeb49..d126330a8b 100644 --- a/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem +++ b/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEA5eh5oL0i+ug1GVd7BosxA4+ac+NA0YRXxj7V4WBCYq4z2P9U -x4yyl14k1wvVlO75mJ86/0qB0mCZGAuc/1VT8JV4zGZKKtH3+qt5srFjZrrwyiJ8 -PPwh17dBGfX7OLu1I/4H7yjfXRzHp57IGN6c8mccRk0eHN0CcQmA8Ozkt3ZKsF6u -JlWnxgvUn+rCrmBDAIkhpVcguCjPW+ocnM6+wbDGlp9iXOqJFb2G3ncpjJpcvCqx -rlf2vHr/MAO7mEF2kC6vJbEYOESbua9bQiLPzH1RkQ6VApQE+ZOMJcEm4N3FvxJb -pTmBj9kZMSlqdeoXlbE+0Aj/Y+LJtwLz9poW9wIDAQABAoIBABSaVbP63xSfFIsV -1NeMR7Mq/kteVSOxeXAxl1cgGjLfq5PJpd5QFH0OGb0PuW8kT0fHFTFliubAc1e9 -Sq9zBRetPWsLy5+0u0VCcqB51w+Qtx1WI84PRjUfnfzml0HJSH1WOX3MKuAcQSQb -TZE0+dmbNeRy6rzpbVuV33E3uXNbQe4ocXPZtNV3KFX47CPZeBPrN6XZPdVPDp4g -oGzB5YKSwRPFfHDarBfyuFxs8VQq7AUYgNGp91a/QWBsX65F/lW1THVRFDSMiBXn -09ZXhq5ZxUluPKrIw9E/4HG/oDcsg9oR65CWjYXC02LiWdW/Pe79DIERB/XyQgI6 -dTmM8xkCgYEA+AcaNzkvDk7/eJRKzip/JAHQOuzyQkdGFgKwg3Kc9fXkGlotFzwF -SJ4v18Jmu6kKE/Oz94mt4b4CYaliTZfME/u/h0WYOj4xL+/VzJhm+kRIrdLPGqcW -p43WRIvY44d9YLFyU8ELYyP1jLpJNFa5zVP5SjzKFlKmgE3vAXe73i0CgYEA7UxG -cWvbo76fTsnhgjC6X5PUE4b38/5hnZ4hCI/2JHzN3Wzv0ki7ML9wuizKCxEw0Mfq -+8gL5XNaDchJdkFrua8roPCM7DQEfGB/jns1Rd34PZrJejnGvjtpg68AXpnmhJYc -+URKGS1SseuTa1cRGBNL9pNEGKr3J/TODcg4pDMCgYEA8Vgf51qKclbmelN6EvEB -NG1bTOthtKKcDTDix5WQLDnvksDqnXA3B/l9PmqZAsQy3UVHeniLLV1x/cwPrscl -utA+B4ft38bCoA21MdeeZKprOlEmW535CmOW+q5GBujBVIR2Zg8zYG4OMrjWIMq/ -E5BBxD4wutYD1FeBWSFEOqUCgYBUzLkvtxeagHh1s3c/CyLPHQjgoY7iRrmjlerA -IJIZn/ABfPiG8S7T0NX78h2Rrub+9TJoH6kAqiQ4YKa/98kDZjH1JYF2t3AS8nki -9ayn9xbLDRGK+DKpsJmYUWWUaPMT1oEsItdIAAZZRpI7/bVCIUM4LpBbMF16jeVE -z5ROOQKBgGr+j4Upowy7dg0wJN12wn3Wxi8gVCZ2arPeb47XzEXWnBTSYzolrve+ -LFVmqrsQ6DOM5Ek+ga2TWGII1qibUx12wOQugw7vKFIE0j8DnrdMDHw34zHp5Il8 -HSnozjMy4QTKv2/Gqh8R9bjD9gsdOFZvTI4vJgNoqHX44U+n6PeJ +MIIEpAIBAAKCAQEA2UBbpyq8PbtM3k1nlVYU9FKokPj/lELDvKLpniy6YfQDYzk4 +Y0UJbLoFHojGRkxOiwS4NejEHgeGPOgNeOrZKEqDrwdB11H5hd/ja8iRYfDramvl +LOgSSpfC+geWXyIpmwA93HNI82bbQ8BczMeR3Q4a9XCbkO7TzlBKW1GteK2X+/Wa +zulNTUU3I8KgiNJvy6PESxVSN71T6vC7gccp5gWuAOtXR//sxcpN96synthQw+MA +UmJySYkEQeXl7tzmDfiLYQ3pNF3sRYpPZgUqu886enDYY/5yrCvcfmJMHjR2BISV +IlyWl4wKAvDdNAgild8ypit7XLDseKzviMBpMQIDAQABAoIBAFe07cuNKzXY2e2d +XkYh3I/OlxMAE15tncwYESy0bgKBmqSCQ0uAYY+tgceT/EgBImxpWV0aPaLJ8t2/ +6UB5v3XOr18IFQKyq4PqoUuwS1E5DDGIoZsBjfDjV+nNCl4Z0qYq8M6BFGB/k+HE +VTKvFyohOa0A6y0V3hiavYokbV4cKH7YgA/1EjkafMqP8wvCOGx45JN7ebx0pfd2 +FkYPWT+ygSOPbJWq2hYOvMEP2FE7p6RRF4ngPZwuSufCx6MD0TEBDMjdiPNTQ5LB +q+CGbUcLZUEcbphtCnl265fk0RQbc17Lg2n99Wes02JUqEoCJr0/czGlJq97Hpou +32S/PmECgYEA9YzFAbwvdrkbm2EBgyeD5qknpp1rUQ8i5kAQRuOPSNRzwBPB2dqs +jVETB78+oAiqAJLaM/+JU58HhDZTtNSM7GRyDEyR1U1f+F/BiSFB5Z1RwsPHFz83 +X9EzrBnRbKaifB6VG0dStwe0RwzezZyTdVlgDU0u5gRJNbHLJboRMV0CgYEA4n9H +1IkgV0iMtUZvAq2O+WK4BI59ChSNKyfUEryzTysJ9Ux2XyuTMj4AYEnpeQNSvlcS +eaoRL5M6HyH7qbPt5nUoeEnaUiq4TFpOd6RRMfxUDX0YfHUIEP4jCs3zJj7igKPI +orsJkcrEA0sjllk3opRZrWClf69aXFXHKjNTNeUCgYEA3p25HlGWVnSL6tqPnHB+ +zOge4pdlbB/k7Aq4twwoI0c5ibJf1TrDpXsTXbqs9Dnmpo589Rd/USikj+EREqiA ++vA11aC1tkRn9a4asFBOhfWS4pjMLu9JLL1ls2nUzERmm1C+rfSDsRxy/NdWR+Lk +bUeSJX0MP80y8lM2cgW3AKECgYEAlUlHUvK++ki7VZADA0TaA83pdDdmewposZUi +1ubBWOEJQ0EOlFPoRSlQniaZWEW0d/h0GPTbNvxXRsXjVktd9pQqBH7JA3dc2hZ7 +uaf4THYAAJP/W6jXlDutZSWxqfew2xEY4QlemVWINJOF9UcAwIv/P6N/8dTmPg3b +pBkIM3ECgYBsSmjvY55Mzhu7k/TrqaTWvumg8tAOFsq/SZzCTtew7ONmk4olcCSv +ipme57urxSnVmW3Wue1le1fb2guEHwLFebZ6jM4IJdYM6P80hS9pAcBlg0GCKfLX +LxPL6Or+N51yErCxeBBXjeGzxzt1KA2M+nSYVlk+a2QJFbVflxJ0lg== -----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.cfg b/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.cfg new file mode 100644 index 0000000000..af1810cbd1 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.cfg @@ -0,0 +1,36 @@ +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req + +[req_distinguished_name] +countryName = US +countryName_default = US +stateOrProvinceName = California +stateOrProvinceName_default = California +localityName = San Francisco +localityName_default = San Francisco +organizationName = Lyft +organizationName_default = Lyft +organizationalUnitName = Lyft Engineering +organizationalUnitName_default = Lyft Engineering +commonName = server1.example.com +commonName_default = server1.example.com +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectAltName = @alt_names +subjectKeyIdentifier = hash + +[v3_ca] +basicConstraints = critical, CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, serverAuth +subjectAltName = @alt_names +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always + +[alt_names] +DNS.1 = *.example.com diff --git a/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.pem b/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.pem new file mode 100644 index 0000000000..187e55b089 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEHzCCAwegAwIBAgIUQ3GhSa9d331nV9WlCR2kncj8/eQwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjIxMTI5MDgzNTI3WhcNMjQx +MTI4MDgzNTI3WjCBgjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx +FjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsM +EEx5ZnQgRW5naW5lZXJpbmcxHDAaBgNVBAMME3NlcnZlcjEuZXhhbXBsZS5jb20w +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCydu5vjSyiIc9f4elZFaSC +1+dmqhjLX7/sddXe4HcH1oRRIzCp6BR3kaBnju6mN2MxyuWHjMjWMHWn1dG+gs8v +bA2g2slNjErvpkAK0B2pcSMnhbOrEX8hOQnN8yIfxHKUMjneoAVkaUuSmK3aNlNk +7rV7+8MI1Wv20ZXwx+jKrBSwVibf2UCw2b2S2QqoVl6wdW5FU0t9BfAoSL3+uMSg +O35wD4R9gE/VSKA4pytMt74s3voey5ewDwj6gxWI/SBqN9k2dJ9dgFZplvoAYcMt +MvclIaSran9aRmwaEZ3fuaubs75KfzCZ2PsFarSIL9gMHSkAQ1ll7WW/Ca9I7IbB +AgMBAAGjgZcwgZQwDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYw +FAYIKwYBBQUHAwIGCCsGAQUFBwMBMBgGA1UdEQQRMA+CDSouZXhhbXBsZS5jb20w +HQYDVR0OBBYEFNf9Tmk+x9Tn1XQxDbO9XYOTiSWcMB8GA1UdIwQYMBaAFDj1rRBH +puEsMTTEkTgPjrECy9BUMA0GCSqGSIb3DQEBCwUAA4IBAQA5G70TN8g/pstV7mOu +PKKPtuFIuCvi4hr5v9vBUgn1MTfHYaX4aQ9AEdGCwufO3D/VdS6VogwedCzjG53m +mFz1UjmCPIXDFv9NwoivyOUFP3DcCha8qEQWnn6M1DJT4DDK5F3pYiq7VeFGO/rb +TiC77XMRrFbgTxeiYTS9nyE+JoEet3G3P7Ty9up0u+uT8AAxZ1E78z8oHb836fe6 +z14hldf06C8k8AJoSaaEdxFaNEFJYKe/8zZpru3qVJyZrLCPDj99l8g+CZKQ1jyZ +1gg0ILMx/1uQVlywy82FouqAIdbMQ+gL8UDPSssRyIrM08BE1EJMHseAqguHA3Ea +xiNZ +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert_info.h b/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert_info.h new file mode 100644 index 0000000000..15617a7ef8 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_SAN_WILDCARD_DNS_CERT_256_HASH[] = + "5467d580cb0d3cfd3406fe8f81dfdf539f4d3f1f11350d0a9a343aae17a2793a"; +constexpr char TEST_SAN_WILDCARD_DNS_CERT_1_HASH[] = "9abbb1060970c0c7d3dcd5f4700b1c7dee27ecc7"; +constexpr char TEST_SAN_WILDCARD_DNS_CERT_SPKI[] = "PmFu9r6F2Er3dRwXW6YoEB1/GJNX6+4RT2wLBv7Ogrc="; +constexpr char TEST_SAN_WILDCARD_DNS_CERT_SERIAL[] = "4371a149af5ddf7d6757d5a5091da49dc8fcfde4"; +constexpr char TEST_SAN_WILDCARD_DNS_CERT_NOT_BEFORE[] = "Nov 29 08:35:27 2022 GMT"; +constexpr char TEST_SAN_WILDCARD_DNS_CERT_NOT_AFTER[] = "Nov 28 08:35:27 2024 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_key.pem b/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_key.pem new file mode 100644 index 0000000000..f10ee84e2d --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/san_wildcard_dns_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAsnbub40soiHPX+HpWRWkgtfnZqoYy1+/7HXV3uB3B9aEUSMw +qegUd5GgZ47upjdjMcrlh4zI1jB1p9XRvoLPL2wNoNrJTYxK76ZACtAdqXEjJ4Wz +qxF/ITkJzfMiH8RylDI53qAFZGlLkpit2jZTZO61e/vDCNVr9tGV8MfoyqwUsFYm +39lAsNm9ktkKqFZesHVuRVNLfQXwKEi9/rjEoDt+cA+EfYBP1UigOKcrTLe+LN76 +HsuXsA8I+oMViP0gajfZNnSfXYBWaZb6AGHDLTL3JSGkq2p/WkZsGhGd37mrm7O+ +Sn8wmdj7BWq0iC/YDB0pAENZZe1lvwmvSOyGwQIDAQABAoIBAHel6OF9AqmIhWHB +MhL5wZu/aHKaI5lmDmRX9BVC5Eqq9Xb4Ys3mvKIVTjdfynxBsg1V/GDOcG+5eYgW +gFJ7nLAsf06SoFePcUYObe1R7jEFmw4KQ5SzXEpH1VzwXrZxnGfi7I1nN3ZEadhU +6Gnd9jc7Gw2Okwm/HG0TwrT1Mgt+B0TQBjE7CL2erEcSad4XHlOIPUChTiPQjO16 +VnzXQA+9A8hbQ0q89sIFk2ADRv9Nk5vZuwqctmIYphEehkhKOp6Yk/fusdElOop1 +FD8mFwn1kRJ8VBwqoMscuyexAxsycj47x24+vo3CYMinGgV5ln+pMTNXC/K6yQid +QoFISCkCgYEA635EyI7rOr77mPaOP1d3GNzDjqIYKE9YM31us34EtgRALdJPBzNC +4uR2xjRMj0RrXloq58OtiVDWdRElrs4YPds3r4NA+4B3ScI+Z0zdfnZi8uFC6a1g +cNBd0ByDTd6X63A38Vo+40NRI7gAwRrxJWX+ncImjqqUvoe0hOJfiisCgYEAwgFa +B4HaSJhL1M1Vrx/4qWhngznaBMURTkHRXCKhx74zHHlCzfZTpnzxSF8LM5jHIY3N +6ZG2Tjx0afOFyxc6TmZ6GCXfdjtnt+2aU1r6E7PEhWCt4OVdtN8+puUeeeIgWwgJ +I5ybQlK88IlMVlULWxghx0NU24xPt4WaS4m22MMCgYEAo36aypNPBQ4rJ1umDpvu +97ftnU00cGgF2FmxTbSkGKqC1E06yMjyGwx62cjdjmn2ER+Qwi+8f6U7/zLUcrRr +aCaWVqhNqU7bgr31DUr0TCRtNirmhc31Mt23BBGHp7xuxOq4LDx3hNjrsFzDSU4n +NcvGJkjw9APCGTWmQsvXgNkCgYBrVdocmzBMx830r6I/YN3vxG64wLIpv2pCtNGD +OznszAN1NnXKhAVl8GyCEOueoWFS5CTPnCmUAYia8fvNEE3XAIV+hL2sqVNKBLBE +e+ouKmCJNqMc8Wm132nHZq5E2uHx23FK/aM8v4//OMc+YJEG1kQh/dOeIHQeYlGq +h0fdhwKBgDb93FcWrGOklUtO/go0jz2CYR12n4yio7hUzrvUuZnnLYaVMaZEIxsO +C2dZcCWa9SVFEnXE1ITmsN+RNv2z8tMtYNeuqTqDWSNIiebOri2VlcbCFKOKaNTU +1Cj7Zd9kE5L+mamuvQ7G+PqUQ6DooZXmW+gSiQVmFVJTsJyQTu03 +-----END RSA PRIVATE KEY----- diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 703a956012..828f1690f9 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -142,6 +142,7 @@ class MockServerContextConfig : public ServerContextConfig { MOCK_METHOD(const Network::Address::IpList&, tlsKeyLogRemote, (), (const)); MOCK_METHOD(const std::string&, tlsKeyLogPath, (), (const)); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), (const)); + MOCK_METHOD(bool, fullScanCertsOnSNIMismatch, (), (const)); }; class MockTlsCertificateConfig : public TlsCertificateConfig { From c17e498cb1125e3d3bd092a2344cc6422fda4784 Mon Sep 17 00:00:00 2001 From: lei zhang Date: Thu, 1 Dec 2022 15:56:00 +0800 Subject: [PATCH 19/35] Cache size (#347) Add support for setting cache size Signed-off-by: LeiZhang Signed-off-by: lei zhang --- .../v3/local_certificate.proto | 5 ++ .../certificate_provider.h | 3 +- .../local_certificate/BUILD | 6 +- .../local_certificate/config.cc | 17 +++-- .../local_certificate/local_certificate.cc | 45 ++++++------ .../local_certificate/local_certificate.h | 72 ++++++++++++++++--- tls_bumping/splicing_bumping_all.yaml | 1 + 7 files changed, 108 insertions(+), 41 deletions(-) diff --git a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto index 93a41ec2e1..0fa5164761 100644 --- a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto +++ b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto @@ -5,8 +5,10 @@ package envoy.extensions.certificate_providers.local_certificate.v3; import "envoy/config/core/v3/base.proto"; import "google/protobuf/timestamp.proto"; +import "google/protobuf/wrappers.proto"; import "udpa/annotations/status.proto"; +import "validate/validate.proto"; option java_package = "io.envoyproxy.envoy.extensions.certificate_providers.local_certificate.v3"; option java_outer_classname = "LocalCertificateProto"; @@ -41,4 +43,7 @@ message LocalCertificate { google.protobuf.Timestamp expiration_time = 5; Pkey pkey = 6; + + // The maximum number of hosts that the cache will hold. If not specified defaults to 1024. + google.protobuf.UInt32Value cache_size = 7 [(validate.rules).uint32 = {gt: 0}]; } diff --git a/envoy/certificate_provider/certificate_provider.h b/envoy/certificate_provider/certificate_provider.h index 152bd55313..97b9474261 100644 --- a/envoy/certificate_provider/certificate_provider.h +++ b/envoy/certificate_provider/certificate_provider.h @@ -68,7 +68,8 @@ class CertificateProvider { * should provide at least one tls certificate. * @return Identity certificates used for handshake */ - virtual std::vector> + virtual std::vector< + std::reference_wrapper> tlsCertificates(const std::string& cert_name) const PURE; /** diff --git a/source/extensions/certificate_providers/local_certificate/BUILD b/source/extensions/certificate_providers/local_certificate/BUILD index 077366ebbe..c5ce458119 100644 --- a/source/extensions/certificate_providers/local_certificate/BUILD +++ b/source/extensions/certificate_providers/local_certificate/BUILD @@ -13,6 +13,9 @@ envoy_cc_library( name = "local_certificate_provider", srcs = ["local_certificate.cc"], hdrs = ["local_certificate.h"], + external_deps = [ + "simple_lru_cache_lib", + ], deps = [ "//envoy/api:api_interface", "//envoy/certificate_provider:certificate_provider_interface", @@ -20,9 +23,7 @@ envoy_cc_library( "//envoy/server:transport_socket_config_interface", "//source/common/common:callback_impl_lib", "//source/common/common:logger_lib", - "//source/common/config:datasource_lib", - "@envoy_api//envoy/extensions/certificate_providers/local_certificate/v3:pkg_cc_proto", ], ) @@ -37,5 +38,6 @@ envoy_cc_extension( "//envoy/certificate_provider:certificate_provider_interface", "//source/common/common:utility_lib", "//source/common/protobuf:message_validator_lib", + "@envoy_api//envoy/extensions/certificate_providers/local_certificate/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/certificate_providers/local_certificate/config.cc b/source/extensions/certificate_providers/local_certificate/config.cc index 8ea101f396..2866511adf 100644 --- a/source/extensions/certificate_providers/local_certificate/config.cc +++ b/source/extensions/certificate_providers/local_certificate/config.cc @@ -1,23 +1,23 @@ -#include "envoy/certificate_provider/certificate_provider.h" +#include "source/extensions/certificate_providers/local_certificate/config.h" +#include "envoy/certificate_provider/certificate_provider.h" #include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" -#include "source/extensions/certificate_providers/local_certificate/config.h" -#include "source/extensions/certificate_providers/local_certificate/local_certificate.h" - #include "source/common/config/utility.h" #include "source/common/protobuf/message_validator_impl.h" +#include "source/extensions/certificate_providers/local_certificate/local_certificate.h" namespace Envoy { namespace Extensions { namespace CertificateProviders { namespace LocalCertificate { -CertificateProvider::CertificateProviderSharedPtr LocalCertificateFactory::createCertificateProviderInstance( +CertificateProvider::CertificateProviderSharedPtr +LocalCertificateFactory::createCertificateProviderInstance( const envoy::config::core::v3::TypedExtensionConfig& config, Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) { - auto message = - std::make_unique(); + auto message = std::make_unique< + envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate>(); Config::Utility::translateOpaqueConfig(config.typed_config(), ProtobufMessage::getStrictValidationVisitor(), *message); return std::make_shared(*message, factory_context, api); @@ -25,8 +25,7 @@ CertificateProvider::CertificateProviderSharedPtr LocalCertificateFactory::creat ProtobufTypes::MessagePtr LocalCertificateFactory::createEmptyConfigProto() { return ProtobufTypes::MessagePtr{ - new envoy::extensions::certificate_providers::local_certificate::v3:: - LocalCertificate()}; + new envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate()}; } REGISTER_FACTORY(LocalCertificateFactory, CertificateProvider::CertificateProviderFactory); diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 2de721e478..b0f4765c73 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -10,10 +10,11 @@ namespace Extensions { namespace CertificateProviders { namespace LocalCertificate { -Provider::Provider(const envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate& config, - Server::Configuration::TransportSocketFactoryContext& factory_context, - Api::Api& api) - : main_thread_dispatcher_(factory_context.mainThreadDispatcher()) { +Provider::Provider( + const envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate& config, + Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) + : main_thread_dispatcher_(factory_context.mainThreadDispatcher()), + cache_size_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, cache_size, CacheDefaultSize)) { ca_cert_ = Config::DataSource::read(config.rootca_cert(), true, api); ca_key_ = Config::DataSource::read(config.rootca_key(), true, api); default_identity_cert_ = Config::DataSource::read(config.default_identity_cert(), true, api); @@ -26,13 +27,17 @@ Provider::Provider(const envoy::extensions::certificate_providers::local_certifi } // Generate TLSCertificate - envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); + envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = + new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); tls_certificate->mutable_certificate_chain()->set_inline_string(default_identity_cert_); tls_certificate->mutable_private_key()->set_inline_string(default_identity_key_); // Update certificates_ map { absl::WriterMutexLock writer_lock{&certificates_lock_}; - certificates_.try_emplace("default", tls_certificate); + if (cache_size_ != CacheDefaultSize) { + certificates_.setMaxSize(cache_size_); + } + certificates_.insert("default", tls_certificate); } } @@ -44,14 +49,12 @@ Envoy::CertificateProvider::CertificateProvider::Capabilities Provider::capabili const std::string Provider::trustedCA(const std::string&) const { return ""; } -std::vector> +std::vector< + std::reference_wrapper> Provider::tlsCertificates(const std::string&) const { - std::vector> result; + absl::ReaderMutexLock reader_lock{&certificates_lock_}; - for (auto [_, value] : certificates_) { - result.push_back(*value); - } - return result; + return certificates_.getCertificates(); } Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateCallback( @@ -59,8 +62,8 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC Event::Dispatcher& thread_local_dispatcher, Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) { - auto handle = std::make_unique( - on_demand_update_callbacks_, sni, callback); + auto handle = + std::make_unique(on_demand_update_callbacks_, sni, callback); // TODO: we need to improve this cache_hit check // It is possible that two SNIs use the same cert, so they share the same SANs. @@ -68,18 +71,21 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC // since we do not allow duplicated SANs. // We need to align this cache_hit with current transport socket behavior bool cache_hit = [&]() { - absl::ReaderMutexLock reader_lock{&certificates_lock_}; - auto it = certificates_.find(sni); - return it != certificates_.end() ? true : false; + // Lookup may change the content of LRU cache, a write lock is needed to prevent + // multiple threads to access the cache. + absl::ReaderMutexLock writer_lock{&certificates_lock_}; + return certificates_.is_in_cache(sni); }(); if (cache_hit) { + ENVOY_LOG(debug, "Cache hit for {}", sni); // Cache hit, run on-demand update callback directly runOnDemandUpdateCallback(sni, thread_local_dispatcher, true); } else { // Cache miss, generate self-signed cert main_thread_dispatcher_.post([sni, metadata, &thread_local_dispatcher, this] { - signCertificate(sni, metadata, thread_local_dispatcher); }); + signCertificate(sni, metadata, thread_local_dispatcher); + }); } return handle; } @@ -161,9 +167,8 @@ void Provider::signCertificate(const std::string sni, // Update certificates_ map { absl::WriterMutexLock writer_lock{&certificates_lock_}; - certificates_.try_emplace(sni, tls_certificate); + certificates_.insert(sni, tls_certificate); } - runAddUpdateCallback(); runOnDemandUpdateCallback(sni, thread_local_dispatcher, false); } diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index 3d9743dc46..f202cb835b 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -6,31 +6,86 @@ #include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" #include "envoy/server/transport_socket_config.h" -#include "envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.pb.h" - #include "source/common/common/callback_impl.h" #include "source/common/common/logger.h" +#include "simple_lru_cache/simple_lru_cache_inl.h" + namespace Envoy { namespace Extensions { namespace CertificateProviders { namespace LocalCertificate { +using ::google::simple_lru_cache::SimpleLRUCache; +using TlsCertificate = envoy::extensions::transport_sockets::tls::v3::TlsCertificate; + +// Default cert cache size +constexpr int CacheDefaultSize = 1024; + +// A cache implementation wraps a SimpleLRUCache for mimic certs +class CertCacheImpl { +public: + CertCacheImpl() { + cert_cache_ = + std::make_unique>(CacheDefaultSize); + } + + ~CertCacheImpl() { + if (cert_cache_) { + cert_cache_->clear(); + } + } + + bool is_in_cache(const std::string& host) { + if (!cert_cache_) { + return false; + } + SimpleLRUCache::ScopedLookup lookup(cert_cache_.get(), host); + if (lookup.found()) { + ASSERT(lookup.value() != nullptr); + return true; + } else { + cert_cache_->remove(host); + } + return false; + } + + void insert(const std::string& host, const TlsCertificate* cert) { + if (cert_cache_) { + cert_cache_->insert(host, cert, 1); + } + } + + void setMaxSize(const uint32_t total_size) { cert_cache_->setMaxSize(total_size); } + + std::vector> getCertificates() const { + std::vector> certs; + for (auto [_, value] : *cert_cache_) { + certs.push_back(*value); + } + return certs; + } + +private: + std::unique_ptr> cert_cache_; +}; + // Local cert provider class Provider : public CertificateProvider::CertificateProvider, Logger::Loggable { public: - Provider(const envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate& config, + Provider(const envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate& + config, Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api); // CertificateProvider::CertificateProvider Envoy::CertificateProvider::CertificateProvider::Capabilities capabilities() const override; const std::string trustedCA(const std::string& cert_name) const override; - std::vector> + std::vector< + std::reference_wrapper> tlsCertificates(const std::string& cert_name) const override; Envoy::CertificateProvider::OnDemandUpdateHandlePtr addOnDemandUpdateCallback( - const std::string cert_name, - Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + const std::string cert_name, Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, Event::Dispatcher& thread_local_dispatcher, ::Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) override; Common::CallbackHandlePtr addUpdateCallback(const std::string& cert_name, @@ -49,9 +104,7 @@ class Provider : public CertificateProvider::CertificateProvider, }; mutable absl::Mutex certificates_lock_; - absl::flat_hash_map - certificates_ ABSL_GUARDED_BY(certificates_lock_); + CertCacheImpl certificates_ ABSL_GUARDED_BY(certificates_lock_); void runAddUpdateCallback(); void runOnDemandUpdateCallback(const std::string& host, @@ -79,6 +132,7 @@ class Provider : public CertificateProvider::CertificateProvider, Common::CallbackManager<> update_callback_manager_; absl::flat_hash_map> on_demand_update_callbacks_; + const uint32_t cache_size_; }; } // namespace LocalCertificate } // namespace CertificateProviders diff --git a/tls_bumping/splicing_bumping_all.yaml b/tls_bumping/splicing_bumping_all.yaml index 0bfcc92b7a..505cb58475 100644 --- a/tls_bumping/splicing_bumping_all.yaml +++ b/tls_bumping/splicing_bumping_all.yaml @@ -368,3 +368,4 @@ certificate_provider_instances: q5sGxGrC1RECGB5Zwx2S2ZY= -----END PRIVATE KEY----- expiration_time: "2023-10-30T08:42:46Z" # UTC time + cache_size: 3 From 6ce4aa3fb85c835e0c54aadf87ede50dd253fa6f Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Thu, 1 Dec 2022 08:33:15 +0000 Subject: [PATCH 20/35] remove useless files Signed-off-by: Luyao Zhong --- baidu.com.key | 28 -------------- baidu.com.pem | 24 ------------ tls_bumping/bumping_w_connect.yaml | 58 +++++++++++++++++++++++++++-- tls_bumping/bumping_wo_connect.yaml | 57 +++++++++++++++++++++++++++- 4 files changed, 110 insertions(+), 57 deletions(-) delete mode 100644 baidu.com.key delete mode 100644 baidu.com.pem diff --git a/baidu.com.key b/baidu.com.key deleted file mode 100644 index 0405de5ca1..0000000000 --- a/baidu.com.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCgCMNZWCDIiO0G -87xBMU/Y0lZDvZhSZg8NA5HQbWumPMNhCvPQyI8dqRvDh0TChWn4ePx24/atl6ay -ICe3SPsE3n850C/gf0zISK9aegl8Ov8rZIH8aw6lqEa1UZcIcFpgQYP/IveshpRf -seyvQPXeMSlwnpNuhAjU2i2HzSMx9Pg6tPyfnV14jqgfrvoPy4K0HMmiDr7mAdcR -7dHMSd4pvH8Akuj97fQn+UDEyi5MMEzcFzj13Or3/f3XCwFy9VuGn6ydpI1ZissU -xZQjfTCcM6+yh2+pO0nzzsHOc3tcm1zsoLEjJB9a+BIEbJ9xQzPbQtxB3hYgZa8H -dYAbN/i1AgMBAAECggEAF+93mENQE+421VVbEL0ZXiqHcHZI8/oDwkSIaI4VDbYE -2tzSfqWgkiUa2/G9XQKrSNh/miCaYnhOHFIm3ojx0lac3L7Aimk/yKzmXTfnd0cz -pv/PWTuB41D50mCzoDV9ruvdnClUtd8jDfUEm2mx3FuqAQgWsO7ai0teh/Mskmn+ -p+esD8j9z87NZbcC47NAxlS5zWhJueTwsDcGh2gM4akZD7ry1C66enSENXJjB223 -z6Cwt200cQfhMah4RqzlC8TomvmKh1sQyXG/kVM6m/DGMom3cno8muiJXJMPEMag -zxD4J3u05hItr7odFYxVYAssnjzGYUQ0ReTJL7OZAQKBgQDSBz150+CFXYKZQb3m -SdU/OPCaOIlLpqLuLT6xhG3gR0KNCEVCZGMyf9P/puQypFAOUNeU+UeKqZEiXD9o -ONwqHZnODcPx+xDM96KQHuIE+l5uyzijnia5t7I6JKgNkoy2LksP/CMLYtLjXtz9 -KdIKKBivCIpuQZG00BVM2LbsIQKBgQDDECXeo0fm+LaSRljnMus60jU33uOmsYmR -5KOq2MtSTYE9N8SFdCdjws+tZjosFOvpk2+v5JwriypOF/oWt0X3DBlyi28CqjoN -4A+cQLT50ogMN5gVAmruOf4EoXp5ppkimU5jqSzx3b0jUHoKO+naIyP+6FPL29Xe -r7IgptNaFQKBgQCwQpM9IqtSQV/Q7nNISL5GCKsjQj821en2qNHseI8dobAgW8iE -J2t4ff6UlqL8PRlSalYCGBIKNLQweepctRtP3PevDa5b7a/z0/8BpGladCO8J6Co -75jgU2GnmgvPGCYu3jrwd1GRxKXnWz1q2SYEkBHnEuvaC/0UGW1GyLj7AQKBgD2E -0Ty3KE0a8ZGOaCl6cJ+bfjdBr3B6G5YMkuWl+/HYCcOB3BPuvyGCZjccv4n5izGe -UyIZKnu9Jzl77F7PrFwuz7PFb9xaTXbkzGeOLMWBdXDM88Tkf07ksU1KlDLbrP4x -dXyO9WLOeQEzN1oU7TrjzE1vfkYT7g6OQNJ1asDFAoGBAMDbDovgu3WGAHgBiaNu -Cy0LhT1ZVvLZqrzEQTk0jkpCGdREiFuWJgJIAF4tnEeBxJXucVYRi2Z/v4a1d7Vr -s9ztrhoRGxTDv4l9InGqW17KhLlSrVpxgAZpnM2LtdohpG1WhAJs7n7nNUa8FNTR -wrK+f0mwOf7+jtoDLZvpZ509 ------END PRIVATE KEY----- diff --git a/baidu.com.pem b/baidu.com.pem deleted file mode 100644 index 9d8a5dc3f8..0000000000 --- a/baidu.com.pem +++ /dev/null @@ -1,24 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID/zCCAeegAwIBAgIBADANBgkqhkiG9w0BAQsFADA8MRQwEgYDVQQLDAtNeVJv -b3RDQSBSMjERMA8GA1UECgwITXlSb290Q0ExETAPBgNVBAMMCE15Um9vdENBMB4X -DTIyMDcxMjAyNDExNVoXDTIzMDcxMjAyNDExNVowFDESMBAGA1UEAwwJYmFpZHUu -Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoAjDWVggyIjtBvO8 -QTFP2NJWQ72YUmYPDQOR0G1rpjzDYQrz0MiPHakbw4dEwoVp+Hj8duP2rZemsiAn -t0j7BN5/OdAv4H9MyEivWnoJfDr/K2SB/GsOpahGtVGXCHBaYEGD/yL3rIaUX7Hs -r0D13jEpcJ6TboQI1Noth80jMfT4OrT8n51deI6oH676D8uCtBzJog6+5gHXEe3R -zEneKbx/AJLo/e30J/lAxMouTDBM3Bc49dzq9/391wsBcvVbhp+snaSNWYrLFMWU -I30wnDOvsodvqTtJ887BznN7XJtc7KCxIyQfWvgSBGyfcUMz20LcQd4WIGWvB3WA -Gzf4tQIDAQABozQwMjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAYBgNVHREEETAP -gg13d3cuYmFpZHUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQAhN+RueMYTf7MmhmmO -LZ5b3sXRfJhsnWbFWEEu0FFqT5t6fqiulCLSdSPcCfnqjG9/S6He3eGJ0DyhkEFc -fjsniHycaaKYqyR3nxMHcOeIOHPMECxfQ8WDWhxpvkxNPp47Lmc+qWPwFz9fv8qn -j8rpAgkIs/06y342xQWhcAKKxlbcmhji+i9Ns7f5GPv7OAhoNpKhljhVaUpKkUXo -SFjWza1haK79sSM4ifIZ7ii8zLIazHZhP1uuZsLDFVPKFH1ryBn/RvSDq8ZKXRQ6 -zqdcLD431YEeYcUbp4ayXKz2djScqYfpM7j2fbzlXnv91RqSy9aM5eJvMKPQS0GA -hc8qF5x+0oGJY6Aaw0i62l/ZgbaEnvaFo0ReqGJyakmQ3ipHxtYLA1zrSFD7+0Bm -ytE971P6zD+R+7hv9kR4NgidPsCOC6gaepXm1/VmS6EHQMzrUhnkEtx6xBBi8o+q -fHEbffxhpHgrurKX7MbEPWVel+EXZ9EhrTPSjOf1+paqKy7Sl8Sh9wPaJL94XdnA -YPmg1KCBHL49eqOhpqR5pW8RWmEbz227PA/gbnPCU06xRSXSf4vcKTA94nblMkb0 -z8Duegu0pRY/AIn7vv+ea8Zwf6IH3eC/iBG3AGM7Y/yLNSCU4qNef4wV8tyrVrhf -5fXXzSLQHtjH5TwN03Lk2VrQvw== ------END CERTIFICATE----- diff --git a/tls_bumping/bumping_w_connect.yaml b/tls_bumping/bumping_w_connect.yaml index ec45c775d0..66ad4208d4 100644 --- a/tls_bumping/bumping_w_connect.yaml +++ b/tls_bumping/bumping_w_connect.yaml @@ -139,8 +139,60 @@ certificate_provider_instances: filename: root-ca.pem rootca_key: filename: root-ca.key + # The following self-signed certificate pair is generated using: + # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem -days 3650 -nodes -subj '/CN=front-envoy' + # + # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy + # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. + # + # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via + # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. default_identity_cert: - filename: baidu.com.pem + inline_string: | + -----BEGIN CERTIFICATE----- + MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm + cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS + BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC + AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou + oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ + VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt + ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh + 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX + 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg + vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N + v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ + Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 + zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe + 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I + m+/R4XnmL4cKQ+5Z + -----END CERTIFICATE----- default_identity_key: - filename: baidu.com.key - \ No newline at end of file + inline_string: | + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD + tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 + VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg + Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj + zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ + oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw + tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB + NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx + lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx + DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 + 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO + eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL + xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 + e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO + mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR + nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB + xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO + EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R + JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 + XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg + +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK + 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 + DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 + o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 + ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU + q5sGxGrC1RECGB5Zwx2S2ZY= + -----END PRIVATE KEY----- diff --git a/tls_bumping/bumping_wo_connect.yaml b/tls_bumping/bumping_wo_connect.yaml index 68fdd28dac..470ed1c735 100644 --- a/tls_bumping/bumping_wo_connect.yaml +++ b/tls_bumping/bumping_wo_connect.yaml @@ -89,7 +89,60 @@ certificate_provider_instances: filename: root-ca.pem rootca_key: filename: root-ca.key + # The following self-signed certificate pair is generated using: + # $ openssl req -x509 -newkey rsa:2048 -keyout a/front-proxy-key.pem -out a/front-proxy-crt.pem -days 3650 -nodes -subj '/CN=front-envoy' + # + # Instead of feeding it as an inline_string, certificate pair can also be fed to Envoy + # via filename. Reference: https://envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-datasource. + # + # Or in a dynamic configuration scenario, certificate pair can be fetched remotely via + # Secret Discovery Service (SDS). Reference: https://envoyproxy.io/docs/envoy/latest/configuration/security/secret. default_identity_cert: - filename: baidu.com.pem + inline_string: | + -----BEGIN CERTIFICATE----- + MIICqDCCAZACCQCquzpHNpqBcDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtm + cm9udC1lbnZveTAeFw0yMDA3MDgwMTMxNDZaFw0zMDA3MDYwMTMxNDZaMBYxFDAS + BgNVBAMMC2Zyb250LWVudm95MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC + AQEAthnYkqVQBX+Wg7aQWyCCb87hBce1hAFhbRM8Y9dQTqxoMXZiA2n8G089hUou + oQpEdJgitXVS6YMFPFUUWfwcqxYAynLK4X5im26Yfa1eO8La8sZUS+4Bjao1gF5/ + VJxSEo2yZ7fFBo8M4E44ZehIIocipCRS+YZehFs6dmHoq/MGvh2eAHIa+O9xssPt + ofFcQMR8rwBHVbKy484O10tNCouX4yUkyQXqCRy6HRu7kSjOjNKSGtjfG+h5M8bh + 10W7ZrsJ1hWhzBulSaMZaUY3vh5ngpws1JATQVSK1Jm/dmMRciwlTK7KfzgxHlSX + 58ENpS7yPTISkEICcLbXkkKGEQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCmj6Hg + vwOxWz0xu+6fSfRL6PGJUGq6wghCfUvjfwZ7zppDUqU47fk+yqPIOzuGZMdAqi7N + v1DXkeO4A3hnMD22Rlqt25vfogAaZVToBeQxCPd/ALBLFrvLUFYuSlS3zXSBpQqQ + Ny2IKFYsMllz5RSROONHBjaJOn5OwqenJ91MPmTAG7ujXKN6INSBM0PjX9Jy4Xb9 + zT+I85jRDQHnTFce1WICBDCYidTIvJtdSSokGSuy4/xyxAAc/BpZAfOjBQ4G1QRe + 9XwOi790LyNUYFJVyeOvNJwveloWuPLHb9idmY5YABwikUY6QNcXwyHTbRCkPB2I + m+/R4XnmL4cKQ+5Z + -----END CERTIFICATE----- default_identity_key: - filename: baidu.com.key + inline_string: | + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2GdiSpVAFf5aD + tpBbIIJvzuEFx7WEAWFtEzxj11BOrGgxdmIDafwbTz2FSi6hCkR0mCK1dVLpgwU8 + VRRZ/ByrFgDKcsrhfmKbbph9rV47wtryxlRL7gGNqjWAXn9UnFISjbJnt8UGjwzg + Tjhl6EgihyKkJFL5hl6EWzp2Yeir8wa+HZ4Achr473Gyw+2h8VxAxHyvAEdVsrLj + zg7XS00Ki5fjJSTJBeoJHLodG7uRKM6M0pIa2N8b6HkzxuHXRbtmuwnWFaHMG6VJ + oxlpRje+HmeCnCzUkBNBVIrUmb92YxFyLCVMrsp/ODEeVJfnwQ2lLvI9MhKQQgJw + tteSQoYRAgMBAAECggEAeDGdEkYNCGQLe8pvg8Z0ccoSGpeTxpqGrNEKhjfi6NrB + NwyVav10iq4FxEmPd3nobzDPkAftfvWc6hKaCT7vyTkPspCMOsQJ39/ixOk+jqFx + lNa1YxyoZ9IV2DIHR1iaj2Z5gB367PZUoGTgstrbafbaNY9IOSyojCIO935ubbcx + DWwL24XAf51ez6sXnI8V5tXmrFlNXhbhJdH8iIxNyM45HrnlUlOk0lCK4gmLJjy9 + 10IS2H2Wh3M5zsTpihH1JvM56oAH1ahrhMXs/rVFXXkg50yD1KV+HQiEbglYKUxO + eMYtfaY9i2CuLwhDnWp3oxP3HfgQQhD09OEN3e0IlQKBgQDZ/3poG9TiMZSjfKqL + xnCABMXGVQsfFWNC8THoW6RRx5Rqi8q08yJrmhCu32YKvccsOljDQJQQJdQO1g09 + e/adJmCnTrqxNtjPkX9txV23Lp6Ak7emjiQ5ICu7iWxrcO3zf7hmKtj7z+av8sjO + mDI7NkX5vnlE74nztBEjp3eC0wKBgQDV2GeJV028RW3b/QyP3Gwmax2+cKLR9PKR + nJnmO5bxAT0nQ3xuJEAqMIss/Rfb/macWc2N/6CWJCRT6a2vgy6xBW+bqG6RdQMB + xEZXFZl+sSKhXPkc5Wjb4lQ14YWyRPrTjMlwez3k4UolIJhJmwl+D7OkMRrOUERO + EtUvc7odCwKBgBi+nhdZKWXveM7B5N3uzXBKmmRz3MpPdC/yDtcwJ8u8msUpTv4R + JxQNrd0bsIqBli0YBmFLYEMg+BwjAee7vXeDFq+HCTv6XMva2RsNryCO4yD3I359 + XfE6DJzB8ZOUgv4Dvluie3TB2Y6ZQV/p+LGt7G13yG4hvofyJYvlg3RPAoGAcjDg + +OH5zLN2eqah8qBN0CYa9/rFt0AJ19+7/smLTJ7QvQq4g0gwS1couplcCEnNGWiK + 72y1n/ckvvplmPeAE19HveMvR9UoCeV5ej86fACy8V/oVpnaaLBvL2aCMjPLjPP9 + DWeCIZp8MV86cvOrGfngf6kJG2qZTueXl4NAuwkCgYEArKkhlZVXjwBoVvtHYmN2 + o+F6cGMlRJTLhNc391WApsgDZfTZSdeJsBsvvzS/Nc0burrufJg0wYioTlpReSy4 + ohhtprnQQAddfjHP7rh2LGt+irFzhdXXQ1ybGaGM9D764KUNCXLuwdly0vzXU4HU + q5sGxGrC1RECGB5Zwx2S2ZY= + -----END PRIVATE KEY----- From b6627abf26b7d19ce158ce7f36d381f977fe03d4 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Thu, 1 Dec 2022 08:40:48 +0000 Subject: [PATCH 21/35] remove useless docs/notes due to rebase Signed-off-by: Luyao Zhong --- changelogs/current.yaml | 105 ---------------------------------------- 1 file changed, 105 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index e8eb387a1a..c3122e048c 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -19,110 +19,5 @@ new_features: New config option :ref:`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: header_formatters - change: | - all access log formatters can be used as custom request/response headers. Custom header's syntax is parsed using access logger's parser and - header values are obtained using access log's substitution formatters. This feature can be reversed by setting runtime guard - ``envoy.reloadable_features.unified_header_formatter`` to false. -- area: http - change: | - made the :ref:`admission control ` work as an upstream filter. - change: | -- area: http - change: | - added default-false ``envoy.reloadable_features.http1_use_balsa_parser`` for experimental BalsaParser. - change: | - added ``envoy.reloadable_features.allow_upstream_filters`` for experimental upstream filters. -- area: dns_resolver - change: | - added DNS stats for c-ares DNS resolver. Detailed documentation is available :ref:`here `. -- area: gzip - change: | - added support for :ref:`max_inflate_ratio`. -- area: access_log - change: | - added downstream handshake timing to connection streamInfo. Can be accessed by custom access loggers. -- area: build - change: | - official released binary is now built on Ubuntu 20.04, requires glibc >= 2.30. -- area: listener - change: | - added multiple listening addresses in single listener. :ref:`listener additional addresses`. -- area: load balancer - change: | - added a new field to subset load balancer config: :ref:`metadata_fallback_policy`. -- area: thrift - change: | - added stats for downstream connection close to detect SR drop. -- area: compression - change: | - added support for :ref:`choose_first`. -- area: cors - change: | - added support for cors PNA. This behavioral change can be temporarily reverted by setting runtime guard ``envoy_reloadable_features_cors_private_network_access`` to false. More details refer to https://developer.chrome.com/blog/private-network-access-preflight. -- area: upstream - change: | - added a filter state object to control the destination address in :ref:`ORIGINAL_DST clusters `. -- area: upstream - change: | - added a new field :ref:`additional_source_addresses ` to the BindConfig, it enables to specify multiple source addresses, and the source address selection is based on target host's address' version. -- area: admin - change: | - added new :ref:`/heap_dump ` endpoint to dump heap profile of Envoy. -- area: health check - change: | - added :ref:`method ` support to configure http health check http method. -- area: access_log - change: | - updated command operator ``%GRPC_STATUS%`` to suppoprt the snake case. -- area: listener - change: | - expose the implementation of :ref:`internal listener ` in xDS API. -- area: ratelimit - change: | - add support for :ref:`adding response headers ` to rate-limited responses. -- area: access_log - change: | - added support for number values in substitution format string in json_format. -- area: http - change: | - added the expected :ref:`receive ` payload check for HTTP health check. - Added :ref:`response_buffer_size ` to configure the maximum HTTP health check response buffer size. -- area: lua - change: | - added new headers method "setHttp1ReasonPhrase" for lua filter, please see :ref:`lua header wrapper `. -- area: lua - change: | - added stats for lua filter, please see :ref:`lua filter stats `. -- area: subset load balancer - change: | - added multiple keys or multiple selectors support for :ref:`single host per subset mode `. -- area: listener - change: | - allow network filters other than HTTP Connection Manager to be created for QUIC listeners. -- area: lua - change: | - added an alternative function signature to ``httpCall()`` with ``options`` as an argument. This allows to skip sampling the produced trace span - by setting ``{["trace_sampled"] = false}`` as the ``options``. And this allows to return multiple header values for same header name by setting - ``{["return_duplicate_headers"] = true}`` as the ``options``. -- area: cluster - change: | - added support to override original destination port via setting :ref:`upstream_port_override `. -- area: generic_proxy - change: | - added an new network filter :ref:`generic_proxy filter `. -- area: redis - change: | - added support for quit command to the redis proxy. -- area: access_log - change: | - log ``duration``, ``upstream_request_attempt_count``, ``connection_termination_details`` and tls ``ja3`` field in the grpc access log - and also log the tls ``sni`` and ``ja3`` field in the grpc access log when envoy is configured as a tls forward proxy. -- area: grpc_json_transcoder - change: | - added support for parsing enum value case insensitively enabled by the config :ref:`case_insensitive_enum_parsing `. -- area: generic_proxy - change: | - added an new network filter :ref:`generic_proxy filter `. deprecated: From ea3ed5ee14842a4c0c398ce5b74574d3123c345c Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Thu, 1 Dec 2022 12:59:12 +0000 Subject: [PATCH 22/35] add mocks to pass test compiling Signed-off-by: Luyao Zhong --- envoy/event/dispatcher.h | 9 ++-- source/common/event/dispatcher_impl.cc | 15 +++--- source/common/event/dispatcher_impl.h | 9 ++-- source/common/network/connection_impl.cc | 15 +++--- source/common/network/connection_impl.h | 4 +- .../network/transport_socket_options_impl.cc | 6 +-- .../network/transport_socket_options_impl.h | 3 +- .../extensions/filters/network/bumping/BUILD | 2 +- .../filters/network/bumping/bumping.cc | 39 +++++++-------- .../transport_sockets/tls/context_impl.cc | 8 +-- source/server/active_stream_listener_base.cc | 5 +- test/common/http/codec_client_test.cc | 4 +- test/common/network/connection_impl_test.cc | 30 +++++++---- test/common/network/listener_impl_test.cc | 24 ++++++--- test/common/secret/sds_api_test.cc | 6 ++- .../common/secret/secret_manager_impl_test.cc | 10 ++-- test/common/upstream/test_cluster_manager.h | 4 ++ test/config_test/config_test.cc | 3 +- .../dns_resolver/cares/dns_impl_test.cc | 4 +- .../cert_validator/default_validator_test.cc | 8 +++ .../tls/cert_validator/test_common.h | 9 ++++ .../tls/context_impl_test.cc | 1 - .../transport_sockets/tls/ssl_socket_test.cc | 50 +++++++++++-------- test/mocks/event/mocks.h | 4 +- test/mocks/event/wrapped_dispatcher.h | 12 ++--- test/mocks/server/BUILD | 1 + test/mocks/server/instance.cc | 2 + test/mocks/server/instance.h | 3 ++ .../transport_socket_factory_context.cc | 4 +- .../server/transport_socket_factory_context.h | 3 ++ test/mocks/ssl/mocks.cc | 2 + test/mocks/ssl/mocks.h | 9 ++++ test/mocks/upstream/BUILD | 1 + .../mocks/upstream/cluster_manager_factory.cc | 5 ++ test/mocks/upstream/cluster_manager_factory.h | 21 +++++++- .../config_validation/cluster_manager_test.cc | 4 +- test/server/configuration_impl_test.cc | 3 +- 37 files changed, 225 insertions(+), 117 deletions(-) diff --git a/envoy/event/dispatcher.h b/envoy/event/dispatcher.h index d17dd634e6..8939def35c 100644 --- a/envoy/event/dispatcher.h +++ b/envoy/event/dispatcher.h @@ -197,11 +197,10 @@ class Dispatcher : public DispatcherBase, public ScopeTracker { * @param stream_info info object for the server connection * @return Network::ConnectionPtr a server connection that is owned by the caller. */ - virtual Network::ServerConnectionPtr - createServerConnection(Network::ConnectionSocketPtr&& socket, - Network::TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info, - const Network::DownstreamTransportSocketFactory& transport_socket_factory) PURE; + virtual Network::ServerConnectionPtr createServerConnection( + Network::ConnectionSocketPtr&& socket, Network::TransportSocketPtr&& transport_socket, + StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory) PURE; /** * Creates an instance of Envoy's Network::ClientConnection. Does NOT initiate the connection; diff --git a/source/common/event/dispatcher_impl.cc b/source/common/event/dispatcher_impl.cc index dbfab31089..8aa3e686e8 100644 --- a/source/common/event/dispatcher_impl.cc +++ b/source/common/event/dispatcher_impl.cc @@ -146,15 +146,14 @@ void DispatcherImpl::clearDeferredDeleteList() { deferred_deleting_ = false; } -Network::ServerConnectionPtr -DispatcherImpl::createServerConnection(Network::ConnectionSocketPtr&& socket, - Network::TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info, - const Network::DownstreamTransportSocketFactory& transport_socket_factory) { +Network::ServerConnectionPtr DispatcherImpl::createServerConnection( + Network::ConnectionSocketPtr&& socket, Network::TransportSocketPtr&& transport_socket, + StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory) { ASSERT(isThreadSafe()); - return std::make_unique( - *this, std::move(socket), std::move(transport_socket), stream_info, - transport_socket_factory, true); + return std::make_unique(*this, std::move(socket), + std::move(transport_socket), stream_info, + transport_socket_factory, true); } Network::ClientConnectionPtr DispatcherImpl::createClientConnection( diff --git a/source/common/event/dispatcher_impl.h b/source/common/event/dispatcher_impl.h index 9de8017077..9cd69183fe 100644 --- a/source/common/event/dispatcher_impl.h +++ b/source/common/event/dispatcher_impl.h @@ -62,11 +62,10 @@ class DispatcherImpl : Logger::Loggable, TimeSource& timeSource() override { return time_source_; } void initializeStats(Stats::Scope& scope, const absl::optional& prefix) override; void clearDeferredDeleteList() override; - Network::ServerConnectionPtr - createServerConnection(Network::ConnectionSocketPtr&& socket, - Network::TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info, - const Network::DownstreamTransportSocketFactory& transport_socket_factory) override; + Network::ServerConnectionPtr createServerConnection( + Network::ConnectionSocketPtr&& socket, Network::TransportSocketPtr&& transport_socket, + StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory) override; Network::ClientConnectionPtr createClientConnection( Network::Address::InstanceConstSharedPtr address, Network::Address::InstanceConstSharedPtr source_address, diff --git a/source/common/network/connection_impl.cc b/source/common/network/connection_impl.cc index 841a822559..d0d55cd8dd 100644 --- a/source/common/network/connection_impl.cc +++ b/source/common/network/connection_impl.cc @@ -810,14 +810,13 @@ void ConnectionImpl::dumpState(std::ostream& os, int indent_level) const { DUMP_DETAILS(socket_); } -ServerConnectionImpl::ServerConnectionImpl(Event::Dispatcher& dispatcher, - ConnectionSocketPtr&& socket, - TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info, - const Network::DownstreamTransportSocketFactory& transport_socket_factory, - bool connected) +ServerConnectionImpl::ServerConnectionImpl( + Event::Dispatcher& dispatcher, ConnectionSocketPtr&& socket, + TransportSocketPtr&& transport_socket, StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory, bool connected) : ConnectionImpl(dispatcher, std::move(socket), std::move(transport_socket), stream_info, - connected), transport_socket_factory_(transport_socket_factory) {} + connected), + transport_socket_factory_(transport_socket_factory) {} void ServerConnectionImpl::setTransportSocketConnectTimeout(std::chrono::milliseconds timeout, Stats::Counter& timeout_stat) { @@ -862,7 +861,7 @@ void ServerConnectionImpl::refreshTransportSocket() { transport_socket_->setTransportSocketCallbacks(*this); // Currently setSslConnection doesn't allow swapping connectioninfo if old connection info exists // Revisit this when related issues are discovered. - //socket_->connectionInfoProvider().setSslConnection(transport_socket_->ssl()); + // socket_->connectionInfoProvider().setSslConnection(transport_socket_->ssl()); } ClientConnectionImpl::ClientConnectionImpl( diff --git a/source/common/network/connection_impl.h b/source/common/network/connection_impl.h index 7af1cf782f..b357377e16 100644 --- a/source/common/network/connection_impl.h +++ b/source/common/network/connection_impl.h @@ -239,7 +239,8 @@ class ServerConnectionImpl : public ConnectionImpl, virtual public ServerConnect public: ServerConnectionImpl(Event::Dispatcher& dispatcher, ConnectionSocketPtr&& socket, TransportSocketPtr&& transport_socket, StreamInfo::StreamInfo& stream_info, - const DownstreamTransportSocketFactory& transport_socket_factory, bool connected); + const DownstreamTransportSocketFactory& transport_socket_factory, + bool connected); // ServerConnection impl void setTransportSocketConnectTimeout(std::chrono::milliseconds timeout, @@ -247,6 +248,7 @@ class ServerConnectionImpl : public ConnectionImpl, virtual public ServerConnect void raiseEvent(ConnectionEvent event) override; void refreshTransportSocket(); + private: void onTransportSocketConnectTimeout(); diff --git a/source/common/network/transport_socket_options_impl.cc b/source/common/network/transport_socket_options_impl.cc index 42921a9f90..cdfb542812 100644 --- a/source/common/network/transport_socket_options_impl.cc +++ b/source/common/network/transport_socket_options_impl.cc @@ -69,9 +69,9 @@ TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& fi if (sni.size()) { server_name = sni; needs_transport_socket_options = true; - } - else if (auto typed_data = filter_state.getDataReadOnly(UpstreamServerName::key()); - typed_data != nullptr) { + } else if (auto typed_data = + filter_state.getDataReadOnly(UpstreamServerName::key()); + typed_data != nullptr) { server_name = typed_data->value(); needs_transport_socket_options = true; } diff --git a/source/common/network/transport_socket_options_impl.h b/source/common/network/transport_socket_options_impl.h index 322c514d42..9fc82f66fb 100644 --- a/source/common/network/transport_socket_options_impl.h +++ b/source/common/network/transport_socket_options_impl.h @@ -107,7 +107,8 @@ class TransportSocketOptionsUtility { * nullptr if nothing is in the filter state. */ static TransportSocketOptionsConstSharedPtr - fromFilterState(const StreamInfo::FilterState& stream_info, absl::string_view sni = absl::string_view("")); + fromFilterState(const StreamInfo::FilterState& stream_info, + absl::string_view sni = absl::string_view("")); }; class CommonUpstreamTransportSocketFactory : public UpstreamTransportSocketFactory { diff --git a/source/extensions/filters/network/bumping/BUILD b/source/extensions/filters/network/bumping/BUILD index d74c928582..e7bcf23403 100644 --- a/source/extensions/filters/network/bumping/BUILD +++ b/source/extensions/filters/network/bumping/BUILD @@ -76,4 +76,4 @@ envoy_cc_extension( "//source/extensions/filters/network/common:factory_base_lib", "@envoy_api//envoy/extensions/filters/network/bumping/v3:pkg_cc_proto", ], -) \ No newline at end of file +) diff --git a/source/extensions/filters/network/bumping/bumping.cc b/source/extensions/filters/network/bumping/bumping.cc index dd5ed04bbb..5060a2d256 100644 --- a/source/extensions/filters/network/bumping/bumping.cc +++ b/source/extensions/filters/network/bumping/bumping.cc @@ -64,9 +64,7 @@ BumpingStats Config::generateStats(Stats::Scope& scope) { return {ALL_BUMPING_STATS(POOL_COUNTER(scope))}; } -RouteConstSharedPtr Config::getRoute() { - return default_route_; -} +RouteConstSharedPtr Config::getRoute() { return default_route_; } Filter::Filter(ConfigSharedPtr config, Upstream::ClusterManager& cluster_manager) : config_(config), cluster_manager_(cluster_manager), @@ -80,7 +78,7 @@ Filter::~Filter() { } ASSERT(generic_conn_pool_ == nullptr); - //ASSERT(upstream_ == nullptr); + // ASSERT(upstream_ == nullptr); } void Filter::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) { @@ -146,10 +144,8 @@ Network::FilterStatus Filter::establishUpstreamConnection() { auto& downstream_connection = read_callbacks_->connection(); auto& filter_state = downstream_connection.streamInfo().filterState(); // Set transport socket SNI of upstream with downstream SNI - transport_socket_options_ = - Network::TransportSocketOptionsUtility::fromFilterState( - *filter_state, - downstream_connection.requestedServerName()); + transport_socket_options_ = Network::TransportSocketOptionsUtility::fromFilterState( + *filter_state, downstream_connection.requestedServerName()); if (auto typed_state = filter_state->getDataReadOnly( Network::UpstreamSocketOptionsFilterState::key()); @@ -183,8 +179,9 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { return false; } - generic_conn_pool_ = factory->createGenericConnPool( - cluster, TcpProxy::TunnelingConfigHelperOptConstRef(), this, *upstream_callbacks_); + generic_conn_pool_ = + factory->createGenericConnPool(cluster, TcpProxy::TunnelingConfigHelperOptConstRef(), this, + *upstream_callbacks_, getStreamInfo()); if (generic_conn_pool_) { connecting_ = true; connect_attempts_++; @@ -202,7 +199,7 @@ void Filter::onGenericPoolFailure(ConnectionPool::PoolFailureReason reason, Upstream::HostDescriptionConstSharedPtr host) { generic_conn_pool_.reset(); read_callbacks_->upstreamHost(host); - //TODO fix potential segmentation fault issue here + // TODO fix potential segmentation fault issue here getStreamInfo().upstreamInfo()->setUpstreamHost(host); getStreamInfo().upstreamInfo()->setUpstreamTransportFailureReason(failure_reason); @@ -228,18 +225,18 @@ void Filter::onGenericPoolReady(StreamInfo::StreamInfo*, // Request mimick cert from local certificate provider. requestCertificate(info); - //upstream_ = std::move(upstream); + // upstream_ = std::move(upstream); generic_conn_pool_.reset(); onUpstreamConnection(); } void Filter::requestCertificate(Ssl::ConnectionInfoConstSharedPtr info) { - ENVOY_CONN_LOG(info, "sni: {}", read_callbacks_->connection(), read_callbacks_->connection().requestedServerName()); + ENVOY_CONN_LOG(info, "sni: {}", read_callbacks_->connection(), + read_callbacks_->connection().requestedServerName()); config_->main_dispatcher_.post([this, info]() { this->on_demand_handle_ = config_->tls_certificate_provider_->addOnDemandUpdateCallback( std::string(read_callbacks_->connection().requestedServerName()), - std::make_shared(info), - read_callbacks_->connection().dispatcher(), *this); + std::make_shared(info), read_callbacks_->connection().dispatcher(), *this); }); } @@ -256,7 +253,7 @@ Network::FilterStatus Filter::onData(Buffer::Instance&, bool) { } Network::FilterStatus Filter::onNewConnection() { - //ASSERT(upstream_ == nullptr); + // ASSERT(upstream_ == nullptr); route_ = pickRoute(); return establishUpstreamConnection(); } @@ -272,7 +269,7 @@ void Filter::onUpstreamEvent(Network::ConnectionEvent event) { if (event == Network::ConnectionEvent::RemoteClose || event == Network::ConnectionEvent::LocalClose) { - //upstream_.reset(); + // upstream_.reset(); if (connecting) { if (event == Network::ConnectionEvent::RemoteClose) { @@ -301,10 +298,10 @@ void Filter::onCacheHit(const std::string) const { void Filter::onCacheMiss(const std::string) const { // Recreate transport socket to use newly generate certificate - try{ - dynamic_cast(read_callbacks_->connection()).refreshTransportSocket(); - } - catch(std::bad_cast exp) { + try { + dynamic_cast(read_callbacks_->connection()) + .refreshTransportSocket(); + } catch (std::bad_cast exp) { ENVOY_CONN_LOG(warn, "connection cast failed in bumping filter", read_callbacks_->connection()); } // Re-enable downstream reads and writes diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 7f26f4cd7c..efa1d32909 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -892,10 +892,10 @@ void ServerContextImpl::populateServerNamesMap(TlsContext& ctx, int pkey_id) { auto sn_match = server_names_map_.try_emplace(sn_pattern, pkey_types_map).first; auto pt_match = sn_match->second.find(pkey_id); if (pt_match != sn_match->second.end()) { -// throw EnvoyException(fmt::format( -// "Failed to load certificate chain from {}, at most one " -// "certificate of a given type may be specified for each DNS SAN entry or Subject CN: {}", -// ctx.cert_chain_file_path_, sn_match->first)); + // throw EnvoyException(fmt::format( + // "Failed to load certificate chain from {}, at most one " + // "certificate of a given type may be specified for each DNS SAN entry or Subject + // CN: {}", ctx.cert_chain_file_path_, sn_match->first)); } sn_match->second.emplace(std::pair>(pkey_id, ctx)); }; diff --git a/source/server/active_stream_listener_base.cc b/source/server/active_stream_listener_base.cc index dea305895f..207b2684b2 100644 --- a/source/server/active_stream_listener_base.cc +++ b/source/server/active_stream_listener_base.cc @@ -41,8 +41,9 @@ void ActiveStreamListenerBase::newConnection(Network::ConnectionSocketPtr&& sock } stream_info->setFilterChainName(filter_chain->name()); auto transport_socket = filter_chain->transportSocketFactory().createDownstreamTransportSocket(); - auto server_conn_ptr = dispatcher().createServerConnection( - std::move(socket), std::move(transport_socket), *stream_info, filter_chain->transportSocketFactory()); + auto server_conn_ptr = + dispatcher().createServerConnection(std::move(socket), std::move(transport_socket), + *stream_info, filter_chain->transportSocketFactory()); if (const auto timeout = filter_chain->transportSocketConnectTimeout(); timeout != std::chrono::milliseconds::zero()) { server_conn_ptr->setTransportSocketConnectTimeout( diff --git a/test/common/http/codec_client_test.cc b/test/common/http/codec_client_test.cc index 910f1995cf..3507689d1a 100644 --- a/test/common/http/codec_client_test.cc +++ b/test/common/http/codec_client_test.cc @@ -318,7 +318,8 @@ class CodecNetworkTest : public Event::TestUsingSimulatedTime, EXPECT_CALL(listener_callbacks_, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { upstream_connection_ = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); upstream_connection_->addConnectionCallbacks(upstream_callbacks_); expected_callbacks--; @@ -380,6 +381,7 @@ class CodecNetworkTest : public Event::TestUsingSimulatedTime, NiceMock inner_encoder_; NiceMock outer_decoder_; StreamInfo::StreamInfoImpl stream_info_; + NiceMock transport_factory_; }; // Send a block of data from upstream, and ensure it is received by the codec. diff --git a/test/common/network/connection_impl_test.cc b/test/common/network/connection_impl_test.cc index 5735ec57d0..990e769558 100644 --- a/test/common/network/connection_impl_test.cc +++ b/test/common/network/connection_impl_test.cc @@ -177,7 +177,8 @@ class ConnectionImplTest : public testing::TestWithParam { EXPECT_CALL(listener_callbacks_, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); @@ -295,6 +296,7 @@ class ConnectionImplTest : public testing::TestWithParam { Socket::OptionsSharedPtr socket_options_; StreamInfo::StreamInfoImpl stream_info_; Network::TransportSocketOptionsConstSharedPtr transport_socket_options_ = nullptr; + NiceMock transport_factory_; }; INSTANTIATE_TEST_SUITE_P(IpVersions, ConnectionImplTest, @@ -369,7 +371,8 @@ TEST_P(ConnectionImplTest, CloseDuringConnectCallback) { EXPECT_CALL(listener_callbacks_, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); server_connection_->addReadFilter(add_and_remove_filter); @@ -396,7 +399,8 @@ TEST_P(ConnectionImplTest, UnregisterRegisterDuringConnectCallback) { EXPECT_CALL(listener_callbacks_, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); @@ -467,7 +471,7 @@ TEST_P(ConnectionImplTest, SetServerTransportSocketTimeout) { auto server_connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), - std::move(mocks.transport_socket_), stream_info_, true); + std::move(mocks.transport_socket_), stream_info_, transport_factory_, true); EXPECT_CALL(*mock_timer, enableTimer(std::chrono::milliseconds(3 * 1000), _)); Stats::MockCounter timeout_counter; @@ -488,7 +492,7 @@ TEST_P(ConnectionImplTest, SetServerTransportSocketTimeoutAfterConnect) { auto server_connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), - std::move(mocks.transport_socket_), stream_info_, true); + std::move(mocks.transport_socket_), stream_info_, transport_factory_, true); transport_socket->callbacks_->raiseEvent(ConnectionEvent::Connected); // This should be a no-op. No timer should be created. @@ -512,7 +516,7 @@ TEST_P(ConnectionImplTest, ServerTransportSocketTimeoutDisabledOnConnect) { auto server_connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), - std::move(mocks.transport_socket_), stream_info_, true); + std::move(mocks.transport_socket_), stream_info_, transport_factory_, true); bool timer_destroyed = false; mock_timer->timer_destroyed_ = &timer_destroyed; @@ -555,7 +559,8 @@ TEST_P(ConnectionImplTest, SocketOptions) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { socket->addOption(option); server_connection_ = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); @@ -604,7 +609,8 @@ TEST_P(ConnectionImplTest, SocketOptionsFailureTest) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { socket->addOption(option); server_connection_ = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); @@ -702,7 +708,8 @@ TEST_P(ConnectionImplTest, ConnectionStats) { EXPECT_CALL(listener_callbacks_, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->setConnectionStats(server_connection_stats.toBufferStats()); server_connection_->addReadFilter(read_filter_); @@ -2009,7 +2016,7 @@ TEST_P(ConnectionImplTest, NetworkConnectionDumpsWithoutAllocatingMemory) { auto server_connection = std::make_unique( *mocks.dispatcher_, std::make_unique(std::move(io_handle), nullptr, nullptr), - std::move(mocks.transport_socket_), stream_info_, true); + std::move(mocks.transport_socket_), stream_info_, transport_factory_, true); // Start measuring memory and dump state. Stats::TestUtil::MemoryTest memory_test; @@ -2900,7 +2907,8 @@ class ReadBufferLimitTest : public ConnectionImplTest { EXPECT_CALL(listener_callbacks_, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); server_connection_->setBufferLimits(read_buffer_limit); server_connection_->addReadFilter(read_filter_); EXPECT_EQ("", server_connection_->nextProtocol()); diff --git a/test/common/network/listener_impl_test.cc b/test/common/network/listener_impl_test.cc index 252ac9af95..fb5ca0a942 100644 --- a/test/common/network/listener_impl_test.cc +++ b/test/common/network/listener_impl_test.cc @@ -33,6 +33,7 @@ static void errorCallbackTest(Address::IpVersion version) { Api::ApiPtr api = Api::createApiForTest(); Event::DispatcherPtr dispatcher(api->allocateDispatcher("test_thread")); NiceMock runtime; + NiceMock transport_factory; auto socket = std::make_shared( Network::Test::getCanonicalLoopbackAddress(version)); @@ -49,7 +50,8 @@ static void errorCallbackTest(Address::IpVersion version) { EXPECT_CALL(listener_callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& accepted_socket) -> void { Network::ConnectionPtr conn = dispatcher->createServerConnection( - std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info); + std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info, + transport_factory); client_connection->close(ConnectionCloseType::NoFlush); conn->close(ConnectionCloseType::NoFlush); socket->close(); @@ -89,6 +91,7 @@ TEST_P(TcpListenerImplTest, UseActualDst) { Network::MockTcpListenerCallbacks listener_callbacks1; Random::MockRandomGenerator random_generator; NiceMock runtime; + NiceMock transport_factory; // Do not redirect since use_original_dst is false. Network::TestTcpListenerImpl listener(dispatcherImpl(), random_generator, runtime, socket, @@ -109,7 +112,8 @@ TEST_P(TcpListenerImplTest, UseActualDst) { EXPECT_CALL(listener_callbacks1, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& accepted_socket) -> void { Network::ConnectionPtr conn = dispatcher_->createServerConnection( - std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info); + std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info, + transport_factory); EXPECT_EQ(*conn->connectionInfoProvider().localAddress(), *socket->connectionInfoProvider().localAddress()); client_connection->close(ConnectionCloseType::NoFlush); @@ -123,6 +127,7 @@ TEST_P(TcpListenerImplTest, UseActualDst) { TEST_P(TcpListenerImplTest, GlobalConnectionLimitEnforcement) { // Required to manipulate runtime values when there is no test server. TestScopedRuntime scoped_runtime; + NiceMock transport_factory; scoped_runtime.mergeValues({{"overload.global_downstream_max_connections", "2"}}); auto socket = std::make_shared( @@ -137,7 +142,8 @@ TEST_P(TcpListenerImplTest, GlobalConnectionLimitEnforcement) { EXPECT_CALL(listener_callbacks, onAccept_(_)) .WillRepeatedly(Invoke([&](Network::ConnectionSocketPtr& accepted_socket) -> void { server_connections.emplace_back(dispatcher_->createServerConnection( - std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info)); + std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info, + transport_factory)); dispatcher_->exit(); })); @@ -188,6 +194,7 @@ TEST_P(TcpListenerImplTest, GlobalConnectionLimitEnforcement) { TEST_P(TcpListenerImplTest, GlobalConnectionLimitListenerOptOut) { // Required to manipulate runtime values when there is no test server. TestScopedRuntime scoped_runtime; + NiceMock transport_factory; scoped_runtime.mergeValues({{"overload.global_downstream_max_connections", "1"}}); auto socket = std::make_shared( @@ -202,7 +209,8 @@ TEST_P(TcpListenerImplTest, GlobalConnectionLimitListenerOptOut) { EXPECT_CALL(listener_callbacks, onAccept_(_)) .WillRepeatedly(Invoke([&](Network::ConnectionSocketPtr& accepted_socket) -> void { server_connections.emplace_back(dispatcher_->createServerConnection( - std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info)); + std::move(accepted_socket), Network::Test::createRawBufferSocket(), stream_info, + transport_factory)); dispatcher_->exit(); })); @@ -238,6 +246,7 @@ TEST_P(TcpListenerImplTest, WildcardListenerUseActualDst) { Network::MockTcpListenerCallbacks listener_callbacks; Random::MockRandomGenerator random_generator; NiceMock runtime; + NiceMock transport_factory; // Do not redirect since use_original_dst is false. Network::TestTcpListenerImpl listener(dispatcherImpl(), random_generator, runtime, socket, listener_callbacks, true, false); @@ -254,7 +263,8 @@ TEST_P(TcpListenerImplTest, WildcardListenerUseActualDst) { EXPECT_CALL(listener_callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { Network::ConnectionPtr conn = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info, + transport_factory); EXPECT_EQ(*conn->connectionInfoProvider().localAddress(), *local_dst_address); client_connection->close(ConnectionCloseType::NoFlush); conn->close(ConnectionCloseType::NoFlush); @@ -280,6 +290,7 @@ TEST_P(TcpListenerImplTest, WildcardListenerIpv4Compat) { Network::MockTcpListenerCallbacks listener_callbacks; Random::MockRandomGenerator random_generator; NiceMock runtime; + NiceMock transport_factory; ASSERT_TRUE(socket->connectionInfoProvider().localAddress()->ip()->isAnyAddress()); @@ -302,7 +313,8 @@ TEST_P(TcpListenerImplTest, WildcardListenerIpv4Compat) { EXPECT_CALL(listener_callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { Network::ConnectionPtr conn = dispatcher_->createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info, + transport_factory); EXPECT_EQ(conn->connectionInfoProvider().localAddress()->ip()->version(), conn->connectionInfoProvider().remoteAddress()->ip()->version()); EXPECT_EQ(conn->connectionInfoProvider().localAddress()->asString(), diff --git a/test/common/secret/sds_api_test.cc b/test/common/secret/sds_api_test.cc index d613a4771e..6e60ac6ed7 100644 --- a/test/common/secret/sds_api_test.cc +++ b/test/common/secret/sds_api_test.cc @@ -617,7 +617,8 @@ TEST_F(SdsApiTest, DynamicCertificateValidationContextUpdateSuccess) { initialize(); subscription_factory_.callbacks_->onConfigUpdate(decoded_resources.refvec_, ""); - Ssl::CertificateValidationContextConfigImpl cvc_config(*sds_api.secret(), *api_); + testing::NiceMock ctx; + Ssl::CertificateValidationContextConfigImpl cvc_config(*sds_api.secret(), *api_, ctx); const std::string ca_cert = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(ca_cert)), @@ -693,7 +694,8 @@ TEST_F(SdsApiTest, DefaultCertificateValidationContextTest) { envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext merged_cvc = default_cvc; merged_cvc.MergeFrom(*sds_api.secret()); - Ssl::CertificateValidationContextConfigImpl cvc_config(merged_cvc, *api_); + testing::NiceMock ctx; + Ssl::CertificateValidationContextConfigImpl cvc_config(merged_cvc, *api_, ctx); // Verify that merging CertificateValidationContext applies logical OR to bool // field. EXPECT_TRUE(cvc_config.allowExpiredCertificate()); diff --git a/test/common/secret/secret_manager_impl_test.cc b/test/common/secret/secret_manager_impl_test.cc index 1109b76212..35e8820408 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -133,8 +133,10 @@ TEST_F(SecretManagerImplTest, CertificateValidationContextSecretLoadSuccess) { ASSERT_EQ(secret_manager->findStaticCertificateValidationContextProvider("undefined"), nullptr); ASSERT_NE(secret_manager->findStaticCertificateValidationContextProvider("abc.com"), nullptr); + testing::NiceMock ctx; Ssl::CertificateValidationContextConfigImpl cvc_config( - *secret_manager->findStaticCertificateValidationContextProvider("abc.com")->secret(), *api_); + *secret_manager->findStaticCertificateValidationContextProvider("abc.com")->secret(), *api_, + ctx); const std::string cert_pem = "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), @@ -530,7 +532,7 @@ name: "abc.com.validation" secret_context.cluster_manager_.subscription_factory_.callbacks_->onConfigUpdate( decoded_resources_2.refvec_, "validation-context-v1"); Ssl::CertificateValidationContextConfigImpl cert_validation_context( - *context_secret_provider->secret(), *api_); + *context_secret_provider->secret(), *api_, ctx); EXPECT_EQ("DUMMY_INLINE_STRING_TRUSTED_CA", cert_validation_context.caCert()); const std::string updated_config_dump = R"EOF( dynamic_active_secrets: @@ -1148,8 +1150,10 @@ TEST_F(SecretManagerImplTest, DeprecatedSanMatcher) { ASSERT_EQ(secret_manager->findStaticCertificateValidationContextProvider("undefined"), nullptr); ASSERT_NE(secret_manager->findStaticCertificateValidationContextProvider("abc.com"), nullptr); + testing::NiceMock ctx; Ssl::CertificateValidationContextConfigImpl cvc_config( - *secret_manager->findStaticCertificateValidationContextProvider("abc.com")->secret(), *api_); + *secret_manager->findStaticCertificateValidationContextProvider("abc.com")->secret(), *api_, + ctx); EXPECT_EQ(cvc_config.subjectAltNameMatchers().size(), 4); EXPECT_EQ("example.foo", cvc_config.subjectAltNameMatchers()[0].matcher().exact()); EXPECT_EQ(envoy::extensions::transport_sockets::tls::v3::SubjectAltNameMatcher::DNS, diff --git a/test/common/upstream/test_cluster_manager.h b/test/common/upstream/test_cluster_manager.h index a1872d5d45..b7a3690522 100644 --- a/test/common/upstream/test_cluster_manager.h +++ b/test/common/upstream/test_cluster_manager.h @@ -119,6 +119,9 @@ class TestClusterManagerFactory : public ClusterManagerFactory { Secret::SecretManager& secretManager() override { return secret_manager_; } Singleton::Manager& singletonManager() override { return singleton_manager_; } + CertificateProvider::CertificateProviderManager& certificateProviderManager() override { + return certificate_provider_manager_; + } MOCK_METHOD(ClusterManager*, clusterManagerFromProto_, (const envoy::config::bootstrap::v3::Bootstrap& bootstrap)); @@ -148,6 +151,7 @@ class TestClusterManagerFactory : public ClusterManagerFactory { NiceMock secret_manager_; NiceMock& log_manager_ = server_context_.access_log_manager_; Singleton::ManagerImpl singleton_manager_{Thread::threadFactoryForTest()}; + NiceMock certificate_provider_manager_; NiceMock validation_visitor_; NiceMock random_; Api::ApiPtr api_; diff --git a/test/config_test/config_test.cc b/test/config_test/config_test.cc index c1e4d1d994..20016054f2 100644 --- a/test/config_test/config_test.cc +++ b/test/config_test/config_test.cc @@ -109,7 +109,8 @@ class ConfigTest { server_.dnsResolver(), ssl_context_manager_, server_.dispatcher(), server_.localInfo(), server_.secretManager(), server_.messageValidationContext(), *api_, server_.httpContext(), server_.grpcContext(), server_.routerContext(), server_.accessLogManager(), - server_.singletonManager(), server_.options(), server_.quic_stat_names_, server_); + server_.singletonManager(), server_.options(), server_.quic_stat_names_, + server_.certificateProviderManager(), server_); ON_CALL(server_, clusterManager()).WillByDefault(Invoke([&]() -> Upstream::ClusterManager& { return *main_config.clusterManager(); diff --git a/test/extensions/network/dns_resolver/cares/dns_impl_test.cc b/test/extensions/network/dns_resolver/cares/dns_impl_test.cc index a0ab4b3349..a2f59abfa3 100644 --- a/test/extensions/network/dns_resolver/cares/dns_impl_test.cc +++ b/test/extensions/network/dns_resolver/cares/dns_impl_test.cc @@ -342,7 +342,8 @@ class TestDnsServer : public TcpListenerCallbacks { void onAccept(ConnectionSocketPtr&& socket) override { Network::ConnectionPtr new_connection = dispatcher_.createServerConnection( - std::move(socket), Network::Test::createRawBufferSocket(), stream_info_); + std::move(socket), Network::Test::createRawBufferSocket(), stream_info_, + transport_factory_); TestDnsServerQuery* query = new TestDnsServerQuery(std::move(new_connection), hosts_a_, hosts_aaaa_, cnames_, record_ttl_, refused_, error_on_a_, error_on_aaaa_); @@ -382,6 +383,7 @@ class TestDnsServer : public TcpListenerCallbacks { // over. std::vector> queries_; StreamInfo::StreamInfoImpl stream_info_; + NiceMock transport_factory_; }; } // namespace diff --git a/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc index 26ac3c4648..c54269f8bc 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc +++ b/test/extensions/transport_sockets/tls/cert_validator/default_validator_test.cc @@ -320,11 +320,19 @@ class MockCertificateValidationContextConfig : public Ssl::CertificateValidation MOCK_METHOD(Api::Api&, api, (), (const override)); bool onlyVerifyLeafCertificateCrl() const override { return false; } absl::optional maxVerifyDepth() const override { return absl::nullopt; } + Envoy::CertificateProvider::CertificateProviderSharedPtr caProvider() const override { + return ca_provider_instance_; + }; + + void setCAUpdateCallback(std::function /*callback*/) override {} private: std::string s_; std::vector strs_; std::vector matchers_; + Envoy::CertificateProvider::CertificateProviderSharedPtr ca_provider_instance_; + std::string ca_provider_cert_name_; + Envoy::Common::CallbackHandlePtr ca_update_callback_handle_; }; TEST(DefaultCertValidatorTest, TestUnexpectedSanMatcherType) { diff --git a/test/extensions/transport_sockets/tls/cert_validator/test_common.h b/test/extensions/transport_sockets/tls/cert_validator/test_common.h index 230e3e913e..7e462a1f70 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/test_common.h +++ b/test/extensions/transport_sockets/tls/cert_validator/test_common.h @@ -89,6 +89,12 @@ class TestCertificateValidationContextConfig absl::optional maxVerifyDepth() const override { return max_verify_depth_; } + Envoy::CertificateProvider::CertificateProviderSharedPtr caProvider() const override { + return ca_provider_instance_; + } + + void setCAUpdateCallback(std::function /*callback*/) override{}; + private: bool allow_expired_certificate_{false}; Api::ApiPtr api_; @@ -98,6 +104,9 @@ class TestCertificateValidationContextConfig const std::string ca_cert_; const std::string ca_cert_path_{"TEST_CA_CERT_PATH"}; const absl::optional max_verify_depth_{absl::nullopt}; + Envoy::CertificateProvider::CertificateProviderSharedPtr ca_provider_instance_; + std::string ca_provider_cert_name_; + Envoy::Common::CallbackHandlePtr ca_update_callback_handle_; }; } // namespace Tls diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index 6cb75dd650..eeb510b5eb 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -501,7 +501,6 @@ TEST_F(SslContextImplTest, AtMostOneRsaCertSameWildcardDNSSan) { } // Multiple RSA certificates with different exact DNS SAN are acceptable. ->>>>>>> 9653024634... tls: SNI-based cert selection during TLS handshake (#22036) TEST_F(SslContextImplTest, AcceptableMultipleRsaCerts) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index 13841dcd93..c208185e01 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -381,14 +381,15 @@ void testUtil(const TestUtilOptions& options) { Network::ConnectionPtr server_connection; Network::MockConnectionCallbacks server_connection_callbacks; NiceMock stream_info; + NiceMock transport_factory; EXPECT_CALL(callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { auto ssl_socket = server_ssl_socket_factory.createDownstreamTransportSocket(); // configureInitialCongestionWindow is an unimplemented empty function, this is just to // increase code coverage. ssl_socket->configureInitialCongestionWindow(100, std::chrono::microseconds(123)); - server_connection = dispatcher->createServerConnection(std::move(socket), - std::move(ssl_socket), stream_info); + server_connection = dispatcher->createServerConnection( + std::move(socket), std::move(ssl_socket), stream_info, transport_factory); server_connection->addConnectionCallbacks(server_connection_callbacks); })); @@ -734,6 +735,7 @@ void testUtilV2(const TestUtilOptionsV2& options) { Network::ConnectionPtr server_connection; Network::MockConnectionCallbacks server_connection_callbacks; NiceMock stream_info; + NiceMock transport_factory; EXPECT_CALL(callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { std::string sni = options.transportSocketOptions() != nullptr && @@ -745,7 +747,7 @@ void testUtilV2(const TestUtilOptionsV2& options) { server_ssl_socket_factory.createDownstreamTransportSocket(); EXPECT_FALSE(transport_socket->startSecureTransport()); server_connection = dispatcher->createServerConnection( - std::move(socket), std::move(transport_socket), stream_info); + std::move(socket), std::move(transport_socket), stream_info, transport_factory); server_connection->addConnectionCallbacks(server_connection_callbacks); })); @@ -948,6 +950,7 @@ class SslSocketTest Event::DispatcherPtr dispatcher_; StreamInfo::StreamInfoImpl stream_info_; Network::Address::IpVersion version_; + NiceMock transport_factory_; }; INSTANTIATE_TEST_SUITE_P( @@ -2957,7 +2960,7 @@ TEST_P(SslSocketTest, FlushCloseDuringHandshake) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection->addConnectionCallbacks(server_connection_callbacks); Buffer::OwnedImpl data("hello"); server_connection->write(data, false); @@ -3027,7 +3030,7 @@ TEST_P(SslSocketTest, HalfClose) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection->enableHalfClose(true); server_connection->addReadFilter(server_read_filter); server_connection->addConnectionCallbacks(server_connection_callbacks); @@ -3109,7 +3112,7 @@ TEST_P(SslSocketTest, ShutdownWithCloseNotify) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection->enableHalfClose(true); server_connection->addReadFilter(server_read_filter); server_connection->addConnectionCallbacks(server_connection_callbacks); @@ -3197,7 +3200,7 @@ TEST_P(SslSocketTest, ShutdownWithoutCloseNotify) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection->enableHalfClose(true); server_connection->addReadFilter(server_read_filter); server_connection->addConnectionCallbacks(server_connection_callbacks); @@ -3313,7 +3316,7 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection->addConnectionCallbacks(server_connection_callbacks); })); @@ -3398,6 +3401,7 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, SSL_SESSION* ssl_session = nullptr; Network::ConnectionPtr server_connection; StreamInfo::StreamInfoImpl stream_info(time_system, nullptr); + NiceMock transport_factory; EXPECT_CALL(callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { Network::DownstreamTransportSocketFactory& tsf = @@ -3406,7 +3410,8 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, ? server_ssl_socket_factory1 : server_ssl_socket_factory2; server_connection = dispatcher->createServerConnection( - std::move(socket), tsf.createDownstreamTransportSocket(), stream_info); + std::move(socket), tsf.createDownstreamTransportSocket(), stream_info, + transport_factory); })); EXPECT_CALL(client_connection_callbacks, onEvent(Network::ConnectionEvent::Connected)) @@ -3451,7 +3456,8 @@ void testTicketSessionResumption(const std::string& server_ctx_yaml1, ? server_ssl_socket_factory1 : server_ssl_socket_factory2; server_connection = dispatcher->createServerConnection( - std::move(socket), tsf.createDownstreamTransportSocket(), stream_info2); + std::move(socket), tsf.createDownstreamTransportSocket(), stream_info2, + transport_factory); server_connection->addConnectionCallbacks(server_connection_callbacks); })); @@ -3537,12 +3543,13 @@ void testSupportForStatelessSessionResumption(const std::string& server_ctx_yaml client_connection->connect(); StreamInfo::StreamInfoImpl stream_info(time_system, nullptr); + NiceMock transport_factory; Network::ConnectionPtr server_connection; EXPECT_CALL(callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info); + stream_info, transport_factory); const SslHandshakerImpl* ssl_socket = dynamic_cast(server_connection->ssl().get()); @@ -4143,6 +4150,7 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { SSL_SESSION* ssl_session = nullptr; Network::ConnectionPtr server_connection; Network::MockConnectionCallbacks server_connection_callbacks; + NiceMock transport_factory; EXPECT_CALL(callbacks, onAccept_(_)) .WillOnce(Invoke([&](Network::ConnectionSocketPtr& accepted_socket) -> void { Network::DownstreamTransportSocketFactory& tsf = @@ -4151,7 +4159,8 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { ? server_ssl_socket_factory : server2_ssl_socket_factory; server_connection = dispatcher_->createServerConnection( - std::move(accepted_socket), tsf.createDownstreamTransportSocket(), stream_info_); + std::move(accepted_socket), tsf.createDownstreamTransportSocket(), stream_info_, + transport_factory); server_connection->addConnectionCallbacks(server_connection_callbacks); })); @@ -4193,7 +4202,8 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { ? server_ssl_socket_factory : server2_ssl_socket_factory; server_connection = dispatcher_->createServerConnection( - std::move(accepted_socket), tsf.createDownstreamTransportSocket(), stream_info_); + std::move(accepted_socket), tsf.createDownstreamTransportSocket(), stream_info_, + transport_factory_); server_connection->addConnectionCallbacks(server_connection_callbacks); })); EXPECT_CALL(server_connection_callbacks, onEvent(Network::ConnectionEvent::RemoteClose)); @@ -4279,7 +4289,7 @@ void SslSocketTest::testClientSessionResumption(const std::string& server_ctx_ya .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection->addConnectionCallbacks(server_connection_callbacks); })); @@ -4325,7 +4335,7 @@ void SslSocketTest::testClientSessionResumption(const std::string& server_ctx_ya .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection->addConnectionCallbacks(server_connection_callbacks); })); @@ -4509,7 +4519,7 @@ TEST_P(SslSocketTest, SslError) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory.createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection->addConnectionCallbacks(server_connection_callbacks); })); @@ -5614,7 +5624,7 @@ class SslReadBufferLimitTest : public SslSocketTest { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory_->createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection_->setBufferLimits(read_buffer_limit); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); @@ -5690,7 +5700,7 @@ class SslReadBufferLimitTest : public SslSocketTest { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory_->createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection_->setBufferLimits(read_buffer_limit); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); @@ -5811,7 +5821,7 @@ TEST_P(SslReadBufferLimitTest, TestBind) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory_->createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); EXPECT_EQ("", server_connection_->nextProtocol()); @@ -5842,7 +5852,7 @@ TEST_P(SslReadBufferLimitTest, SmallReadsIntoSameSlice) { .WillOnce(Invoke([&](Network::ConnectionSocketPtr& socket) -> void { server_connection_ = dispatcher_->createServerConnection( std::move(socket), server_ssl_socket_factory_->createDownstreamTransportSocket(), - stream_info_); + stream_info_, transport_factory_); server_connection_->setBufferLimits(read_buffer_limit); server_connection_->addConnectionCallbacks(server_callbacks_); server_connection_->addReadFilter(read_filter_); diff --git a/test/mocks/event/mocks.h b/test/mocks/event/mocks.h index 148beed2ff..96cc918fe5 100644 --- a/test/mocks/event/mocks.h +++ b/test/mocks/event/mocks.h @@ -43,8 +43,8 @@ class MockDispatcher : public Dispatcher { } Network::ServerConnectionPtr createServerConnection(Network::ConnectionSocketPtr&& socket, - Network::TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo&) override { + Network::TransportSocketPtr&& transport_socket, StreamInfo::StreamInfo&, + const Network::DownstreamTransportSocketFactory&) override { // The caller expects both the socket and the transport socket to be moved. socket.reset(); transport_socket.reset(); diff --git a/test/mocks/event/wrapped_dispatcher.h b/test/mocks/event/wrapped_dispatcher.h index 03e403fe5f..d9701b3561 100644 --- a/test/mocks/event/wrapped_dispatcher.h +++ b/test/mocks/event/wrapped_dispatcher.h @@ -33,12 +33,12 @@ class WrappedDispatcher : public Dispatcher { void clearDeferredDeleteList() override { impl_.clearDeferredDeleteList(); } - Network::ServerConnectionPtr - createServerConnection(Network::ConnectionSocketPtr&& socket, - Network::TransportSocketPtr&& transport_socket, - StreamInfo::StreamInfo& stream_info) override { - return impl_.createServerConnection(std::move(socket), std::move(transport_socket), - stream_info); + Network::ServerConnectionPtr createServerConnection( + Network::ConnectionSocketPtr&& socket, Network::TransportSocketPtr&& transport_socket, + StreamInfo::StreamInfo& stream_info, + const Network::DownstreamTransportSocketFactory& transport_socket_factory) override { + return impl_.createServerConnection(std::move(socket), std::move(transport_socket), stream_info, + transport_socket_factory); } Network::ClientConnectionPtr createClientConnection( diff --git a/test/mocks/server/BUILD b/test/mocks/server/BUILD index 28311cf689..7edf2646a7 100644 --- a/test/mocks/server/BUILD +++ b/test/mocks/server/BUILD @@ -241,6 +241,7 @@ envoy_cc_mock( hdrs = ["transport_socket_factory_context.h"], deps = [ "//envoy/server:tracer_config_interface", + "//source/common/certificate_provider:certificate_provider_manager_impl_lib", "//source/common/secret:secret_manager_impl_lib", "//test/mocks/access_log:access_log_mocks", "//test/mocks/api:api_mocks", diff --git a/test/mocks/server/instance.cc b/test/mocks/server/instance.cc index ab6c7d9d70..d75827f59d 100644 --- a/test/mocks/server/instance.cc +++ b/test/mocks/server/instance.cc @@ -51,6 +51,8 @@ MockInstance::MockInstance() ON_CALL(*this, transportSocketFactoryContext()) .WillByDefault(ReturnRef(*transport_socket_factory_context_)); ON_CALL(*this, enableReusePortDefault()).WillByDefault(Return(true)); + ON_CALL(*this, certificateProviderManager()) + .WillByDefault(ReturnRef(certificate_provider_manager_)); } MockInstance::~MockInstance() = default; diff --git a/test/mocks/server/instance.h b/test/mocks/server/instance.h index a85008ebb0..ac56f178b8 100644 --- a/test/mocks/server/instance.h +++ b/test/mocks/server/instance.h @@ -89,6 +89,7 @@ class MockInstance : public Instance { MOCK_METHOD(Configuration::TransportSocketFactoryContext&, transportSocketFactoryContext, ()); MOCK_METHOD(bool, enableReusePortDefault, ()); MOCK_METHOD(void, setSinkPredicates, (std::unique_ptr &&)); + MOCK_METHOD(CertificateProvider::CertificateProviderManager&, certificateProviderManager, ()); void setDefaultTracingConfig(const envoy::config::trace::v3::Tracing& tracing_config) override { http_context_.setDefaultTracingConfig(tracing_config); @@ -132,6 +133,8 @@ class MockInstance : public Instance { server_factory_context_; std::shared_ptr> transport_socket_factory_context_; + testing::NiceMock + certificate_provider_manager_; }; namespace Configuration { diff --git a/test/mocks/server/transport_socket_factory_context.cc b/test/mocks/server/transport_socket_factory_context.cc index 52e5a28eb7..65c5cd63b3 100644 --- a/test/mocks/server/transport_socket_factory_context.cc +++ b/test/mocks/server/transport_socket_factory_context.cc @@ -13,7 +13,7 @@ using ::testing::ReturnRef; MockTransportSocketFactoryContext::MockTransportSocketFactoryContext() : secret_manager_(std::make_unique(config_tracker_)), - singleton_manager_(Thread::threadFactoryForTest()) { + singleton_manager_(Thread::threadFactoryForTest()), certificate_provider_manager_(api_) { ON_CALL(*this, clusterManager()).WillByDefault(ReturnRef(cluster_manager_)); ON_CALL(*this, api()).WillByDefault(ReturnRef(api_)); ON_CALL(*this, messageValidationVisitor()) @@ -23,6 +23,8 @@ MockTransportSocketFactoryContext::MockTransportSocketFactoryContext() ON_CALL(*this, options()).WillByDefault(ReturnRef(options_)); ON_CALL(*this, accessLogManager()).WillByDefault(ReturnRef(access_log_manager_)); ON_CALL(*this, singletonManager()).WillByDefault(ReturnRef(singleton_manager_)); + ON_CALL(*this, certificateProviderManager()) + .WillByDefault(ReturnRef(certificate_provider_manager_)); } MockTransportSocketFactoryContext::~MockTransportSocketFactoryContext() = default; diff --git a/test/mocks/server/transport_socket_factory_context.h b/test/mocks/server/transport_socket_factory_context.h index 4b6d5b1fde..d745a0307e 100644 --- a/test/mocks/server/transport_socket_factory_context.h +++ b/test/mocks/server/transport_socket_factory_context.h @@ -2,6 +2,7 @@ #include "envoy/server/transport_socket_config.h" +#include "source/common/certificate_provider/certificate_provider_manager_impl.h" #include "source/common/secret/secret_manager_impl.h" #include "test/mocks/access_log/mocks.h" @@ -39,6 +40,7 @@ class MockTransportSocketFactoryContext : public TransportSocketFactoryContext { MOCK_METHOD(ProtobufMessage::ValidationVisitor&, messageValidationVisitor, ()); MOCK_METHOD(Api::Api&, api, ()); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, ()); + MOCK_METHOD(CertificateProvider::CertificateProviderManager&, certificateProviderManager, ()); testing::NiceMock cluster_manager_; testing::NiceMock api_; @@ -49,6 +51,7 @@ class MockTransportSocketFactoryContext : public TransportSocketFactoryContext { std::unique_ptr secret_manager_; testing::NiceMock access_log_manager_; Singleton::ManagerImpl singleton_manager_; + CertificateProvider::CertificateProviderManagerImpl certificate_provider_manager_; }; } // namespace Configuration } // namespace Server diff --git a/test/mocks/ssl/mocks.cc b/test/mocks/ssl/mocks.cc index 0609c7fe65..ec3a179649 100644 --- a/test/mocks/ssl/mocks.cc +++ b/test/mocks/ssl/mocks.cc @@ -22,6 +22,8 @@ MockClientContextConfig::MockClientContextConfig() { ON_CALL(*this, tlsKeyLogLocal()).WillByDefault(testing::ReturnRef(iplist_)); ON_CALL(*this, tlsKeyLogRemote()).WillByDefault(testing::ReturnRef(iplist_)); ON_CALL(*this, tlsKeyLogPath()).WillByDefault(testing::ReturnRef(path_)); + ON_CALL(*this, certProviderCaps()) + .WillByDefault(testing::Return(certificate_provider_capabilities_)); } MockClientContextConfig::~MockClientContextConfig() = default; diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 828f1690f9..4f1c6f1a82 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -63,6 +63,8 @@ class MockConnectionInfo : public ConnectionInfo { MOCK_METHOD(const std::string&, tlsVersion, (), (const)); MOCK_METHOD(const std::string&, alpn, (), (const)); MOCK_METHOD(const std::string&, sni, (), (const)); + MOCK_METHOD(int, pkeyTypePeerCertificate, (), (const)); + MOCK_METHOD(int, pkeySizePeerCertificate, (), (const)); }; class MockClientContext : public ClientContext { @@ -104,6 +106,8 @@ class MockClientContextConfig : public ClientContextConfig { MOCK_METHOD(const Network::Address::IpList&, tlsKeyLogRemote, (), (const)); MOCK_METHOD(const std::string&, tlsKeyLogPath, (), (const)); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), (const)); + MOCK_METHOD(CertificateProvider::CertificateProvider::Capabilities, certProviderCaps, (), + (const)); Ssl::HandshakerCapabilities capabilities_; std::string sni_{"default_sni.example.com"}; std::string ciphers_{"RSA"}; @@ -111,6 +115,7 @@ class MockClientContextConfig : public ClientContextConfig { std::string test_{}; Network::Address::IpList iplist_; std::string path_{}; + CertificateProvider::CertificateProvider::Capabilities certificate_provider_capabilities_; }; class MockServerContextConfig : public ServerContextConfig { @@ -143,6 +148,8 @@ class MockServerContextConfig : public ServerContextConfig { MOCK_METHOD(const std::string&, tlsKeyLogPath, (), (const)); MOCK_METHOD(AccessLog::AccessLogManager&, accessLogManager, (), (const)); MOCK_METHOD(bool, fullScanCertsOnSNIMismatch, (), (const)); + MOCK_METHOD(CertificateProvider::CertificateProvider::Capabilities, certProviderCaps, (), + (const)); }; class MockTlsCertificateConfig : public TlsCertificateConfig { @@ -183,6 +190,8 @@ class MockCertificateValidationContextConfig : public CertificateValidationConte trustChainVerification, (), (const)); MOCK_METHOD(bool, onlyVerifyLeafCertificateCrl, (), (const)); MOCK_METHOD(absl::optional, maxVerifyDepth, (), (const)); + MOCK_METHOD(Envoy::CertificateProvider::CertificateProviderSharedPtr, caProvider, (), (const)); + MOCK_METHOD(void, setCAUpdateCallback, (std::function callback)); }; class MockPrivateKeyMethodManager : public PrivateKeyMethodManager { diff --git a/test/mocks/upstream/BUILD b/test/mocks/upstream/BUILD index 624a336837..01c5b43508 100644 --- a/test/mocks/upstream/BUILD +++ b/test/mocks/upstream/BUILD @@ -237,6 +237,7 @@ envoy_cc_mock( srcs = ["cluster_manager_factory.cc"], hdrs = ["cluster_manager_factory.h"], deps = [ + "//envoy/certificate_provider:certificate_provider_manager_interface", "//envoy/upstream:cluster_manager_interface", "//source/common/singleton:manager_impl_lib", "//test/mocks/secret:secret_mocks", diff --git a/test/mocks/upstream/cluster_manager_factory.cc b/test/mocks/upstream/cluster_manager_factory.cc index 37727679a5..c90c7c0da6 100644 --- a/test/mocks/upstream/cluster_manager_factory.cc +++ b/test/mocks/upstream/cluster_manager_factory.cc @@ -1,6 +1,11 @@ #include "cluster_manager_factory.h" namespace Envoy { +namespace CertificateProvider { +MockCertificateProviderManager::MockCertificateProviderManager() = default; + +MockCertificateProviderManager::~MockCertificateProviderManager() = default; +} // namespace CertificateProvider namespace Upstream { MockClusterManagerFactory::MockClusterManagerFactory() = default; diff --git a/test/mocks/upstream/cluster_manager_factory.h b/test/mocks/upstream/cluster_manager_factory.h index 91f53900b4..c3cb499857 100644 --- a/test/mocks/upstream/cluster_manager_factory.h +++ b/test/mocks/upstream/cluster_manager_factory.h @@ -1,5 +1,6 @@ #pragma once +#include "envoy/certificate_provider/certificate_provider_manager.h" #include "envoy/upstream/cluster_manager.h" #include "source/common/singleton/manager_impl.h" @@ -10,9 +11,23 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using ::testing::NiceMock; namespace Envoy { +namespace CertificateProvider { +class MockCertificateProviderManager : public CertificateProviderManager { +public: + MockCertificateProviderManager(); + ~MockCertificateProviderManager() override; + + MOCK_METHOD(void, addCertificateProvider, + (absl::string_view name, const envoy::config::core::v3::TypedExtensionConfig& config, + Server::Configuration::TransportSocketFactoryContext& factory_context)); + + MOCK_METHOD(CertificateProviderSharedPtr, getCertificateProvider, (absl::string_view name)); +}; +} // namespace CertificateProvider + namespace Upstream { -using ::testing::NiceMock; class MockClusterManagerFactory : public ClusterManagerFactory { public: MockClusterManagerFactory(); @@ -20,6 +35,9 @@ class MockClusterManagerFactory : public ClusterManagerFactory { Secret::MockSecretManager& secretManager() override { return secret_manager_; }; Singleton::Manager& singletonManager() override { return singleton_manager_; } + CertificateProvider::CertificateProviderManager& certificateProviderManager() override { + return certificate_provider_manager_; + } MOCK_METHOD(ClusterManagerPtr, clusterManagerFromProto, (const envoy::config::bootstrap::v3::Bootstrap& bootstrap)); @@ -50,6 +68,7 @@ class MockClusterManagerFactory : public ClusterManagerFactory { private: NiceMock secret_manager_; Singleton::ManagerImpl singleton_manager_{Thread::threadFactoryForTest()}; + NiceMock certificate_provider_manager_; }; } // namespace Upstream } // namespace Envoy diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index 174421e2e8..ec1eae576c 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -50,11 +50,13 @@ TEST(ValidationClusterManagerTest, MockedMethods) { AccessLog::MockAccessLogManager log_manager; Singleton::ManagerImpl singleton_manager{Thread::threadFactoryForTest()}; NiceMock server; + NiceMock certificate_provider_manager; ValidationClusterManagerFactory factory( admin, runtime, stats_store, tls, dns_resolver, ssl_context_manager, dispatcher, local_info, secret_manager, validation_context, *api, http_context, grpc_context, router_context, - log_manager, singleton_manager, options, quic_stat_names, server); + log_manager, singleton_manager, options, quic_stat_names, certificate_provider_manager, + server); const envoy::config::bootstrap::v3::Bootstrap bootstrap; ClusterManagerPtr cluster_manager = factory.clusterManagerFromProto(bootstrap); diff --git a/test/server/configuration_impl_test.cc b/test/server/configuration_impl_test.cc index 836622a6df..9188062a2b 100644 --- a/test/server/configuration_impl_test.cc +++ b/test/server/configuration_impl_test.cc @@ -66,7 +66,8 @@ class ConfigurationImplTest : public testing::Test { server_.dispatcher(), server_.localInfo(), server_.secretManager(), server_.messageValidationContext(), *api_, server_.httpContext(), server_.grpcContext(), server_.routerContext(), server_.accessLogManager(), server_.singletonManager(), - server_.options(), server_.quic_stat_names_, server_) {} + server_.options(), server_.quic_stat_names_, server_.certificateProviderManager(), + server_) {} void addStatsdFakeClusterConfig(envoy::config::metrics::v3::StatsSink& sink) { envoy::config::metrics::v3::StatsdSink statsd_sink; From b7ae2b7bd4a6f6a896322d99d5957bb00233fd5c Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Fri, 2 Dec 2022 05:50:26 +0000 Subject: [PATCH 23/35] code cleanup Signed-off-by: Luyao Zhong --- .../v3/local_certificate.proto | 4 +- .../certificate_provider.h | 3 ++ envoy/ssl/connection.h | 4 +- .../local_certificate/local_certificate.cc | 53 ++++++++----------- .../local_certificate/local_certificate.h | 3 +- 5 files changed, 32 insertions(+), 35 deletions(-) diff --git a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto index 0fa5164761..f42b24a967 100644 --- a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto +++ b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto @@ -42,8 +42,10 @@ message LocalCertificate { // Indicates the time at which the certificate expires. google.protobuf.Timestamp expiration_time = 5; + // The pkey type and size. If not specified, the type/size of original server cert will be copied. + // It supports RSA_2048, RSA_3072, RSA_4096, ECDSA_P256. Otherwise, defaults to RSA_2048. Pkey pkey = 6; - + // The maximum number of hosts that the cache will hold. If not specified defaults to 1024. google.protobuf.UInt32Value cache_size = 7 [(validate.rules).uint32 = {gt: 0}]; } diff --git a/envoy/certificate_provider/certificate_provider.h b/envoy/certificate_provider/certificate_provider.h index 97b9474261..ef14cb7b3b 100644 --- a/envoy/certificate_provider/certificate_provider.h +++ b/envoy/certificate_provider/certificate_provider.h @@ -56,6 +56,9 @@ class CertificateProvider { virtual ~CertificateProvider() = default; + /** + * @return a struct with their capabilities. See Capabilities above. + */ virtual Capabilities capabilities() const PURE; /** diff --git a/envoy/ssl/connection.h b/envoy/ssl/connection.h index 2109798725..3dfc546052 100644 --- a/envoy/ssl/connection.h +++ b/envoy/ssl/connection.h @@ -149,12 +149,12 @@ class ConnectionInfo { virtual const std::string& sni() const PURE; /** - * @return pkey id, EVP_PKEY_EC or EVP_PKEY_RSA + * @return pkey id, it should be EVP_PKEY_EC or EVP_PKEY_RSA */ virtual int pkeyTypePeerCertificate() const PURE; /** - * @return EC curve name or RSA size + * @return EC curve name id or RSA size */ virtual int pkeySizePeerCertificate() const PURE; }; diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index b0f4765c73..885410f6d8 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -14,31 +14,27 @@ Provider::Provider( const envoy::extensions::certificate_providers::local_certificate::v3::LocalCertificate& config, Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) : main_thread_dispatcher_(factory_context.mainThreadDispatcher()), + ca_cert_(Config::DataSource::read(config.rootca_cert(), true, api)), + ca_key_(Config::DataSource::read(config.rootca_key(), true, api)), + default_identity_cert_(Config::DataSource::read(config.default_identity_cert(), true, api)), + default_identity_key_(Config::DataSource::read(config.default_identity_key(), true, api)), + pkey_(config.pkey()), cache_size_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, cache_size, CacheDefaultSize)) { - ca_cert_ = Config::DataSource::read(config.rootca_cert(), true, api); - ca_key_ = Config::DataSource::read(config.rootca_key(), true, api); - default_identity_cert_ = Config::DataSource::read(config.default_identity_cert(), true, api); - default_identity_key_ = Config::DataSource::read(config.default_identity_key(), true, api); - pkey_ = config.pkey(); - + ASSERT(main_thread_dispatcher_.isThreadSafe()); if (config.has_expiration_time()) { auto seconds = google::protobuf::util::TimeUtil::TimestampToSeconds(config.expiration_time()); expiration_config_ = std::chrono::system_clock::from_time_t(static_cast(seconds)); } - // Generate TLSCertificate + // Set cert cache size + certificates_.setMaxSize(cache_size_); + + // Set default TLS Certificate envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); tls_certificate->mutable_certificate_chain()->set_inline_string(default_identity_cert_); tls_certificate->mutable_private_key()->set_inline_string(default_identity_key_); - // Update certificates_ map - { - absl::WriterMutexLock writer_lock{&certificates_lock_}; - if (cache_size_ != CacheDefaultSize) { - certificates_.setMaxSize(cache_size_); - } - certificates_.insert("default", tls_certificate); - } + certificates_.insert("default", tls_certificate); } Envoy::CertificateProvider::CertificateProvider::Capabilities Provider::capabilities() const { @@ -52,8 +48,7 @@ const std::string Provider::trustedCA(const std::string&) const { return ""; } std::vector< std::reference_wrapper> Provider::tlsCertificates(const std::string&) const { - - absl::ReaderMutexLock reader_lock{&certificates_lock_}; + ASSERT(main_thread_dispatcher_.isThreadSafe()); return certificates_.getCertificates(); } @@ -61,7 +56,7 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC const std::string sni, ::Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, Event::Dispatcher& thread_local_dispatcher, Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) { - + ASSERT(main_thread_dispatcher_.isThreadSafe()); auto handle = std::make_unique(on_demand_update_callbacks_, sni, callback); @@ -71,9 +66,6 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC // since we do not allow duplicated SANs. // We need to align this cache_hit with current transport socket behavior bool cache_hit = [&]() { - // Lookup may change the content of LRU cache, a write lock is needed to prevent - // multiple threads to access the cache. - absl::ReaderMutexLock writer_lock{&certificates_lock_}; return certificates_.is_in_cache(sni); }(); @@ -92,14 +84,19 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC Common::CallbackHandlePtr Provider::addUpdateCallback(const std::string&, std::function callback) { + ASSERT(main_thread_dispatcher_.isThreadSafe()); return update_callback_manager_.add(callback); } -void Provider::runAddUpdateCallback() { update_callback_manager_.runCallbacks(); } +void Provider::runAddUpdateCallback() { + ASSERT(main_thread_dispatcher_.isThreadSafe()); + update_callback_manager_.runCallbacks(); +} void Provider::runOnDemandUpdateCallback(const std::string& host, Event::Dispatcher& thread_local_dispatcher, bool in_cache) { + ASSERT(main_thread_dispatcher_.isThreadSafe()); auto host_it = on_demand_update_callbacks_.find(host); if (host_it != on_demand_update_callbacks_.end()) { for (auto* pending_callbacks : host_it->second) { @@ -128,7 +125,6 @@ void Provider::signCertificate(const std::string sni, bssl::UniquePtr ca_key; ca_key.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); - /********* generate identity certificate locally *****/ // create a new CSR X509_REQ* req = X509_REQ_new(); X509_REQ_set_version(req, 0); @@ -145,8 +141,7 @@ void Provider::signCertificate(const std::string sni, setSANs(metadata, crt); setExpirationTime(metadata, crt); X509_sign(crt, ca_key.get(), EVP_sha256()); - /********* generate identity certificate locally *****/ - + // convert certificate and key to string bssl::UniquePtr buf(BIO_new(BIO_s_mem())); PEM_write_bio_X509(buf.get(), crt); const uint8_t* output; @@ -158,17 +153,15 @@ void Provider::signCertificate(const std::string sni, BIO_mem_contents(buf.get(), &output, &length); std::string key_pem(reinterpret_cast(output), length); - // Generate TLSCertificate + // Generate TLS Certificate envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); tls_certificate->mutable_certificate_chain()->set_inline_string(cert_pem); tls_certificate->mutable_private_key()->set_inline_string(key_pem); // Update certificates_ map - { - absl::WriterMutexLock writer_lock{&certificates_lock_}; - certificates_.insert(sni, tls_certificate); - } + certificates_.insert(sni, tls_certificate); + runAddUpdateCallback(); runOnDemandUpdateCallback(sni, thread_local_dispatcher, false); } diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index f202cb835b..5f7f9bd588 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -103,8 +103,7 @@ class Provider : public CertificateProvider::CertificateProvider, ::Envoy::CertificateProvider::OnDemandUpdateCallbacks& callbacks_; }; - mutable absl::Mutex certificates_lock_; - CertCacheImpl certificates_ ABSL_GUARDED_BY(certificates_lock_); + CertCacheImpl certificates_; void runAddUpdateCallback(); void runOnDemandUpdateCallback(const std::string& host, From b76971b8984eecd98a7fb5ff26a30d832f71fab3 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Fri, 2 Dec 2022 07:44:16 +0000 Subject: [PATCH 24/35] replace raw pointer with smart pointer Signed-off-by: Luyao Zhong --- .../v3/local_certificate.proto | 2 +- .../filters/network/bumping/v3/bumping.proto | 5 +-- .../local_certificate/local_certificate.cc | 38 +++++++++---------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto index f42b24a967..c718912cd6 100644 --- a/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto +++ b/api/envoy/extensions/certificate_providers/local_certificate/v3/local_certificate.proto @@ -19,7 +19,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Local Certificate Provider] // [#extension: envoy.certificate_providers.local_certificate] -// [#next-free-field: 7] +// [#next-free-field: 8] message LocalCertificate { enum Pkey { UNSPECIFIED = 0; diff --git a/api/envoy/extensions/filters/network/bumping/v3/bumping.proto b/api/envoy/extensions/filters/network/bumping/v3/bumping.proto index fd8f263e27..54872fd6f9 100644 --- a/api/envoy/extensions/filters/network/bumping/v3/bumping.proto +++ b/api/envoy/extensions/filters/network/bumping/v3/bumping.proto @@ -38,6 +38,5 @@ message Bumping { google.protobuf.UInt32Value max_connect_attempts = 4 [(validate.rules).uint32 = {gte: 1}]; // Certificate provider instance for fetching TLS certificates. - transport_sockets.tls.v3.CertificateProviderPluginInstance - tls_certificate_provider_instance = 5; -} \ No newline at end of file + transport_sockets.tls.v3.CertificateProviderPluginInstance tls_certificate_provider_instance = 5; +} diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 885410f6d8..02394fee04 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -126,30 +126,30 @@ void Provider::signCertificate(const std::string sni, ca_key.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); // create a new CSR - X509_REQ* req = X509_REQ_new(); - X509_REQ_set_version(req, 0); - setSubjectToCSR(metadata->connectionInfo()->subjectPeerCertificate().data(), req); - // creates a new, empty public-key object - EVP_PKEY* key = EVP_PKEY_new(); - setPkeyToCSR(metadata, key, req); + bssl::UniquePtr req(X509_REQ_new()); + X509_REQ_set_version(req.get(), 0); + setSubjectToCSR(metadata->connectionInfo()->subjectPeerCertificate().data(), req.get()); + // creates a new, empty EVP_PKEY object to store public and private keys + bssl::UniquePtr key(EVP_PKEY_new()); + setPkeyToCSR(metadata, key.get(), req.get()); // create a new certificate, - X509* crt = X509_new(); - X509_set_version(crt, X509_VERSION_3); - X509_set_issuer_name(crt, X509_get_subject_name(ca_cert.get())); - X509_set_subject_name(crt, X509_REQ_get_subject_name(req)); - X509_set_pubkey(crt, X509_REQ_get_pubkey(req)); - setSANs(metadata, crt); - setExpirationTime(metadata, crt); - X509_sign(crt, ca_key.get(), EVP_sha256()); + bssl::UniquePtr crt(X509_new()); + X509_set_version(crt.get(), X509_VERSION_3); + X509_set_issuer_name(crt.get(), X509_get_subject_name(ca_cert.get())); + X509_set_subject_name(crt.get(), X509_REQ_get_subject_name(req.get())); + X509_set_pubkey(crt.get(), X509_REQ_get_pubkey(req.get())); + setSANs(metadata, crt.get()); + setExpirationTime(metadata, crt.get()); + X509_sign(crt.get(), ca_key.get(), EVP_sha256()); // convert certificate and key to string bssl::UniquePtr buf(BIO_new(BIO_s_mem())); - PEM_write_bio_X509(buf.get(), crt); + PEM_write_bio_X509(buf.get(), crt.get()); const uint8_t* output; size_t length; BIO_mem_contents(buf.get(), &output, &length); std::string cert_pem(reinterpret_cast(output), length); buf.reset(BIO_new(BIO_s_mem())); - PEM_write_bio_PrivateKey(buf.get(), key, nullptr, nullptr, length, nullptr, nullptr); + PEM_write_bio_PrivateKey(buf.get(), key.get(), nullptr, nullptr, length, nullptr, nullptr); BIO_mem_contents(buf.get(), &output, &length); std::string key_pem(reinterpret_cast(output), length); @@ -167,7 +167,7 @@ void Provider::signCertificate(const std::string sni, } void Provider::setSubjectToCSR(absl::string_view subject, X509_REQ* req) { - X509_NAME* x509_name = X509_NAME_new(); + bssl::UniquePtr x509_name(X509_NAME_new()); // Parse the RFC 2253 format output of subjectPeerCertificate and set back to mimic cert. const std::string delim = ","; std::string item; @@ -179,13 +179,13 @@ void Provider::setSubjectToCSR(absl::string_view subject, X509_REQ* req) { } else { absl::StrAppend(&item, v.substr(0, v.length())); std::vector entries = absl::StrSplit(item, "="); - X509_NAME_add_entry_by_txt(x509_name, entries[0].c_str(), MBSTRING_ASC, + X509_NAME_add_entry_by_txt(x509_name.get(), entries[0].c_str(), MBSTRING_ASC, reinterpret_cast(entries[1].c_str()), -1, -1, 0); item.clear(); } } - X509_REQ_set_subject_name(req, x509_name); + X509_REQ_set_subject_name(req, x509_name.get()); } void Provider::setPkeyToCSR(Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, From c549f0c9b2fa802833fcfd0d8f1c9608d59dfcbd Mon Sep 17 00:00:00 2001 From: lei zhang Date: Fri, 2 Dec 2022 16:30:23 +0800 Subject: [PATCH 25/35] Code cleanup (#349) Signed-off-by: LeiZhang Signed-off-by: lei zhang --- .../certificate_providers/local_certificate/BUILD | 1 + .../local_certificate/local_certificate.cc | 9 ++++----- source/extensions/filters/network/bumping/BUILD | 4 ++-- .../transport_sockets/tls/context_config_impl.cc | 3 ++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/source/extensions/certificate_providers/local_certificate/BUILD b/source/extensions/certificate_providers/local_certificate/BUILD index c5ce458119..8b9e474b3e 100644 --- a/source/extensions/certificate_providers/local_certificate/BUILD +++ b/source/extensions/certificate_providers/local_certificate/BUILD @@ -24,6 +24,7 @@ envoy_cc_library( "//source/common/common:callback_impl_lib", "//source/common/common:logger_lib", "//source/common/config:datasource_lib", + "//source/common/protobuf", "@envoy_api//envoy/extensions/certificate_providers/local_certificate/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 02394fee04..6ceee5ae71 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -4,6 +4,7 @@ #include "source/common/common/logger.h" #include "source/common/config/datasource.h" +#include "source/common/protobuf/protobuf.h" namespace Envoy { namespace Extensions { @@ -22,7 +23,7 @@ Provider::Provider( cache_size_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, cache_size, CacheDefaultSize)) { ASSERT(main_thread_dispatcher_.isThreadSafe()); if (config.has_expiration_time()) { - auto seconds = google::protobuf::util::TimeUtil::TimestampToSeconds(config.expiration_time()); + auto seconds = Envoy::ProtobufUtil::TimeUtil::TimestampToSeconds(config.expiration_time()); expiration_config_ = std::chrono::system_clock::from_time_t(static_cast(seconds)); } @@ -65,9 +66,7 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC // if we generate two mimic certs for these SNIs, it can not pass the certs config check // since we do not allow duplicated SANs. // We need to align this cache_hit with current transport socket behavior - bool cache_hit = [&]() { - return certificates_.is_in_cache(sni); - }(); + bool cache_hit = [&]() { return certificates_.is_in_cache(sni); }(); if (cache_hit) { ENVOY_LOG(debug, "Cache hit for {}", sni); @@ -276,7 +275,7 @@ void Provider::setExpirationTime(Envoy::CertificateProvider::OnDemandUpdateMetad X509_gmtime_adj(X509_get_notBefore(crt), 0); // Compare expiration_time config with upstream cert expiration. Use smaller // value of those two dates as expiration time of mimic cert. - auto now = std::chrono::system_clock::now(); + auto now = main_thread_dispatcher_.timeSource().systemTime(); auto cert_expiration = metadata->connectionInfo()->expirationPeerCertificate(); uint64_t valid_seconds; if (expiration_config_) { diff --git a/source/extensions/filters/network/bumping/BUILD b/source/extensions/filters/network/bumping/BUILD index e7bcf23403..2303dd53ec 100644 --- a/source/extensions/filters/network/bumping/BUILD +++ b/source/extensions/filters/network/bumping/BUILD @@ -23,8 +23,8 @@ envoy_cc_library( deps = [ "//envoy/access_log:access_log_interface", "//envoy/buffer:buffer_interface", - "//envoy/common:time_interface", "//envoy/certificate_provider:certificate_provider_interface", + "//envoy/common:time_interface", "//envoy/event:dispatcher_interface", "//envoy/network:connection_interface", "//envoy/network:filter_interface", @@ -47,8 +47,8 @@ envoy_cc_library( "//source/common/http:codec_client_lib", "//source/common/network:application_protocol_lib", "//source/common/network:cidr_range_lib", - "//source/common/network:filter_lib", "//source/common/network:connection_impl", + "//source/common/network:filter_lib", "//source/common/network:proxy_protocol_filter_state_lib", "//source/common/network:socket_option_factory_lib", "//source/common/network:transport_socket_options_lib", diff --git a/source/extensions/transport_sockets/tls/context_config_impl.cc b/source/extensions/transport_sockets/tls/context_config_impl.cc index fe5e176c6e..7669ce60dc 100644 --- a/source/extensions/transport_sockets/tls/context_config_impl.cc +++ b/source/extensions/transport_sockets/tls/context_config_impl.cc @@ -445,7 +445,8 @@ ServerContextConfigImpl::ServerContextConfigImpl( if (!capabilities().provides_certificates) { if ((config.common_tls_context().tls_certificates().size() + config.common_tls_context().tls_certificate_sds_secret_configs().size()) + - config.common_tls_context().has_tls_certificate_provider_instance() == 0) { + config.common_tls_context().has_tls_certificate_provider_instance() == + 0) { throw EnvoyException("No TLS certificates found for server context"); } else if (!config.common_tls_context().tls_certificates().empty() && !config.common_tls_context().tls_certificate_sds_secret_configs().empty()) { From 1bc698cdc8ac6d7e08844662237b7029d09f23cb Mon Sep 17 00:00:00 2001 From: Greg Greenway Date: Tue, 6 Dec 2022 16:51:39 -0800 Subject: [PATCH 26/35] tls: allow multiple certs with the same SAN Signed-off-by: Greg Greenway Signed-off-by: Luyao Zhong --- .../root/intro/arch_overview/security/ssl.rst | 2 +- .../transport_sockets/tls/context_impl.cc | 11 +-- .../tls/context_impl_test.cc | 29 +++----- .../transport_sockets/tls/ssl_socket_test.cc | 67 +++++++++++++++++++ 4 files changed, 85 insertions(+), 24 deletions(-) diff --git a/docs/root/intro/arch_overview/security/ssl.rst b/docs/root/intro/arch_overview/security/ssl.rst index 982abf648f..334be1b756 100644 --- a/docs/root/intro/arch_overview/security/ssl.rst +++ b/docs/root/intro/arch_overview/security/ssl.rst @@ -121,7 +121,7 @@ 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. +* If multiple certificates of a particular type (RSA or ECDSA) are specified for the same name or name pattern, the first one loaded is used for that name. * Non-P-256 server ECDSA certificates are rejected. * Static and SDS certificates may not be mixed in a given :ref:`DownstreamTlsContext `. diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index efa1d32909..802f3ea5d1 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -892,10 +892,13 @@ void ServerContextImpl::populateServerNamesMap(TlsContext& ctx, int pkey_id) { auto sn_match = server_names_map_.try_emplace(sn_pattern, pkey_types_map).first; auto pt_match = sn_match->second.find(pkey_id); if (pt_match != sn_match->second.end()) { - // throw EnvoyException(fmt::format( - // "Failed to load certificate chain from {}, at most one " - // "certificate of a given type may be specified for each DNS SAN entry or Subject - // CN: {}", ctx.cert_chain_file_path_, sn_match->first)); + // When there are duplicate names, prefer the earlier one. + // + // If all of the SANs in a certificate are unused due to duplicates, it could be useful + // to issue a warning, but that would require additional tracking that hasn't been + // implemented. + return; +>>>>>>> ee1ed2d770... tls: allow multiple certs with the same SAN } sn_match->second.emplace(std::pair>(pkey_id, ctx)); }; diff --git a/test/extensions/transport_sockets/tls/context_impl_test.cc b/test/extensions/transport_sockets/tls/context_impl_test.cc index eeb510b5eb..c474a0c7ab 100644 --- a/test/extensions/transport_sockets/tls/context_impl_test.cc +++ b/test/extensions/transport_sockets/tls/context_impl_test.cc @@ -454,8 +454,8 @@ TEST_F(SslContextImplTest, TestNoCert) { EXPECT_TRUE(context->getCertChainInformation().empty()); } -// Multiple RSA certificates with the same exact DNS SAN are rejected. -TEST_F(SslContextImplTest, AtMostOneRsaCertSameExactDNSSan) { +// Multiple RSA certificates with the same exact DNS SAN are allowed. +TEST_F(SslContextImplTest, DuplicateRsaCertSameExactDNSSan) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( common_tls_context: @@ -471,14 +471,11 @@ TEST_F(SslContextImplTest, AtMostOneRsaCertSameExactDNSSan) { )EOF"; TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); ServerContextConfigImpl server_context_config(tls_context, factory_context_); - EXPECT_THROW_WITH_REGEX(manager_.createSslServerContext(store_, server_context_config, {}), - EnvoyException, - "at most one certificate of a given type may be specified for each DNS " - "SAN entry or Subject CN"); + EXPECT_NO_THROW(loadConfig(server_context_config)); } -// Multiple RSA certificates with the same wildcard DNS SAN are rejected. -TEST_F(SslContextImplTest, AtMostOneRsaCertSameWildcardDNSSan) { +// Multiple RSA certificates with the same wildcard DNS SAN are allowed. +TEST_F(SslContextImplTest, DuplicateRsaCertSameWildcardDNSSan) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( common_tls_context: @@ -494,10 +491,7 @@ TEST_F(SslContextImplTest, AtMostOneRsaCertSameWildcardDNSSan) { )EOF"; TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); ServerContextConfigImpl server_context_config(tls_context, factory_context_); - EXPECT_THROW_WITH_REGEX(manager_.createSslServerContext(store_, server_context_config, {}), - EnvoyException, - "at most one certificate of a given type may be specified for each DNS " - "SAN entry or Subject CN"); + EXPECT_NO_THROW(loadConfig(server_context_config)); } // Multiple RSA certificates with different exact DNS SAN are acceptable. @@ -520,8 +514,8 @@ TEST_F(SslContextImplTest, AcceptableMultipleRsaCerts) { EXPECT_NO_THROW(loadConfig(server_context_config)); } -// Multiple ECDSA certificates with the same exact DNS SAN are rejected. -TEST_F(SslContextImplTest, AtMostOneEcdsaCert) { +// Multiple ECDSA certificates with the same exact DNS SAN are allowed. +TEST_F(SslContextImplTest, DuplicateEcdsaCert) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( common_tls_context: @@ -537,13 +531,10 @@ TEST_F(SslContextImplTest, AtMostOneEcdsaCert) { )EOF"; TestUtility::loadFromYaml(TestEnvironment::substitute(tls_context_yaml), tls_context); ServerContextConfigImpl server_context_config(tls_context, factory_context_); - EXPECT_THROW_WITH_REGEX(manager_.createSslServerContext(store_, server_context_config, {}), - EnvoyException, - "at most one certificate of a given type may be specified for each DNS " - "SAN entry or Subject CN"); + EXPECT_NO_THROW(loadConfig(server_context_config)); } -// Multiple ECDSA certificates with different exact DNS SAN are acceptable. +// Multiple ECDSA certificates with different DNS SAN are acceptable. TEST_F(SslContextImplTest, AcceptableMultipleEcdsaCerts) { envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context; const std::string tls_context_yaml = R"EOF( diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index c208185e01..f078ccd0fa 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -34,6 +34,7 @@ #include "test/extensions/transport_sockets/tls/test_data/san_dns_ecdsa_1_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/san_dns_rsa_2_cert_info.h" +#include "test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/san_multiple_dns_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/san_uri_cert_info.h" #include "test/extensions/transport_sockets/tls/test_data/selfsigned_ecdsa_p256_cert_info.h" @@ -1343,6 +1344,72 @@ TEST_P(SslSocketTest, MultiCertPreferExactSniMatch) { testUtil(test_options.setExpectedSni("server1.example.com")); } +// When client supports SNI and multiple certs have a matching SAN, prefer the earlier +// cert in the list. +TEST_P(SslSocketTest, MultiCertPreferFirstCertWithSAN) { + { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "server1.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SAN_DNS_RSA_1_CERT_256_HASH); + 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/san_dns_rsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + testUtil(test_options.setExpectedSni("server1.example.com")); + } + + // Now do the same test but with `tls_certificates` in the opposite order, and the client + // validating the other certificate. + { + const std::string client_ctx_yaml = absl::StrCat(R"EOF( + sni: "server1.example.com" + common_tls_context: + tls_params: + tls_minimum_protocol_version: TLSv1_2 + tls_maximum_protocol_version: TLSv1_2 + cipher_suites: + - ECDHE-ECDSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES128-GCM-SHA256 + validation_context: + verify_certificate_hash: )EOF", + TEST_SAN_MULTIPLE_DNS_1_CERT_256_HASH); + 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/san_multiple_dns_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_multiple_dns_1_key.pem" + - certificate_chain: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_rsa_1_key.pem" +)EOF"; + + TestUtilOptions test_options(client_ctx_yaml, server_ctx_yaml, true, version_); + testUtil(test_options.setExpectedSni("server1.example.com")); + } +} + // When client supports SNI and there is no exact match, validate that wildcard "*.example.com" // matches to "wildcardonlymatch.example.com". TEST_P(SslSocketTest, MultiCertWildcardSniMatch) { From b65d4f4f436aab64b381ff76423e26a66f7bc738 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Mon, 12 Dec 2022 15:22:45 +0000 Subject: [PATCH 27/35] bug fix Signed-off-by: Luyao Zhong --- source/extensions/transport_sockets/tls/context_impl.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 802f3ea5d1..cf31014c17 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -898,7 +898,6 @@ void ServerContextImpl::populateServerNamesMap(TlsContext& ctx, int pkey_id) { // to issue a warning, but that would require additional tracking that hasn't been // implemented. return; ->>>>>>> ee1ed2d770... tls: allow multiple certs with the same SAN } sn_match->second.emplace(std::pair>(pkey_id, ctx)); }; From bfc4d84043882714075bbfb1d658ff7aa542aae1 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Wed, 14 Dec 2022 15:43:42 +0000 Subject: [PATCH 28/35] avoid establishing additional connection with upstream When the mimic cert is already present, skip establishing connection with upstream in Bumping Filter. Signed-off-by: Luyao Zhong --- .../certificate_provider.h | 6 +-- .../local_certificate/local_certificate.cc | 34 ++++++++++------- .../local_certificate/local_certificate.h | 3 +- .../filters/network/bumping/bumping.cc | 38 +++++++++++++++++-- .../filters/network/bumping/bumping.h | 4 +- 5 files changed, 63 insertions(+), 22 deletions(-) diff --git a/envoy/certificate_provider/certificate_provider.h b/envoy/certificate_provider/certificate_provider.h index ef14cb7b3b..4af5ca40ff 100644 --- a/envoy/certificate_provider/certificate_provider.h +++ b/envoy/certificate_provider/certificate_provider.h @@ -32,12 +32,12 @@ class OnDemandUpdateCallbacks { * Called when cert is already in cache. * @param host supplies host of cert. */ - virtual void onCacheHit(const std::string host) const PURE; + virtual void onCacheHit(const std::string host, bool check_only) PURE; /** * Called when cert cache is missed. * @param host supplies host of cert. */ - virtual void onCacheMiss(const std::string host) const PURE; + virtual void onCacheMiss(const std::string host, bool check_only) PURE; }; class OnDemandUpdateHandle { @@ -86,7 +86,7 @@ class CertificateProvider { * @return OnDemandUpdateHandle the handle which can remove that update callback. */ virtual OnDemandUpdateHandlePtr addOnDemandUpdateCallback( - const std::string cert_name, Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + const std::string cert_name, absl::optional metadata, Event::Dispatcher& thread_local_dispatcher, OnDemandUpdateCallbacks& callback) PURE; /** diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 6ceee5ae71..1ae79c43c3 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -1,5 +1,6 @@ #include "source/extensions/certificate_providers/local_certificate/local_certificate.h" +#include #include #include "source/common/common/logger.h" @@ -54,7 +55,7 @@ Provider::tlsCertificates(const std::string&) const { } Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateCallback( - const std::string sni, ::Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + const std::string sni, absl::optional<::Envoy::CertificateProvider::OnDemandUpdateMetadataPtr> metadata, Event::Dispatcher& thread_local_dispatcher, Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) { ASSERT(main_thread_dispatcher_.isThreadSafe()); @@ -68,15 +69,21 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC // We need to align this cache_hit with current transport socket behavior bool cache_hit = [&]() { return certificates_.is_in_cache(sni); }(); - if (cache_hit) { - ENVOY_LOG(debug, "Cache hit for {}", sni); - // Cache hit, run on-demand update callback directly - runOnDemandUpdateCallback(sni, thread_local_dispatcher, true); - } else { - // Cache miss, generate self-signed cert - main_thread_dispatcher_.post([sni, metadata, &thread_local_dispatcher, this] { - signCertificate(sni, metadata, thread_local_dispatcher); - }); + + if (metadata.has_value()) { + if (cache_hit) { + ENVOY_LOG(debug, "Cache hit for {}", sni); + // Cache hit, run on-demand update callback directly + runOnDemandUpdateCallback(sni, metadata, thread_local_dispatcher, true); + } else { + // Cache miss, generate self-signed cert + main_thread_dispatcher_.post([sni, metadata, &thread_local_dispatcher, this] { + signCertificate(sni, metadata.value(), thread_local_dispatcher); + }); + } + } + else { + runOnDemandUpdateCallback(sni, absl::nullopt, thread_local_dispatcher, cache_hit); } return handle; } @@ -93,6 +100,7 @@ void Provider::runAddUpdateCallback() { } void Provider::runOnDemandUpdateCallback(const std::string& host, + absl::optional metadata, Event::Dispatcher& thread_local_dispatcher, bool in_cache) { ASSERT(main_thread_dispatcher_.isThreadSafe()); @@ -102,9 +110,9 @@ void Provider::runOnDemandUpdateCallback(const std::string& host, auto& callbacks = pending_callbacks->callbacks_; pending_callbacks->cancel(); if (in_cache) { - thread_local_dispatcher.post([&callbacks, host] { callbacks.onCacheHit(host); }); + thread_local_dispatcher.post([&callbacks, host, metadata] { callbacks.onCacheHit(host, !metadata.has_value()); }); } else { - thread_local_dispatcher.post([&callbacks, host] { callbacks.onCacheMiss(host); }); + thread_local_dispatcher.post([&callbacks, host, metadata] { callbacks.onCacheMiss(host, !metadata.has_value()); }); } } on_demand_update_callbacks_.erase(host_it); @@ -162,7 +170,7 @@ void Provider::signCertificate(const std::string sni, certificates_.insert(sni, tls_certificate); runAddUpdateCallback(); - runOnDemandUpdateCallback(sni, thread_local_dispatcher, false); + runOnDemandUpdateCallback(sni, metadata, thread_local_dispatcher, false); } void Provider::setSubjectToCSR(absl::string_view subject, X509_REQ* req) { diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index 5f7f9bd588..5512b96d00 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -85,7 +85,7 @@ class Provider : public CertificateProvider::CertificateProvider, std::reference_wrapper> tlsCertificates(const std::string& cert_name) const override; Envoy::CertificateProvider::OnDemandUpdateHandlePtr addOnDemandUpdateCallback( - const std::string cert_name, Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, + const std::string cert_name, absl::optional metadata, Event::Dispatcher& thread_local_dispatcher, ::Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) override; Common::CallbackHandlePtr addUpdateCallback(const std::string& cert_name, @@ -107,6 +107,7 @@ class Provider : public CertificateProvider::CertificateProvider, void runAddUpdateCallback(); void runOnDemandUpdateCallback(const std::string& host, + absl::optional metadata, Event::Dispatcher& thread_local_dispatcher, bool in_cache = true); // void signCertificate(std::string sni, absl::Span dns_sans, const std::string // subject, diff --git a/source/extensions/filters/network/bumping/bumping.cc b/source/extensions/filters/network/bumping/bumping.cc index 5060a2d256..1836b49759 100644 --- a/source/extensions/filters/network/bumping/bumping.cc +++ b/source/extensions/filters/network/bumping/bumping.cc @@ -231,6 +231,18 @@ void Filter::onGenericPoolReady(StreamInfo::StreamInfo*, } void Filter::requestCertificate(Ssl::ConnectionInfoConstSharedPtr info) { + if (info == nullptr) { + // if no info provided, only check if cert is present and determime whether or not + // to establish connection with upstream + config_->main_dispatcher_.post([this]() { + this->on_demand_handle_ = config_->tls_certificate_provider_->addOnDemandUpdateCallback( + std::string(read_callbacks_->connection().requestedServerName()), + absl::nullopt, read_callbacks_->connection().dispatcher(), *this); + }); + return; + } + // if info is provided, it represents upstream connection is established, interact with + // certificate provide to mimic certificate ENVOY_CONN_LOG(info, "sni: {}", read_callbacks_->connection(), read_callbacks_->connection().requestedServerName()); config_->main_dispatcher_.post([this, info]() { @@ -254,8 +266,11 @@ Network::FilterStatus Filter::onData(Buffer::Instance&, bool) { Network::FilterStatus Filter::onNewConnection() { // ASSERT(upstream_ == nullptr); + ENVOY_CONN_LOG(info, "Bumping onNewConnection()", read_callbacks_->connection()); route_ = pickRoute(); - return establishUpstreamConnection(); + // check if the corresponding mimic cert is present + requestCertificate(nullptr); + return Network::FilterStatus::StopIteration; } void Filter::onUpstreamEvent(Network::ConnectionEvent event) { @@ -288,7 +303,16 @@ void Filter::onUpstreamConnection() { getStreamInfo().downstreamAddressProvider().requestedServerName()); } -void Filter::onCacheHit(const std::string) const { +void Filter::onCacheHit(const std::string, bool) { + // cert is present in cert provider cache, continue filter chain no matter + // upstream connection is established or not + // Recreate transport socket to ensure up-to-date certificate is used + try { + dynamic_cast(read_callbacks_->connection()) + .refreshTransportSocket(); + } catch (std::bad_cast exp) { + ENVOY_CONN_LOG(warn, "connection cast failed in bumping filter", read_callbacks_->connection()); + } // Re-enable downstream reads and writes read_callbacks_->connection().readDisable(false); read_callbacks_->connection().write_disable = false; @@ -296,7 +320,15 @@ void Filter::onCacheHit(const std::string) const { read_callbacks_->continueReading(); } -void Filter::onCacheMiss(const std::string) const { +void Filter::onCacheMiss(const std::string, bool check_only) { + if (check_only) { + // cert is not present in cert provider cache, and mimic cert is not generated + // establish connection with upstream and trigger cert mimicking based on original certification. + establishUpstreamConnection(); + //read_callbacks_->continueReading(); + return; + } + // cert is not present in cert provider cache, and mimic cert is generated // Recreate transport socket to use newly generate certificate try { dynamic_cast(read_callbacks_->connection()) diff --git a/source/extensions/filters/network/bumping/bumping.h b/source/extensions/filters/network/bumping/bumping.h index dbbe43c01f..a8e586b475 100644 --- a/source/extensions/filters/network/bumping/bumping.h +++ b/source/extensions/filters/network/bumping/bumping.h @@ -175,8 +175,8 @@ class Filter : public Network::ReadFilter, } // CertificateProvider::OnDemandUpdateCallbacks - void onCacheHit(const std::string host) const override; - void onCacheMiss(const std::string host) const override; + void onCacheHit(const std::string host, bool check_only) override; + void onCacheMiss(const std::string host, bool check_only) override; void requestCertificate(Ssl::ConnectionInfoConstSharedPtr info); From 9c130c1b2c8183d912f0406b5e297c381a238b3e Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 13 Dec 2022 07:01:44 +0000 Subject: [PATCH 29/35] bug fix: add nullptr check for cert When custom handshaker provides certificates, it's possible that no certificate is loaded correctly and become a null pointer. Skip the null cert when populate the map used for SNI-based certificate selection. Signed-off-by: Luyao Zhong --- .../transport_sockets/tls/context_impl.cc | 3 + .../tls/context_manager_impl.cc | 4 - .../tls/context_manager_impl.h | 2 +- test/extensions/transport_sockets/tls/BUILD | 1 + .../tls/handshaker_factory_test.cc | 95 +++++++++++++++++++ 5 files changed, 100 insertions(+), 5 deletions(-) diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index cf31014c17..d2ac1b3d90 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -787,6 +787,9 @@ ServerContextImpl::ServerContextImpl(Stats::Scope& scope, } for (auto& ctx : tls_contexts_) { + if (ctx.cert_chain_ == nullptr) { + continue; + } bssl::UniquePtr public_key(X509_get_pubkey(ctx.cert_chain_.get())); const int pkey_id = EVP_PKEY_id(public_key.get()); // Load DNS SAN entries and Subject Common Name as server name patterns after certificate diff --git a/source/extensions/transport_sockets/tls/context_manager_impl.cc b/source/extensions/transport_sockets/tls/context_manager_impl.cc index d039370791..4544ec7a15 100644 --- a/source/extensions/transport_sockets/tls/context_manager_impl.cc +++ b/source/extensions/transport_sockets/tls/context_manager_impl.cc @@ -17,10 +17,6 @@ namespace Tls { ContextManagerImpl::ContextManagerImpl(TimeSource& time_source) : time_source_(time_source) {} -ContextManagerImpl::~ContextManagerImpl() { - KNOWN_ISSUE_ASSERT(contexts_.empty(), "https://github.com/envoyproxy/envoy/issues/10030"); -} - Envoy::Ssl::ClientContextSharedPtr ContextManagerImpl::createSslClientContext(Stats::Scope& scope, const Envoy::Ssl::ClientContextConfig& config) { diff --git a/source/extensions/transport_sockets/tls/context_manager_impl.h b/source/extensions/transport_sockets/tls/context_manager_impl.h index 494f8a30ce..d7386f1d19 100644 --- a/source/extensions/transport_sockets/tls/context_manager_impl.h +++ b/source/extensions/transport_sockets/tls/context_manager_impl.h @@ -26,7 +26,7 @@ namespace Tls { class ContextManagerImpl final : public Envoy::Ssl::ContextManager { public: explicit ContextManagerImpl(TimeSource& time_source); - ~ContextManagerImpl() override; + ~ContextManagerImpl() override = default; // Ssl::ContextManager Ssl::ClientContextSharedPtr diff --git a/test/extensions/transport_sockets/tls/BUILD b/test/extensions/transport_sockets/tls/BUILD index fa8bf6e60b..375bbb3072 100644 --- a/test/extensions/transport_sockets/tls/BUILD +++ b/test/extensions/transport_sockets/tls/BUILD @@ -202,6 +202,7 @@ envoy_cc_test( "//test/mocks/stats:stats_mocks", "//test/test_common:registry_lib", "//test/test_common:test_runtime_lib", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", ], ) diff --git a/test/extensions/transport_sockets/tls/handshaker_factory_test.cc b/test/extensions/transport_sockets/tls/handshaker_factory_test.cc index 212a8b1b09..c20545c377 100644 --- a/test/extensions/transport_sockets/tls/handshaker_factory_test.cc +++ b/test/extensions/transport_sockets/tls/handshaker_factory_test.cc @@ -1,5 +1,6 @@ #include +#include "envoy/extensions/transport_sockets/tls/v3/tls.pb.h" #include "envoy/network/transport_socket.h" #include "envoy/ssl/handshaker.h" @@ -204,6 +205,100 @@ TEST_F(HandshakerFactoryTest, HandshakerContextProvidesObjectsFromParentContext) socket_factory.createTransportSocket(nullptr, nullptr); } +class HandshakerFactoryImplForDownstreamTest + : public Extensions::TransportSockets::Tls::HandshakerFactoryImpl { +public: + using CreateHandshakerHook = + std::function; + + static constexpr char kFactoryName[] = "envoy.testonly_downstream_handshaker"; + + std::string name() const override { return kFactoryName; } + + Ssl::HandshakerFactoryCb + createHandshakerCb(const Protobuf::Message& message, Ssl::HandshakerFactoryContext& context, + ProtobufMessage::ValidationVisitor& validation_visitor) override { + if (handshaker_cb_) { + handshaker_cb_(message, context, validation_visitor); + } + + // The default HandshakerImpl doesn't take a config or use the HandshakerFactoryContext. + return [](bssl::UniquePtr ssl, int ssl_extended_socket_info_index, + Ssl::HandshakeCallbacks* handshake_callbacks) { + return std::make_shared(std::move(ssl), ssl_extended_socket_info_index, + handshake_callbacks); + }; + } + + Ssl::SslCtxCb sslctxCb(Ssl::HandshakerFactoryContext& handshaker_factory_context) const override { + // Get process object, cast to custom process object, and return custom + // callback. + return CustomProcessObjectForTest::get(handshaker_factory_context.api().processContext()) + ->getSslCtxCb(); + } + + Ssl::HandshakerCapabilities capabilities() const override { return capabilities_; } + + void setCapabilities(Ssl::HandshakerCapabilities cap) { capabilities_ = cap; } + + CreateHandshakerHook handshaker_cb_; + Ssl::HandshakerCapabilities capabilities_; +}; +class HandshakerFactoryDownstreamTest : public testing::Test { +protected: + HandshakerFactoryDownstreamTest() + : context_manager_( + std::make_unique(time_system_)) { + scoped_runtime_.mergeValues( + {{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); + } + + // Helper for downcasting a socket to a test socket so we can examine its + // SSL_CTX. + SSL_CTX* extractSslCtx(Network::TransportSocket* socket) { + SslSocket* ssl_socket = dynamic_cast(socket); + SSL* ssl = ssl_socket->rawSslForTest(); + return SSL_get_SSL_CTX(ssl); + } + + Event::GlobalTimeSystem time_system_; + Stats::IsolatedStoreImpl stats_store_; + std::unique_ptr context_manager_; + envoy::extensions::transport_sockets::tls::v3::DownstreamTlsContext tls_context_; + TestScopedRuntime scoped_runtime_; + Ssl::HandshakerCapabilities capabilities_; +}; + +// TlsCertificate messages can be empty when handshaker provides certificates. +TEST_F(HandshakerFactoryDownstreamTest, ServerHandshakerProvidesCertificates) { + HandshakerFactoryImplForDownstreamTest handshaker_factory; + Ssl::HandshakerCapabilities cap; + cap.provides_certificates = true; + handshaker_factory.setCapabilities(cap); + Registry::InjectFactory registered_factory(handshaker_factory); + + // DownstreamTlsContext proto expects to use the newly-registered handshaker. + envoy::config::core::v3::TypedExtensionConfig* custom_handshaker = + tls_context_.mutable_common_tls_context()->mutable_custom_handshaker(); + custom_handshaker->set_name(HandshakerFactoryImplForDownstreamTest::kFactoryName); + + CustomProcessObjectForTest custom_process_object_for_test( + /*cb=*/[](SSL_CTX* ssl_ctx) { SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1); }); + auto process_context_impl = std::make_unique( + static_cast(custom_process_object_for_test)); + + NiceMock mock_factory_ctx; + EXPECT_CALL(mock_factory_ctx.api_, processContext()) + .WillRepeatedly(Return(std::reference_wrapper(*process_context_impl))); + + Extensions::TransportSockets::Tls::ServerContextConfigImpl server_context_config( + tls_context_, mock_factory_ctx); + EXPECT_TRUE(server_context_config.isReady()); + EXPECT_NO_THROW(context_manager_->createSslServerContext(stats_store_, server_context_config, + std::vector{})); +} + } // namespace } // namespace Tls } // namespace TransportSockets From 92e25a25efe589107a0ec4f6f16aa5408dc466c9 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 20 Dec 2022 02:35:52 +0000 Subject: [PATCH 30/35] fix format Signed-off-by: Luyao Zhong --- .../certificate_provider.h | 3 +- .../network/transport_socket_options_impl.cc | 2 +- .../local_certificate/local_certificate.cc | 28 ++++++++++--------- .../local_certificate/local_certificate.h | 12 ++++---- .../filters/network/bumping/bumping.cc | 9 +++--- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/envoy/certificate_provider/certificate_provider.h b/envoy/certificate_provider/certificate_provider.h index 4af5ca40ff..e243b38f14 100644 --- a/envoy/certificate_provider/certificate_provider.h +++ b/envoy/certificate_provider/certificate_provider.h @@ -86,7 +86,8 @@ class CertificateProvider { * @return OnDemandUpdateHandle the handle which can remove that update callback. */ virtual OnDemandUpdateHandlePtr addOnDemandUpdateCallback( - const std::string cert_name, absl::optional metadata, + const std::string cert_name, + absl::optional metadata, Event::Dispatcher& thread_local_dispatcher, OnDemandUpdateCallbacks& callback) PURE; /** diff --git a/source/common/network/transport_socket_options_impl.cc b/source/common/network/transport_socket_options_impl.cc index cdfb542812..4243af3054 100644 --- a/source/common/network/transport_socket_options_impl.cc +++ b/source/common/network/transport_socket_options_impl.cc @@ -66,7 +66,7 @@ TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& fi bool needs_transport_socket_options = false; // sni parameter prevails over value from filterstate - if (sni.size()) { + if (!sni.empty()) { server_name = sni; needs_transport_socket_options = true; } else if (auto typed_data = diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 1ae79c43c3..3609078e26 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -1,8 +1,9 @@ #include "source/extensions/certificate_providers/local_certificate/local_certificate.h" -#include #include +#include + #include "source/common/common/logger.h" #include "source/common/config/datasource.h" #include "source/common/protobuf/protobuf.h" @@ -55,7 +56,8 @@ Provider::tlsCertificates(const std::string&) const { } Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateCallback( - const std::string sni, absl::optional<::Envoy::CertificateProvider::OnDemandUpdateMetadataPtr> metadata, + const std::string sni, + absl::optional<::Envoy::CertificateProvider::OnDemandUpdateMetadataPtr> metadata, Event::Dispatcher& thread_local_dispatcher, Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) { ASSERT(main_thread_dispatcher_.isThreadSafe()); @@ -67,8 +69,7 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC // if we generate two mimic certs for these SNIs, it can not pass the certs config check // since we do not allow duplicated SANs. // We need to align this cache_hit with current transport socket behavior - bool cache_hit = [&]() { return certificates_.is_in_cache(sni); }(); - + bool cache_hit = [&]() { return certificates_.isInCache(sni); }(); if (metadata.has_value()) { if (cache_hit) { @@ -81,8 +82,7 @@ Envoy::CertificateProvider::OnDemandUpdateHandlePtr Provider::addOnDemandUpdateC signCertificate(sni, metadata.value(), thread_local_dispatcher); }); } - } - else { + } else { runOnDemandUpdateCallback(sni, absl::nullopt, thread_local_dispatcher, cache_hit); } return handle; @@ -99,10 +99,10 @@ void Provider::runAddUpdateCallback() { update_callback_manager_.runCallbacks(); } -void Provider::runOnDemandUpdateCallback(const std::string& host, - absl::optional metadata, - Event::Dispatcher& thread_local_dispatcher, - bool in_cache) { +void Provider::runOnDemandUpdateCallback( + const std::string& host, + absl::optional metadata, + Event::Dispatcher& thread_local_dispatcher, bool in_cache) { ASSERT(main_thread_dispatcher_.isThreadSafe()); auto host_it = on_demand_update_callbacks_.find(host); if (host_it != on_demand_update_callbacks_.end()) { @@ -110,9 +110,11 @@ void Provider::runOnDemandUpdateCallback(const std::string& host, auto& callbacks = pending_callbacks->callbacks_; pending_callbacks->cancel(); if (in_cache) { - thread_local_dispatcher.post([&callbacks, host, metadata] { callbacks.onCacheHit(host, !metadata.has_value()); }); + thread_local_dispatcher.post( + [&callbacks, host, metadata] { callbacks.onCacheHit(host, !metadata.has_value()); }); } else { - thread_local_dispatcher.post([&callbacks, host, metadata] { callbacks.onCacheMiss(host, !metadata.has_value()); }); + thread_local_dispatcher.post( + [&callbacks, host, metadata] { callbacks.onCacheMiss(host, !metadata.has_value()); }); } } on_demand_update_callbacks_.erase(host_it); @@ -185,7 +187,7 @@ void Provider::setSubjectToCSR(absl::string_view subject, X509_REQ* req) { absl::StrAppend(&item, v.substr(0, v.length() - 1), delim); } else { absl::StrAppend(&item, v.substr(0, v.length())); - std::vector entries = absl::StrSplit(item, "="); + std::vector entries = absl::StrSplit(item, '='); X509_NAME_add_entry_by_txt(x509_name.get(), entries[0].c_str(), MBSTRING_ASC, reinterpret_cast(entries[1].c_str()), -1, -1, 0); diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index 5512b96d00..106590eb19 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -36,7 +36,7 @@ class CertCacheImpl { } } - bool is_in_cache(const std::string& host) { + bool isInCache(const std::string& host) { if (!cert_cache_) { return false; } @@ -85,7 +85,8 @@ class Provider : public CertificateProvider::CertificateProvider, std::reference_wrapper> tlsCertificates(const std::string& cert_name) const override; Envoy::CertificateProvider::OnDemandUpdateHandlePtr addOnDemandUpdateCallback( - const std::string cert_name, absl::optional metadata, + const std::string cert_name, + absl::optional metadata, Event::Dispatcher& thread_local_dispatcher, ::Envoy::CertificateProvider::OnDemandUpdateCallbacks& callback) override; Common::CallbackHandlePtr addUpdateCallback(const std::string& cert_name, @@ -106,9 +107,10 @@ class Provider : public CertificateProvider::CertificateProvider, CertCacheImpl certificates_; void runAddUpdateCallback(); - void runOnDemandUpdateCallback(const std::string& host, - absl::optional metadata, - Event::Dispatcher& thread_local_dispatcher, bool in_cache = true); + void runOnDemandUpdateCallback( + const std::string& host, + absl::optional metadata, + Event::Dispatcher& thread_local_dispatcher, bool in_cache = true); // void signCertificate(std::string sni, absl::Span dns_sans, const std::string // subject, // Event::Dispatcher& thread_local_dispatcher); diff --git a/source/extensions/filters/network/bumping/bumping.cc b/source/extensions/filters/network/bumping/bumping.cc index 1836b49759..2f66842555 100644 --- a/source/extensions/filters/network/bumping/bumping.cc +++ b/source/extensions/filters/network/bumping/bumping.cc @@ -236,8 +236,8 @@ void Filter::requestCertificate(Ssl::ConnectionInfoConstSharedPtr info) { // to establish connection with upstream config_->main_dispatcher_.post([this]() { this->on_demand_handle_ = config_->tls_certificate_provider_->addOnDemandUpdateCallback( - std::string(read_callbacks_->connection().requestedServerName()), - absl::nullopt, read_callbacks_->connection().dispatcher(), *this); + std::string(read_callbacks_->connection().requestedServerName()), absl::nullopt, + read_callbacks_->connection().dispatcher(), *this); }); return; } @@ -323,9 +323,10 @@ void Filter::onCacheHit(const std::string, bool) { void Filter::onCacheMiss(const std::string, bool check_only) { if (check_only) { // cert is not present in cert provider cache, and mimic cert is not generated - // establish connection with upstream and trigger cert mimicking based on original certification. + // establish connection with upstream and trigger cert mimicking based on original + // certification. establishUpstreamConnection(); - //read_callbacks_->continueReading(); + // read_callbacks_->continueReading(); return; } // cert is not present in cert provider cache, and mimic cert is generated From dccf77d5e6494b685e05ea2b363119dc0e9aaf6e Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 20 Dec 2022 08:42:06 +0000 Subject: [PATCH 31/35] fix test case Signed-off-by: Luyao Zhong --- .../internal_listener/active_internal_listener_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/extensions/bootstrap/internal_listener/active_internal_listener_test.cc b/test/extensions/bootstrap/internal_listener/active_internal_listener_test.cc index 8f4e22be5f..528b4b1249 100644 --- a/test/extensions/bootstrap/internal_listener/active_internal_listener_test.cc +++ b/test/extensions/bootstrap/internal_listener/active_internal_listener_test.cc @@ -169,7 +169,7 @@ TEST_F(ActiveInternalListenerTest, AcceptSocketAndCreateNetworkFilter) { EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); EXPECT_CALL(*filter_chain_, transportSocketFactory) - .WillOnce(testing::ReturnRef(*transport_socket_factory)); + .WillRepeatedly(testing::ReturnRef(*transport_socket_factory)); EXPECT_CALL(*filter_chain_, networkFilterFactories).WillOnce(ReturnRef(*filter_factory_callback)); auto* connection = new NiceMock(); EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); @@ -217,7 +217,7 @@ TEST_F(ActiveInternalListenerTest, DestroyListenerCloseAllConnections) { .WillRepeatedly(Invoke([&](Network::ListenerFilterManager&) -> bool { return true; })); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); EXPECT_CALL(*filter_chain_, transportSocketFactory) - .WillOnce(testing::ReturnRef(*transport_socket_factory)); + .WillRepeatedly(testing::ReturnRef(*transport_socket_factory)); EXPECT_CALL(*filter_chain_, networkFilterFactories).WillOnce(ReturnRef(*filter_factory_callback)); auto* connection = new NiceMock(); EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); From 3e473898c1d78bc8f84fc5249ae4cc51f0f6c815 Mon Sep 17 00:00:00 2001 From: lei zhang Date: Tue, 20 Dec 2022 17:03:15 +0800 Subject: [PATCH 32/35] Fix active tcp listener test (#351) Signed-off-by: LeiZhang Signed-off-by: lei zhang --- test/server/active_tcp_listener_test.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/server/active_tcp_listener_test.cc b/test/server/active_tcp_listener_test.cc index d2665c5417..848ea50734 100644 --- a/test/server/active_tcp_listener_test.cc +++ b/test/server/active_tcp_listener_test.cc @@ -579,7 +579,8 @@ TEST_F(ActiveTcpListenerTest, RedirectedRebalancer) { EXPECT_CALL(conn_handler_, incNumConnections()); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); EXPECT_CALL(*filter_chain_, transportSocketFactory) - .WillOnce(testing::ReturnRef(*transport_socket_factory)); + .Times(2) + .WillRepeatedly(testing::ReturnRef(*transport_socket_factory)); EXPECT_CALL(*filter_chain_, networkFilterFactories).WillOnce(ReturnRef(*filter_factory_callback)); EXPECT_CALL(listener_config2, filterChainFactory()) .WillRepeatedly(ReturnRef(filter_chain_factory_)); @@ -661,7 +662,8 @@ TEST_F(ActiveTcpListenerTest, Rebalance) { EXPECT_CALL(conn_handler_, incNumConnections()); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); EXPECT_CALL(*filter_chain_, transportSocketFactory) - .WillOnce(testing::ReturnRef(*transport_socket_factory)); + .Times(2) + .WillRepeatedly(testing::ReturnRef(*transport_socket_factory)); EXPECT_CALL(*filter_chain_, networkFilterFactories).WillOnce(ReturnRef(*filter_factory_callback)); EXPECT_CALL(listener_config2, filterChainFactory()) .WillRepeatedly(ReturnRef(filter_chain_factory_)); From e598dbf71610197c81bd67f5ce0f52b826f14f5e Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 20 Dec 2022 09:17:58 +0000 Subject: [PATCH 33/35] fix code format and fix spelling format Signed-off-by: Luyao Zhong --- source/common/network/connection_impl.cc | 2 +- source/common/network/transport_socket_options_impl.cc | 2 +- .../local_certificate/local_certificate.cc | 6 ++---- tls_bumping/splicing_bumping_all.yaml | 2 +- tls_bumping/splicing_w_connect.yaml | 1 - 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/source/common/network/connection_impl.cc b/source/common/network/connection_impl.cc index d0d55cd8dd..13b191a459 100644 --- a/source/common/network/connection_impl.cc +++ b/source/common/network/connection_impl.cc @@ -859,7 +859,7 @@ void ServerConnectionImpl::refreshTransportSocket() { auto transport_socket = transport_socket_factory_.createDownstreamTransportSocket(); std::swap(transport_socket, transport_socket_); transport_socket_->setTransportSocketCallbacks(*this); - // Currently setSslConnection doesn't allow swapping connectioninfo if old connection info exists + // Currently setSslConnection doesn't allow swapping connection info if old connection info exists // Revisit this when related issues are discovered. // socket_->connectionInfoProvider().setSslConnection(transport_socket_->ssl()); } diff --git a/source/common/network/transport_socket_options_impl.cc b/source/common/network/transport_socket_options_impl.cc index 4243af3054..23953ab49a 100644 --- a/source/common/network/transport_socket_options_impl.cc +++ b/source/common/network/transport_socket_options_impl.cc @@ -65,7 +65,7 @@ TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& fi bool needs_transport_socket_options = false; - // sni parameter prevails over value from filterstate + // sni parameter prevails over value from filter state if (!sni.empty()) { server_name = sni; needs_transport_socket_options = true; diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 3609078e26..27ab63a5ca 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -134,7 +134,7 @@ void Provider::signCertificate(const std::string sni, bssl::UniquePtr ca_key; ca_key.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); - // create a new CSR + // create a new Cert Signing Request bssl::UniquePtr req(X509_REQ_new()); X509_REQ_set_version(req.get(), 0); setSubjectToCSR(metadata->connectionInfo()->subjectPeerCertificate().data(), req.get()); @@ -200,12 +200,10 @@ void Provider::setSubjectToCSR(absl::string_view subject, X509_REQ* req) { void Provider::setPkeyToCSR(Envoy::CertificateProvider::OnDemandUpdateMetadataPtr metadata, EVP_PKEY* key, X509_REQ* req) { auto pkey_rsa = [&](const int size) { - // BN provides support for working with arbitrary sized integers BIGNUM* bne = BN_new(); BN_set_word(bne, RSA_F4); RSA* rsa = RSA_new(); - // generates a new RSA key where the modulus has size |bits|=2048 and the public exponent is - // |e|=bne + // generates a new RSA key where the modulus has size and the public exponent RSA_generate_key_ex(rsa, size, bne, nullptr); // set the underlying public key in an |EVP_PKEY| object diff --git a/tls_bumping/splicing_bumping_all.yaml b/tls_bumping/splicing_bumping_all.yaml index 505cb58475..2a8d224ea4 100644 --- a/tls_bumping/splicing_bumping_all.yaml +++ b/tls_bumping/splicing_bumping_all.yaml @@ -214,7 +214,7 @@ static_resources: - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router - + clusters: - name: decap_cluster_http load_assignment: diff --git a/tls_bumping/splicing_w_connect.yaml b/tls_bumping/splicing_w_connect.yaml index 4601afcae9..088629ec47 100644 --- a/tls_bumping/splicing_w_connect.yaml +++ b/tls_bumping/splicing_w_connect.yaml @@ -84,4 +84,3 @@ static_resources: dns_cache_config: name: dynamic_forward_proxy_cache_config dns_lookup_family: V4_ONLY - \ No newline at end of file From 8b3a178d239b2a10b33ca3f06ba7abd690d4b7e3 Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 20 Dec 2022 12:14:48 +0000 Subject: [PATCH 34/35] fix clang_tidy warning fix clang_tidy warnings and fix format Signed-off-by: Luyao Zhong --- .../local_certificate/local_certificate.cc | 18 +++++++++--------- .../local_certificate/local_certificate.h | 6 ++++-- .../filters/network/bumping/bumping.cc | 4 ++-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.cc b/source/extensions/certificate_providers/local_certificate/local_certificate.cc index 27ab63a5ca..b935c13908 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.cc +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.cc @@ -3,6 +3,7 @@ #include #include +#include #include "source/common/common/logger.h" #include "source/common/config/datasource.h" @@ -33,11 +34,11 @@ Provider::Provider( certificates_.setMaxSize(cache_size_); // Set default TLS Certificate - envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = - new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); + TlsCertificatePtr tls_certificate = + std::make_unique(); tls_certificate->mutable_certificate_chain()->set_inline_string(default_identity_cert_); tls_certificate->mutable_private_key()->set_inline_string(default_identity_key_); - certificates_.insert("default", tls_certificate); + certificates_.insert("default", std::move(tls_certificate)); } Envoy::CertificateProvider::CertificateProvider::Capabilities Provider::capabilities() const { @@ -48,8 +49,7 @@ Envoy::CertificateProvider::CertificateProvider::Capabilities Provider::capabili const std::string Provider::trustedCA(const std::string&) const { return ""; } -std::vector< - std::reference_wrapper> +std::vector> Provider::tlsCertificates(const std::string&) const { ASSERT(main_thread_dispatcher_.isThreadSafe()); return certificates_.getCertificates(); @@ -163,13 +163,13 @@ void Provider::signCertificate(const std::string sni, std::string key_pem(reinterpret_cast(output), length); // Generate TLS Certificate - envoy::extensions::transport_sockets::tls::v3::TlsCertificate* tls_certificate = - new envoy::extensions::transport_sockets::tls::v3::TlsCertificate(); + TlsCertificatePtr tls_certificate = + std::make_unique(); tls_certificate->mutable_certificate_chain()->set_inline_string(cert_pem); tls_certificate->mutable_private_key()->set_inline_string(key_pem); // Update certificates_ map - certificates_.insert(sni, tls_certificate); + certificates_.insert(sni, std::move(tls_certificate)); runAddUpdateCallback(); runOnDemandUpdateCallback(sni, metadata, thread_local_dispatcher, false); @@ -181,7 +181,7 @@ void Provider::setSubjectToCSR(absl::string_view subject, X509_REQ* req) { const std::string delim = ","; std::string item; for (absl::string_view v : absl::StrSplit(subject, delim)) { - // This happens when peer subject from connectioninfo contains escaped comma, + // This happens when peer subject from connection info contains escaped comma, // like O=Technology Co.\\, Ltd, have to remove the double backslash. if (v.back() == '\\') { absl::StrAppend(&item, v.substr(0, v.length() - 1), delim); diff --git a/source/extensions/certificate_providers/local_certificate/local_certificate.h b/source/extensions/certificate_providers/local_certificate/local_certificate.h index 106590eb19..9996455454 100644 --- a/source/extensions/certificate_providers/local_certificate/local_certificate.h +++ b/source/extensions/certificate_providers/local_certificate/local_certificate.h @@ -18,6 +18,8 @@ namespace LocalCertificate { using ::google::simple_lru_cache::SimpleLRUCache; using TlsCertificate = envoy::extensions::transport_sockets::tls::v3::TlsCertificate; +using TlsCertificatePtr = + std::unique_ptr; // Default cert cache size constexpr int CacheDefaultSize = 1024; @@ -50,9 +52,9 @@ class CertCacheImpl { return false; } - void insert(const std::string& host, const TlsCertificate* cert) { + void insert(const std::string& host, TlsCertificatePtr&& cert) { if (cert_cache_) { - cert_cache_->insert(host, cert, 1); + cert_cache_->insert(host, cert.release(), 1); } } diff --git a/source/extensions/filters/network/bumping/bumping.cc b/source/extensions/filters/network/bumping/bumping.cc index 2f66842555..1661263bed 100644 --- a/source/extensions/filters/network/bumping/bumping.cc +++ b/source/extensions/filters/network/bumping/bumping.cc @@ -223,7 +223,7 @@ void Filter::onGenericPoolReady(StreamInfo::StreamInfo*, const Network::ConnectionInfoProvider&, Ssl::ConnectionInfoConstSharedPtr info) { - // Request mimick cert from local certificate provider. + // Request cert from local certificate provider. requestCertificate(info); // upstream_ = std::move(upstream); generic_conn_pool_.reset(); @@ -232,7 +232,7 @@ void Filter::onGenericPoolReady(StreamInfo::StreamInfo*, void Filter::requestCertificate(Ssl::ConnectionInfoConstSharedPtr info) { if (info == nullptr) { - // if no info provided, only check if cert is present and determime whether or not + // if no info provided, only check if cert is present and determine whether or not // to establish connection with upstream config_->main_dispatcher_.post([this]() { this->on_demand_handle_ = config_->tls_certificate_provider_->addOnDemandUpdateCallback( From b0c81136e6e8968bca973be4eab51e39ce393c0c Mon Sep 17 00:00:00 2001 From: Luyao Zhong Date: Tue, 20 Dec 2022 12:35:53 +0000 Subject: [PATCH 35/35] add matt to CODEOWNER to pass format check Signed-off-by: Luyao Zhong --- CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index b30e99bf10..782846fc64 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -8,6 +8,8 @@ /*/extensions/access_loggers/common @auni53 @zuercher /*/extensions/access_loggers/open_telemetry @itamarkam @yanavlasov /*/extensions/access_loggers/stream @mattklein123 @davinci26 +# certificate providers +/*/extensions/certificate_providers/local_certificate @liverbirdkte @LuyaoZhongi @mattklein123 # alternate protocols cache extensions /*/extensions/filters/http/alternate_protocols_cache @RyanTheOptimist @alyssawilk # csrf extension @@ -20,6 +22,8 @@ extensions/filters/common/original_src @snowp @klarose # dubbo_proxy extension /*/extensions/filters/network/dubbo_proxy @zyfjeff @lizan @wbpcode +# bumping filter extension +/*/extensions/filters/network/bumping @liverbirdkte @LuyaoZhong @mattklein123 # cdn_loop extension /*/extensions/filters/http/cdn_loop @justin-mp @penguingao @alyssawilk # external processing filter