diff --git a/CODEOWNERS b/CODEOWNERS index 978720712303..0d304eff72f1 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -41,6 +41,8 @@ extensions/filters/common/original_src @snowp @klarose /*/extensions/transport_sockets/alts @htuch @yangminzhu # tls transport socket extension /*/extensions/transport_sockets/tls @lizan @asraa @ggreenway +# tls SPIFFE certificate validator extension +/*/extensions/transport_sockets/tls/cert_validator/spiffe @mathetake @lizan # proxy protocol socket extension /*/extensions/transport_sockets/proxy_protocol @alyssawilk @wez470 # common transport socket diff --git a/api/envoy/extensions/transport_sockets/tls/v3/common.proto b/api/envoy/extensions/transport_sockets/tls/v3/common.proto index 1def231a3795..3fdb751152c9 100644 --- a/api/envoy/extensions/transport_sockets/tls/v3/common.proto +++ b/api/envoy/extensions/transport_sockets/tls/v3/common.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.extensions.transport_sockets.tls.v3; import "envoy/config/core/v3/base.proto"; +import "envoy/config/core/v3/extension.proto"; import "envoy/type/matcher/v3/string.proto"; import "google/protobuf/any.proto"; @@ -211,7 +212,7 @@ message TlsSessionTicketKeys { [(validate.rules).repeated = {min_items: 1}, (udpa.annotations.sensitive) = true]; } -// [#next-free-field: 12] +// [#next-free-field: 13] message CertificateValidationContext { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.auth.CertificateValidationContext"; @@ -371,4 +372,16 @@ message CertificateValidationContext { // Certificate trust chain verification mode. TrustChainVerification trust_chain_verification = 10 [(validate.rules).enum = {defined_only: true}]; + + // The configuration of an extension specific certificate validator. + // If specified, all validation is done by the specified validator, + // and the behavior of all other validation settings is defined by the specified validator (and may be entirely ignored, unused, and unvalidated). + // Refer to the documentation for the specified validator. If you do not want a custom validation algorithm, do not set this field. + // The following names are available here: + // + // .. _extension_envoy.tls.cert_validator.spiffe: + // + // **envoy.tls.cert_validator.spiffe**: `SPIFFE `_ certificate validator. + // Please refer to :ref:`envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig ` for more information. + config.core.v3.TypedExtensionConfig custom_validator_config = 12; } diff --git a/api/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto b/api/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto new file mode 100644 index 000000000000..b6fb921d65b8 --- /dev/null +++ b/api/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.tls.v3; + +import "envoy/config/core/v3/base.proto"; + +import "udpa/annotations/sensitive.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v3"; +option java_outer_classname = "TlsSpiffeValidatorConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: SPIFFE Certificate Validator] + +// Configuration specific to the SPIFFE certificate validator provided at +// :ref:`envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext.custom_validator_config`. +// +// Example: +// +// .. code-block:: yaml +// +// custom_validator_config: +// name: envoy.tls.cert_validator.spiffe +// typed_config: +// "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig +// trust_domains: +// - name: foo.com +// trust_bundle: +// filename: "foo.pem" +// - name: envoy.com +// trust_bundle: +// filename: "envoy.pem" +// +// In this example, a presented peer certificate whose SAN matches `spiffe//foo.com/**` is validated against +// the "foo.pem" x.509 certificate. All the trust bundles are isolated from each other, so no trust domain can mint +// a SVID belonging to another trust domain. That means, in this example, a SVID signed by `envoy.com`'s CA with `spiffe//foo.com/**` +// SAN would be rejected since Envoy selects the trust bundle according to the presented SAN before validate the certificate. +message SPIFFECertValidatorConfig { + message TrustDomain { + // Name of the trust domain, `example.com`, `foo.bar.gov` for example. + // Note that this must *not* have "spiffe://" prefix. + string name = 1 [(validate.rules).string = {min_len: 1}]; + + // Specify a data source holding x.509 trust bundle used for validating incoming SVID(s) in this trust domain. + config.core.v3.DataSource trust_bundle = 2; + } + + // This field specifies trust domains used for validating incoming X.509-SVID(s). + repeated TrustDomain trust_domains = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/api/envoy/extensions/transport_sockets/tls/v4alpha/common.proto b/api/envoy/extensions/transport_sockets/tls/v4alpha/common.proto index c797f95bf2e6..69f3f6ea30fd 100644 --- a/api/envoy/extensions/transport_sockets/tls/v4alpha/common.proto +++ b/api/envoy/extensions/transport_sockets/tls/v4alpha/common.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.extensions.transport_sockets.tls.v4alpha; import "envoy/config/core/v4alpha/base.proto"; +import "envoy/config/core/v4alpha/extension.proto"; import "envoy/type/matcher/v4alpha/string.proto"; import "google/protobuf/any.proto"; @@ -213,7 +214,7 @@ message TlsSessionTicketKeys { [(validate.rules).repeated = {min_items: 1}, (udpa.annotations.sensitive) = true]; } -// [#next-free-field: 12] +// [#next-free-field: 13] message CertificateValidationContext { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext"; @@ -373,4 +374,16 @@ message CertificateValidationContext { // Certificate trust chain verification mode. TrustChainVerification trust_chain_verification = 10 [(validate.rules).enum = {defined_only: true}]; + + // The configuration of an extension specific certificate validator. + // If specified, all validation is done by the specified validator, + // and the behavior of all other validation settings is defined by the specified validator (and may be entirely ignored, unused, and unvalidated). + // Refer to the documentation for the specified validator. If you do not want a custom validation algorithm, do not set this field. + // The following names are available here: + // + // .. _extension_envoy.tls.cert_validator.spiffe: + // + // **envoy.tls.cert_validator.spiffe**: `SPIFFE `_ certificate validator. + // Please refer to :ref:`envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig ` for more information. + config.core.v4alpha.TypedExtensionConfig custom_validator_config = 12; } diff --git a/api/envoy/extensions/transport_sockets/tls/v4alpha/tls_spiffe_validator_config.proto b/api/envoy/extensions/transport_sockets/tls/v4alpha/tls_spiffe_validator_config.proto new file mode 100644 index 000000000000..27770eece81a --- /dev/null +++ b/api/envoy/extensions/transport_sockets/tls/v4alpha/tls_spiffe_validator_config.proto @@ -0,0 +1,60 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.tls.v4alpha; + +import "envoy/config/core/v4alpha/base.proto"; + +import "udpa/annotations/sensitive.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v4alpha"; +option java_outer_classname = "TlsSpiffeValidatorConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: SPIFFE Certificate Validator] + +// Configuration specific to the SPIFFE certificate validator provided at +// :ref:`envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext.custom_validator_config`. +// +// Example: +// +// .. code-block:: yaml +// +// custom_validator_config: +// name: envoy.tls.cert_validator.spiffe +// typed_config: +// "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig +// trust_domains: +// - name: foo.com +// trust_bundle: +// filename: "foo.pem" +// - name: envoy.com +// trust_bundle: +// filename: "envoy.pem" +// +// In this example, a presented peer certificate whose SAN matches `spiffe//foo.com/**` is validated against +// the "foo.pem" x.509 certificate. All the trust bundles are isolated from each other, so no trust domain can mint +// a SVID belonging to another trust domain. That means, in this example, a SVID signed by `envoy.com`'s CA with `spiffe//foo.com/**` +// SAN would be rejected since Envoy selects the trust bundle according to the presented SAN before validate the certificate. +message SPIFFECertValidatorConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig"; + + message TrustDomain { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig.TrustDomain"; + + // Name of the trust domain, `example.com`, `foo.bar.gov` for example. + // Note that this must *not* have "spiffe://" prefix. + string name = 1 [(validate.rules).string = {min_len: 1}]; + + // Specify a data source holding x.509 trust bundle used for validating incoming SVID(s) in this trust domain. + config.core.v4alpha.DataSource trust_bundle = 2; + } + + // This field specifies trust domains used for validating incoming X.509-SVID(s). + repeated TrustDomain trust_domains = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/bazel/envoy_library.bzl b/bazel/envoy_library.bzl index 0131f3c132c7..0f9d688c03ae 100644 --- a/bazel/envoy_library.bzl +++ b/bazel/envoy_library.bzl @@ -91,6 +91,7 @@ EXTENSION_CATEGORIES = [ "envoy.tracers", "envoy.transport_sockets.downstream", "envoy.transport_sockets.upstream", + "envoy.tls.cert_validator", "envoy.upstreams", "envoy.wasm.runtime", "DELIBERATELY_OMITTED", diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 5eb52cb9ecc5..ce1aeba00abd 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -123,6 +123,7 @@ New Features * tcp_proxy: added a :ref:`use_post field ` for using HTTP POST to proxy TCP streams. * tcp_proxy: added a :ref:`headers_to_add field ` for setting additional headers to the HTTP requests for TCP proxing. * thrift_proxy: added a :ref:`max_requests_per_connection field ` for setting maximum requests for per downstream connection. +* tls peer certificate validation: added :ref:`SPIFFE validator ` for supporting isolated multiple trust bundles in a single listener or cluster. Deprecated ---------- diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/tls/v3/common.proto b/generated_api_shadow/envoy/extensions/transport_sockets/tls/v3/common.proto index 2ddca5720fc8..91e77dd5ef4e 100644 --- a/generated_api_shadow/envoy/extensions/transport_sockets/tls/v3/common.proto +++ b/generated_api_shadow/envoy/extensions/transport_sockets/tls/v3/common.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.extensions.transport_sockets.tls.v3; import "envoy/config/core/v3/base.proto"; +import "envoy/config/core/v3/extension.proto"; import "envoy/type/matcher/v3/string.proto"; import "google/protobuf/any.proto"; @@ -211,7 +212,7 @@ message TlsSessionTicketKeys { [(validate.rules).repeated = {min_items: 1}, (udpa.annotations.sensitive) = true]; } -// [#next-free-field: 12] +// [#next-free-field: 13] message CertificateValidationContext { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.auth.CertificateValidationContext"; @@ -370,5 +371,17 @@ message CertificateValidationContext { TrustChainVerification trust_chain_verification = 10 [(validate.rules).enum = {defined_only: true}]; + // The configuration of an extension specific certificate validator. + // If specified, all validation is done by the specified validator, + // and the behavior of all other validation settings is defined by the specified validator (and may be entirely ignored, unused, and unvalidated). + // Refer to the documentation for the specified validator. If you do not want a custom validation algorithm, do not set this field. + // The following names are available here: + // + // .. _extension_envoy.tls.cert_validator.spiffe: + // + // **envoy.tls.cert_validator.spiffe**: `SPIFFE `_ certificate validator. + // Please refer to :ref:`envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig ` for more information. + config.core.v3.TypedExtensionConfig custom_validator_config = 12; + repeated string hidden_envoy_deprecated_verify_subject_alt_name = 4 [deprecated = true]; } diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto b/generated_api_shadow/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto new file mode 100644 index 000000000000..b6fb921d65b8 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.tls.v3; + +import "envoy/config/core/v3/base.proto"; + +import "udpa/annotations/sensitive.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v3"; +option java_outer_classname = "TlsSpiffeValidatorConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: SPIFFE Certificate Validator] + +// Configuration specific to the SPIFFE certificate validator provided at +// :ref:`envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext.custom_validator_config`. +// +// Example: +// +// .. code-block:: yaml +// +// custom_validator_config: +// name: envoy.tls.cert_validator.spiffe +// typed_config: +// "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig +// trust_domains: +// - name: foo.com +// trust_bundle: +// filename: "foo.pem" +// - name: envoy.com +// trust_bundle: +// filename: "envoy.pem" +// +// In this example, a presented peer certificate whose SAN matches `spiffe//foo.com/**` is validated against +// the "foo.pem" x.509 certificate. All the trust bundles are isolated from each other, so no trust domain can mint +// a SVID belonging to another trust domain. That means, in this example, a SVID signed by `envoy.com`'s CA with `spiffe//foo.com/**` +// SAN would be rejected since Envoy selects the trust bundle according to the presented SAN before validate the certificate. +message SPIFFECertValidatorConfig { + message TrustDomain { + // Name of the trust domain, `example.com`, `foo.bar.gov` for example. + // Note that this must *not* have "spiffe://" prefix. + string name = 1 [(validate.rules).string = {min_len: 1}]; + + // Specify a data source holding x.509 trust bundle used for validating incoming SVID(s) in this trust domain. + config.core.v3.DataSource trust_bundle = 2; + } + + // This field specifies trust domains used for validating incoming X.509-SVID(s). + repeated TrustDomain trust_domains = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/tls/v4alpha/common.proto b/generated_api_shadow/envoy/extensions/transport_sockets/tls/v4alpha/common.proto index c797f95bf2e6..69f3f6ea30fd 100644 --- a/generated_api_shadow/envoy/extensions/transport_sockets/tls/v4alpha/common.proto +++ b/generated_api_shadow/envoy/extensions/transport_sockets/tls/v4alpha/common.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.extensions.transport_sockets.tls.v4alpha; import "envoy/config/core/v4alpha/base.proto"; +import "envoy/config/core/v4alpha/extension.proto"; import "envoy/type/matcher/v4alpha/string.proto"; import "google/protobuf/any.proto"; @@ -213,7 +214,7 @@ message TlsSessionTicketKeys { [(validate.rules).repeated = {min_items: 1}, (udpa.annotations.sensitive) = true]; } -// [#next-free-field: 12] +// [#next-free-field: 13] message CertificateValidationContext { option (udpa.annotations.versioning).previous_message_type = "envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext"; @@ -373,4 +374,16 @@ message CertificateValidationContext { // Certificate trust chain verification mode. TrustChainVerification trust_chain_verification = 10 [(validate.rules).enum = {defined_only: true}]; + + // The configuration of an extension specific certificate validator. + // If specified, all validation is done by the specified validator, + // and the behavior of all other validation settings is defined by the specified validator (and may be entirely ignored, unused, and unvalidated). + // Refer to the documentation for the specified validator. If you do not want a custom validation algorithm, do not set this field. + // The following names are available here: + // + // .. _extension_envoy.tls.cert_validator.spiffe: + // + // **envoy.tls.cert_validator.spiffe**: `SPIFFE `_ certificate validator. + // Please refer to :ref:`envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig ` for more information. + config.core.v4alpha.TypedExtensionConfig custom_validator_config = 12; } diff --git a/generated_api_shadow/envoy/extensions/transport_sockets/tls/v4alpha/tls_spiffe_validator_config.proto b/generated_api_shadow/envoy/extensions/transport_sockets/tls/v4alpha/tls_spiffe_validator_config.proto new file mode 100644 index 000000000000..27770eece81a --- /dev/null +++ b/generated_api_shadow/envoy/extensions/transport_sockets/tls/v4alpha/tls_spiffe_validator_config.proto @@ -0,0 +1,60 @@ +syntax = "proto3"; + +package envoy.extensions.transport_sockets.tls.v4alpha; + +import "envoy/config/core/v4alpha/base.proto"; + +import "udpa/annotations/sensitive.proto"; +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.tls.v4alpha"; +option java_outer_classname = "TlsSpiffeValidatorConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = NEXT_MAJOR_VERSION_CANDIDATE; + +// [#protodoc-title: SPIFFE Certificate Validator] + +// Configuration specific to the SPIFFE certificate validator provided at +// :ref:`envoy.extensions.transport_sockets.tls.v3.CertificateValidationContext.custom_validator_config`. +// +// Example: +// +// .. code-block:: yaml +// +// custom_validator_config: +// name: envoy.tls.cert_validator.spiffe +// typed_config: +// "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig +// trust_domains: +// - name: foo.com +// trust_bundle: +// filename: "foo.pem" +// - name: envoy.com +// trust_bundle: +// filename: "envoy.pem" +// +// In this example, a presented peer certificate whose SAN matches `spiffe//foo.com/**` is validated against +// the "foo.pem" x.509 certificate. All the trust bundles are isolated from each other, so no trust domain can mint +// a SVID belonging to another trust domain. That means, in this example, a SVID signed by `envoy.com`'s CA with `spiffe//foo.com/**` +// SAN would be rejected since Envoy selects the trust bundle according to the presented SAN before validate the certificate. +message SPIFFECertValidatorConfig { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig"; + + message TrustDomain { + option (udpa.annotations.versioning).previous_message_type = + "envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig.TrustDomain"; + + // Name of the trust domain, `example.com`, `foo.bar.gov` for example. + // Note that this must *not* have "spiffe://" prefix. + string name = 1 [(validate.rules).string = {min_len: 1}]; + + // Specify a data source holding x.509 trust bundle used for validating incoming SVID(s) in this trust domain. + config.core.v4alpha.DataSource trust_bundle = 2; + } + + // This field specifies trust domains used for validating incoming X.509-SVID(s). + repeated TrustDomain trust_domains = 1 [(validate.rules).repeated = {min_items: 1}]; +} diff --git a/include/envoy/ssl/BUILD b/include/envoy/ssl/BUILD index 08266cf1376f..30f15f687f3e 100644 --- a/include/envoy/ssl/BUILD +++ b/include/envoy/ssl/BUILD @@ -57,6 +57,7 @@ envoy_cc_library( envoy_cc_library( name = "certificate_validation_context_config_interface", hdrs = ["certificate_validation_context_config.h"], + external_deps = ["abseil_optional"], deps = [ "//source/common/common:matchers_lib", "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", @@ -67,14 +68,11 @@ envoy_cc_library( envoy_cc_library( name = "ssl_socket_extended_info_interface", hdrs = ["ssl_socket_extended_info.h"], - deps = [ - ], ) envoy_cc_library( name = "ssl_socket_state", hdrs = ["ssl_socket_state.h"], - deps = [], ) envoy_cc_library( diff --git a/include/envoy/ssl/certificate_validation_context_config.h b/include/envoy/ssl/certificate_validation_context_config.h index 5011cb1226f0..4098c0b7369c 100644 --- a/include/envoy/ssl/certificate_validation_context_config.h +++ b/include/envoy/ssl/certificate_validation_context_config.h @@ -4,10 +4,13 @@ #include #include +#include "envoy/api/api.h" #include "envoy/common/pure.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" #include "envoy/type/matcher/v3/string.pb.h" +#include "absl/types/optional.h" + namespace Envoy { namespace Ssl { @@ -69,6 +72,17 @@ class CertificateValidationContextConfig { virtual envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext:: TrustChainVerification trustChainVerification() const PURE; + + /** + * @return the configuration for the custom certificate validator if configured. + */ + virtual const absl::optional& + customValidatorConfig() const PURE; + + /** + * @return a reference to the api object. + */ + virtual Api::Api& api() const PURE; }; using CertificateValidationContextConfigPtr = std::unique_ptr; diff --git a/source/common/ssl/BUILD b/source/common/ssl/BUILD index 0be754cc4809..c02d221dd528 100644 --- a/source/common/ssl/BUILD +++ b/source/common/ssl/BUILD @@ -26,6 +26,7 @@ envoy_cc_library( name = "certificate_validation_context_config_impl_lib", srcs = ["certificate_validation_context_config_impl.cc"], hdrs = ["certificate_validation_context_config_impl.h"], + external_deps = ["abseil_optional"], deps = [ "//include/envoy/api:api_interface", "//include/envoy/ssl:certificate_validation_context_config_interface", diff --git a/source/common/ssl/certificate_validation_context_config_impl.cc b/source/common/ssl/certificate_validation_context_config_impl.cc index 2f4a1ac8bc84..a8a50fa01482 100644 --- a/source/common/ssl/certificate_validation_context_config_impl.cc +++ b/source/common/ssl/certificate_validation_context_config_impl.cc @@ -32,7 +32,13 @@ CertificateValidationContextConfigImpl::CertificateValidationContextConfigImpl( verify_certificate_spki_list_(config.verify_certificate_spki().begin(), config.verify_certificate_spki().end()), allow_expired_certificate_(config.allow_expired_certificate()), - trust_chain_verification_(config.trust_chain_verification()) { + trust_chain_verification_(config.trust_chain_verification()), + custom_validator_config_( + config.has_custom_validator_config() + ? absl::make_optional( + config.custom_validator_config()) + : absl::nullopt), + api_(api) { if (ca_cert_.empty()) { if (!certificate_revocation_list_.empty()) { throw EnvoyException(fmt::format("Failed to load CRL from {} without trusted CA", diff --git a/source/common/ssl/certificate_validation_context_config_impl.h b/source/common/ssl/certificate_validation_context_config_impl.h index 1636c2ed0713..56765baa7434 100644 --- a/source/common/ssl/certificate_validation_context_config_impl.h +++ b/source/common/ssl/certificate_validation_context_config_impl.h @@ -44,6 +44,13 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte return trust_chain_verification_; } + const absl::optional& + customValidatorConfig() const override { + return custom_validator_config_; + } + + Api::Api& api() const override { return api_; } + private: const std::string ca_cert_; const std::string ca_cert_path_; @@ -56,6 +63,8 @@ class CertificateValidationContextConfigImpl : public CertificateValidationConte const bool allow_expired_certificate_; const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext:: TrustChainVerification trust_chain_verification_; + const absl::optional custom_validator_config_; + Api::Api& api_; }; } // namespace Ssl diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 4e43431bb08f..b8327d794181 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -249,6 +249,12 @@ EXTENSIONS = { # "envoy.io_socket.user_space": "//source/extensions/io_socket/user_space:config", + + # + # TLS peer certification validators + # + + "envoy.tls.cert_validator.spiffe": "//source/extensions/transport_sockets/tls/cert_validator/spiffe:config", } # These can be changed to ["//visibility:public"], for downstream builds which diff --git a/source/extensions/quic_listeners/quiche/BUILD b/source/extensions/quic_listeners/quiche/BUILD index 689eb54758b2..553250ec1834 100644 --- a/source/extensions/quic_listeners/quiche/BUILD +++ b/source/extensions/quic_listeners/quiche/BUILD @@ -107,7 +107,6 @@ envoy_cc_library( ":envoy_quic_proof_verifier_base_lib", ":envoy_quic_utils_lib", "//source/extensions/transport_sockets/tls:context_lib", - "//source/extensions/transport_sockets/tls/cert_validator:cert_validator_lib", ], ) diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.cc b/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.cc index 98b52e8ccc0e..4064f45e205b 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.cc +++ b/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.cc @@ -1,6 +1,7 @@ #include "extensions/quic_listeners/quiche/envoy_quic_proof_verifier.h" #include "extensions/quic_listeners/quiche/envoy_quic_utils.h" +#include "extensions/transport_sockets/tls/cert_validator/default_validator.h" #include "quiche/quic/core/crypto/certificate_view.h" diff --git a/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.h b/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.h index 983ed91df703..a29eb999119f 100644 --- a/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.h +++ b/source/extensions/quic_listeners/quiche/envoy_quic_proof_verifier.h @@ -1,7 +1,6 @@ #pragma once #include "extensions/quic_listeners/quiche/envoy_quic_proof_verifier_base.h" -#include "extensions/transport_sockets/tls/cert_validator/default_validator.h" #include "extensions/transport_sockets/tls/context_impl.h" namespace Envoy { diff --git a/source/extensions/transport_sockets/tls/cert_validator/BUILD b/source/extensions/transport_sockets/tls/cert_validator/BUILD index 9b7e0aaca992..9c9340cf567f 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/BUILD +++ b/source/extensions/transport_sockets/tls/cert_validator/BUILD @@ -12,15 +12,19 @@ envoy_cc_library( name = "cert_validator_lib", srcs = [ "default_validator.cc", + "factory.cc", ], hdrs = [ "cert_validator.h", "default_validator.h", + "factory.h", + "well_known_names.h", ], external_deps = [ "ssl", + "abseil_base", + "abseil_hash", ], - # TLS is core functionality. visibility = ["//visibility:public"], deps = [ "//include/envoy/ssl:context_config_interface", diff --git a/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h b/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h index e5ae5b2c1678..9b55c1cf6fa1 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h +++ b/source/extensions/transport_sockets/tls/cert_validator/cert_validator.h @@ -18,7 +18,6 @@ #include "extensions/transport_sockets/tls/stats.h" -#include "absl/synchronization/mutex.h" #include "openssl/ssl.h" #include "openssl/x509v3.h" diff --git a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc index 4c3f243e9b53..c9177d390363 100644 --- a/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc +++ b/source/extensions/transport_sockets/tls/cert_validator/default_validator.cc @@ -7,6 +7,7 @@ #include #include "envoy/network/transport_socket.h" +#include "envoy/registry/registry.h" #include "envoy/ssl/context.h" #include "envoy/ssl/context_config.h" #include "envoy/ssl/private_key/private_key.h" @@ -25,6 +26,8 @@ #include "common/stats/utility.h" #include "extensions/transport_sockets/tls/cert_validator/cert_validator.h" +#include "extensions/transport_sockets/tls/cert_validator/factory.h" +#include "extensions/transport_sockets/tls/cert_validator/well_known_names.h" #include "extensions/transport_sockets/tls/stats.h" #include "extensions/transport_sockets/tls/utility.h" @@ -473,6 +476,18 @@ size_t DefaultCertValidator::daysUntilFirstCertExpires() const { return Utility::getDaysUntilExpiration(ca_cert_.get(), time_source_); } +class DefaultCertValidatorFactory : public CertValidatorFactory { +public: + CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, + SslStats& stats, TimeSource& time_source) override { + return std::make_unique(config, stats, time_source); + } + + absl::string_view name() override { return CertValidatorNames::get().Default; } +}; + +REGISTER_FACTORY(DefaultCertValidatorFactory, CertValidatorFactory); + } // namespace Tls } // namespace TransportSockets } // namespace Extensions diff --git a/source/extensions/transport_sockets/tls/cert_validator/factory.cc b/source/extensions/transport_sockets/tls/cert_validator/factory.cc new file mode 100644 index 000000000000..349b04e260b4 --- /dev/null +++ b/source/extensions/transport_sockets/tls/cert_validator/factory.cc @@ -0,0 +1,21 @@ +#include "extensions/transport_sockets/tls/cert_validator/factory.h" + +#include "envoy/ssl/context_config.h" + +#include "extensions/transport_sockets/tls/cert_validator/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +std::string getCertValidatorName(const Envoy::Ssl::CertificateValidationContextConfig* config) { + return config != nullptr && config->customValidatorConfig().has_value() + ? config->customValidatorConfig().value().name() + : CertValidatorNames::get().Default; +}; + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/cert_validator/factory.h b/source/extensions/transport_sockets/tls/cert_validator/factory.h new file mode 100644 index 000000000000..590bf15fcde9 --- /dev/null +++ b/source/extensions/transport_sockets/tls/cert_validator/factory.h @@ -0,0 +1,36 @@ +#pragma once + +#include "envoy/common/pure.h" +#include "envoy/ssl/context_config.h" + +#include "common/common/utility.h" + +#include "extensions/transport_sockets/tls/cert_validator/cert_validator.h" +#include "extensions/transport_sockets/tls/stats.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +std::string getCertValidatorName(const Envoy::Ssl::CertificateValidationContextConfig* config); + +class CertValidatorFactory { +public: + virtual ~CertValidatorFactory() = default; + + virtual CertValidatorPtr + createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + TimeSource& time_source) PURE; + + virtual absl::string_view name() PURE; + + std::string category() { return "envoy.tls.cert_validator"; } +}; + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD b/source/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD new file mode 100644 index 000000000000..d6f74254f3a0 --- /dev/null +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD @@ -0,0 +1,45 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_extension_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_extension_package() + +envoy_cc_extension( + name = "config", + srcs = [ + "spiffe_validator.cc", + ], + hdrs = [ + "spiffe_validator.h", + ], + category = "envoy.tls.cert_validator", + external_deps = [ + "ssl", + "abseil_base", + "abseil_hash", + ], + security_posture = "unknown", + status = "wip", + visibility = ["//visibility:public"], + deps = [ + "//include/envoy/ssl:context_config_interface", + "//include/envoy/ssl:ssl_socket_extended_info_interface", + "//source/common/common:assert_lib", + "//source/common/common:base64_lib", + "//source/common/common:c_smart_ptr_lib", + "//source/common/common:hex_lib", + "//source/common/common:utility_lib", + "//source/common/config:datasource_lib", + "//source/common/config:utility_lib", + "//source/common/stats:symbol_table_lib", + "//source/common/stats:utility_lib", + "//source/extensions/transport_sockets/tls:stats_lib", + "//source/extensions/transport_sockets/tls:utility_lib", + "//source/extensions/transport_sockets/tls/cert_validator:cert_validator_lib", + "@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc new file mode 100644 index 000000000000..72a680ead567 --- /dev/null +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.cc @@ -0,0 +1,264 @@ +#include "extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "envoy/common/pure.h" +#include "envoy/extensions/transport_sockets/tls/v3/tls_spiffe_validator_config.pb.h" +#include "envoy/network/transport_socket.h" +#include "envoy/registry/registry.h" +#include "envoy/ssl/context.h" +#include "envoy/ssl/context_config.h" +#include "envoy/ssl/private_key/private_key.h" +#include "envoy/ssl/ssl_socket_extended_info.h" + +#include "common/common/matchers.h" +#include "common/common/regex.h" +#include "common/config/datasource.h" +#include "common/config/utility.h" +#include "common/protobuf/message_validator_impl.h" +#include "common/stats/symbol_table_impl.h" + +#include "extensions/transport_sockets/tls/cert_validator/factory.h" +#include "extensions/transport_sockets/tls/cert_validator/well_known_names.h" +#include "extensions/transport_sockets/tls/stats.h" +#include "extensions/transport_sockets/tls/utility.h" + +#include "openssl/ssl.h" +#include "openssl/x509v3.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +using SPIFFEConfig = envoy::extensions::transport_sockets::tls::v3::SPIFFECertValidatorConfig; + +SPIFFEValidator::SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, + SslStats& stats, TimeSource& time_source) + : stats_(stats), time_source_(time_source) { + ASSERT(config != nullptr); + + SPIFFEConfig message; + Config::Utility::translateOpaqueConfig(config->customValidatorConfig().value().typed_config(), + ProtobufWkt::Struct(), + ProtobufMessage::getStrictValidationVisitor(), message); + + auto size = message.trust_domains().size(); + trust_bundle_stores_.reserve(size); + for (auto& domain : message.trust_domains()) { + if (trust_bundle_stores_.find(domain.name()) != trust_bundle_stores_.end()) { + throw EnvoyException(absl::StrCat( + "Multiple trust bundles are given for one trust domain for ", domain.name())); + } + + auto cert = Config::DataSource::read(domain.trust_bundle(), true, config->api()); + bssl::UniquePtr bio(BIO_new_mem_buf(const_cast(cert.data()), cert.size())); + RELEASE_ASSERT(bio != nullptr, ""); + bssl::UniquePtr list( + PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr)); + if (list == nullptr || sk_X509_INFO_num(list.get()) == 0) { + throw EnvoyException( + absl::StrCat("Failed to load trusted CA certificate for ", domain.name())); + } + + auto store = X509StorePtr(X509_STORE_new()); + bool has_crl = false; + bool ca_loaded = false; + for (const X509_INFO* item : list.get()) { + if (item->x509) { + X509_STORE_add_cert(store.get(), item->x509); + ca_certs_.push_back(bssl::UniquePtr(item->x509)); + X509_up_ref(item->x509); + if (!ca_loaded) { + // TODO: With the current interface, we cannot return the multiple + // cert information on getCaCertInformation method. + // So temporarily we return the first CA's info here. + ca_loaded = true; + ca_file_name_ = absl::StrCat(domain.name(), ": ", + domain.trust_bundle().filename().empty() + ? "" + : domain.trust_bundle().filename()); + } + } + + if (item->crl) { + has_crl = true; + X509_STORE_add_crl(store.get(), item->crl); + } + } + if (has_crl) { + X509_STORE_set_flags(store.get(), X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + } + trust_bundle_stores_[domain.name()] = std::move(store); + } +} + +void SPIFFEValidator::addClientValidationContext(SSL_CTX* ctx, bool) { + bssl::UniquePtr list(sk_X509_NAME_new( + [](const X509_NAME** a, const X509_NAME** b) -> int { return X509_NAME_cmp(*a, *b); })); + + for (auto& ca : ca_certs_) { + X509_NAME* name = X509_get_subject_name(ca.get()); + + // Check for duplicates. + if (sk_X509_NAME_find(list.get(), nullptr, name)) { + continue; + } + + bssl::UniquePtr name_dup(X509_NAME_dup(name)); + if (name_dup == nullptr || !sk_X509_NAME_push(list.get(), name_dup.release())) { + throw EnvoyException(absl::StrCat("Failed to load trusted client CA certificate")); + } + } + SSL_CTX_set_client_CA_list(ctx, list.release()); +} + +void SPIFFEValidator::updateDigestForSessionId(bssl::ScopedEVP_MD_CTX& md, + uint8_t hash_buffer[EVP_MAX_MD_SIZE], + unsigned hash_length) { + int rc; + for (auto& ca : ca_certs_) { + rc = X509_digest(ca.get(), EVP_sha256(), hash_buffer, &hash_length); + RELEASE_ASSERT(rc == 1, Utility::getLastCryptoError().value_or("")); + RELEASE_ASSERT(hash_length == SHA256_DIGEST_LENGTH, + fmt::format("invalid SHA256 hash length {}", hash_length)); + rc = EVP_DigestUpdate(md.get(), hash_buffer, hash_length); + RELEASE_ASSERT(rc == 1, Utility::getLastCryptoError().value_or("")); + } +} + +int SPIFFEValidator::initializeSslContexts(std::vector, bool) { + return SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; +} + +int SPIFFEValidator::doVerifyCertChain(X509_STORE_CTX* store_ctx, + Ssl::SslExtendedSocketInfo* ssl_extended_info, + X509& leaf_cert, const Network::TransportSocketOptions*) { + if (!SPIFFEValidator::certificatePrecheck(&leaf_cert)) { + if (ssl_extended_info) { + ssl_extended_info->setCertificateValidationStatus(Envoy::Ssl::ClientValidationStatus::Failed); + } + stats_.fail_verify_error_.inc(); + return 0; + } + + auto trust_bundle = getTrustBundleStore(&leaf_cert); + if (!trust_bundle) { + if (ssl_extended_info) { + ssl_extended_info->setCertificateValidationStatus(Envoy::Ssl::ClientValidationStatus::Failed); + } + stats_.fail_verify_error_.inc(); + return 0; + } + + // Set the trust bundle's certificate store on the context, and do the verification. + store_ctx->ctx = trust_bundle; + auto ret = X509_verify_cert(store_ctx); + if (ssl_extended_info) { + ssl_extended_info->setCertificateValidationStatus( + ret == 1 ? Envoy::Ssl::ClientValidationStatus::Validated + : Envoy::Ssl::ClientValidationStatus::Failed); + } + if (!ret) { + stats_.fail_verify_error_.inc(); + } + return ret; +} + +X509_STORE* SPIFFEValidator::getTrustBundleStore(X509* leaf_cert) { + bssl::UniquePtr san_names(static_cast( + X509_get_ext_d2i(leaf_cert, NID_subject_alt_name, nullptr, nullptr))); + if (!san_names) { + return nullptr; + } + + std::string trust_domain; + for (const GENERAL_NAME* general_name : san_names.get()) { + if (general_name->type != GEN_URI) { + continue; + } + + const std::string san = Utility::generalNameAsString(general_name); + trust_domain = SPIFFEValidator::extractTrustDomain(san); + // We can assume that valid SVID has only one URI san. + break; + } + + if (trust_domain.empty()) { + return nullptr; + } + + auto target_store = trust_bundle_stores_.find(trust_domain); + return target_store != trust_bundle_stores_.end() ? target_store->second.get() : nullptr; +} + +bool SPIFFEValidator::certificatePrecheck(X509* leaf_cert) { + // Check basic constrains and key usage. + // https://github.com/spiffe/spiffe/blob/master/standards/X509-SVID.md#52-leaf-validation + auto ext = X509_get_extension_flags(leaf_cert); + if (ext & EXFLAG_CA) { + return false; + } + + auto us = X509_get_key_usage(leaf_cert); + return (us & (KU_CRL_SIGN | KU_KEY_CERT_SIGN)) == 0; +} + +std::string SPIFFEValidator::extractTrustDomain(const std::string& san) { + static const std::string prefix = "spiffe://"; + if (!absl::StartsWith(san, prefix)) { + return ""; + } + + auto pos = san.find('/', prefix.size()); + if (pos != std::string::npos) { + return san.substr(prefix.size(), pos - prefix.size()); + } + return ""; +} + +size_t SPIFFEValidator::daysUntilFirstCertExpires() const { + if (ca_certs_.empty()) { + return 0; + } + size_t ret = SIZE_MAX; + for (auto& cert : ca_certs_) { + size_t tmp = Utility::getDaysUntilExpiration(cert.get(), time_source_); + if (tmp < ret) { + ret = tmp; + } + } + return ret; +} + +Envoy::Ssl::CertificateDetailsPtr SPIFFEValidator::getCaCertInformation() const { + if (ca_certs_.empty()) { + return nullptr; + } + // TODO(mathetake): With the current interface, we cannot pass the multiple cert information. + // So temporarily we return the first CA's info here. + return Utility::certificateDetails(ca_certs_[0].get(), getCaFileName(), time_source_); +}; + +class SPIFFEValidatorFactory : public CertValidatorFactory { +public: + CertValidatorPtr createCertValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, + SslStats& stats, TimeSource& time_source) override { + return std::make_unique(config, stats, time_source); + } + + absl::string_view name() override { return CertValidatorNames::get().SPIFFE; } +}; + +REGISTER_FACTORY(SPIFFEValidatorFactory, CertValidatorFactory); + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h new file mode 100644 index 000000000000..487c6a3fa790 --- /dev/null +++ b/source/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "envoy/common/pure.h" +#include "envoy/network/transport_socket.h" +#include "envoy/ssl/context.h" +#include "envoy/ssl/context_config.h" +#include "envoy/ssl/private_key/private_key.h" +#include "envoy/ssl/ssl_socket_extended_info.h" + +#include "common/common/c_smart_ptr.h" +#include "common/common/matchers.h" +#include "common/stats/symbol_table_impl.h" + +#include "extensions/transport_sockets/tls/cert_validator/cert_validator.h" +#include "extensions/transport_sockets/tls/stats.h" + +#include "openssl/ssl.h" +#include "openssl/x509v3.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +using X509StorePtr = CSmartPtr; + +class SPIFFEValidator : public CertValidator { +public: + SPIFFEValidator(SslStats& stats, TimeSource& time_source) + : stats_(stats), time_source_(time_source){}; + SPIFFEValidator(const Envoy::Ssl::CertificateValidationContextConfig* config, SslStats& stats, + TimeSource& time_source); + ~SPIFFEValidator() override = default; + + // Tls::CertValidator + void addClientValidationContext(SSL_CTX* context, bool require_client_cert) override; + + int doVerifyCertChain(X509_STORE_CTX* store_ctx, Ssl::SslExtendedSocketInfo* ssl_extended_info, + X509& leaf_cert, + const Network::TransportSocketOptions* transport_socket_options) override; + + int initializeSslContexts(std::vector contexts, bool provides_certificates) override; + + void updateDigestForSessionId(bssl::ScopedEVP_MD_CTX& md, uint8_t hash_buffer[EVP_MAX_MD_SIZE], + unsigned hash_length) override; + + size_t daysUntilFirstCertExpires() const override; + std::string getCaFileName() const override { return ca_file_name_; } + Envoy::Ssl::CertificateDetailsPtr getCaCertInformation() const override; + + // Utility functions + X509_STORE* getTrustBundleStore(X509* leaf_cert); + static std::string extractTrustDomain(const std::string& san); + static bool certificatePrecheck(X509* leaf_cert); + absl::flat_hash_map& trustBundleStores() { + return trust_bundle_stores_; + }; + +private: + std::vector> ca_certs_; + std::string ca_file_name_; + absl::flat_hash_map trust_bundle_stores_; + + SslStats& stats_; + TimeSource& time_source_; +}; + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/cert_validator/well_known_names.h b/source/extensions/transport_sockets/tls/cert_validator/well_known_names.h new file mode 100644 index 000000000000..226830cd514c --- /dev/null +++ b/source/extensions/transport_sockets/tls/cert_validator/well_known_names.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "common/singleton/const_singleton.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +/** + * Well-known certificate validator's names. + */ +class CertValidatorValues { +public: + // default certificate validator + const std::string Default = "envoy.tls.cert_validator.default"; + + // SPIFFE(https://github.com/spiffe/spiffe) + const std::string SPIFFE = "envoy.tls.cert_validator.spiffe"; +}; + +using CertValidatorNames = ConstSingleton; + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index 92df19adfde4..bf85f95f647a 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -22,7 +22,7 @@ #include "common/runtime/runtime_features.h" #include "common/stats/utility.h" -#include "extensions/transport_sockets/tls/cert_validator/default_validator.h" +#include "extensions/transport_sockets/tls/cert_validator/factory.h" #include "extensions/transport_sockets/tls/stats.h" #include "extensions/transport_sockets/tls/utility.h" @@ -78,8 +78,18 @@ ContextImpl::ContextImpl(Stats::Scope& scope, const Envoy::Ssl::ContextConfig& c ssl_curves_(stat_name_set_->add("ssl.curves")), ssl_sigalgs_(stat_name_set_->add("ssl.sigalgs")), capabilities_(config.capabilities()) { - cert_validator_ = std::make_unique(config.certificateValidationContext(), - stats_, time_source_); + auto cert_validator_name = getCertValidatorName(config.certificateValidationContext()); + auto cert_validator_factory = + Registry::FactoryRegistry::getFactory(cert_validator_name); + + if (!cert_validator_factory) { + throw EnvoyException( + absl::StrCat("Failed to get certificate validator factory for ", cert_validator_name)); + } + + cert_validator_ = cert_validator_factory->createCertValidator( + config.certificateValidationContext(), stats_, time_source_); + const auto tls_certificates = config.tlsCertificates(); tls_contexts_.resize(std::max(static_cast(1), tls_certificates.size())); diff --git a/test/config/utility.cc b/test/config/utility.cc index 13cfc0eaaff9..a24dc76a802c 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -1091,10 +1091,14 @@ void ConfigHelper::initializeTls( common_tls_context.add_alpn_protocols(Http::Utility::AlpnNames::get().Http11); auto* validation_context = common_tls_context.mutable_validation_context(); - validation_context->mutable_trusted_ca()->set_filename( - TestEnvironment::runfilesPath("test/config/integration/certs/cacert.pem")); - validation_context->add_verify_certificate_hash( - options.expect_client_ecdsa_cert_ ? TEST_CLIENT_ECDSA_CERT_HASH : TEST_CLIENT_CERT_HASH); + if (options.custom_validator_config_) { + validation_context->set_allocated_custom_validator_config(options.custom_validator_config_); + } else { + validation_context->mutable_trusted_ca()->set_filename( + TestEnvironment::runfilesPath("test/config/integration/certs/cacert.pem")); + validation_context->add_verify_certificate_hash( + options.expect_client_ecdsa_cert_ ? TEST_CLIENT_ECDSA_CERT_HASH : TEST_CLIENT_CERT_HASH); + } // We'll negotiate up to TLSv1.3 for the tests that care, but it really // depends on what the client sets. diff --git a/test/config/utility.h b/test/config/utility.h index 7bf60d63b1c1..14f22ef08689 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -68,6 +68,13 @@ class ConfigHelper { return *this; } + ServerSslOptions& setCustomValidatorConfig( + envoy::config::core::v3::TypedExtensionConfig* custom_validator_config) { + custom_validator_config_ = custom_validator_config; + return *this; + } + + envoy::config::core::v3::TypedExtensionConfig* custom_validator_config_; bool rsa_cert_{true}; bool rsa_cert_ocsp_staple_{true}; bool ecdsa_cert_{false}; diff --git a/test/extensions/quic_listeners/quiche/envoy_quic_proof_source_test.cc b/test/extensions/quic_listeners/quiche/envoy_quic_proof_source_test.cc index 082fe1b67d79..e4cb60625833 100644 --- a/test/extensions/quic_listeners/quiche/envoy_quic_proof_source_test.cc +++ b/test/extensions/quic_listeners/quiche/envoy_quic_proof_source_test.cc @@ -70,6 +70,8 @@ class TestGetProofCallback : public quic::ProofSource::Callback { .WillByDefault(ReturnRef(empty_string_list)); ON_CALL(cert_validation_ctx_config_, verifyCertificateSpkiList()) .WillByDefault(ReturnRef(empty_string_list)); + const absl::optional nullopt = absl::nullopt; + ON_CALL(cert_validation_ctx_config_, customValidatorConfig()).WillByDefault(ReturnRef(nullopt)); verifier_ = std::make_unique(store_, client_context_config_, time_system_); } diff --git a/test/extensions/quic_listeners/quiche/envoy_quic_proof_verifier_test.cc b/test/extensions/quic_listeners/quiche/envoy_quic_proof_verifier_test.cc index 9cdc169cd6f5..a0f13fd900e5 100644 --- a/test/extensions/quic_listeners/quiche/envoy_quic_proof_verifier_test.cc +++ b/test/extensions/quic_listeners/quiche/envoy_quic_proof_verifier_test.cc @@ -65,6 +65,8 @@ class EnvoyQuicProofVerifierTest : public testing::Test { .WillRepeatedly(ReturnRef(empty_string_list_)); EXPECT_CALL(cert_validation_ctx_config_, verifyCertificateSpkiList()) .WillRepeatedly(ReturnRef(empty_string_list_)); + EXPECT_CALL(cert_validation_ctx_config_, customValidatorConfig()) + .WillRepeatedly(ReturnRef(custom_validator_config_)); verifier_ = std::make_unique(store_, client_context_config_, time_system_); } @@ -79,6 +81,8 @@ class EnvoyQuicProofVerifierTest : public testing::Test { const std::string cert_chain_{quic::test::kTestCertificateChainPem}; const std::string root_ca_cert_; const std::string leaf_cert_; + const absl::optional custom_validator_config_{ + absl::nullopt}; NiceMock store_; Event::GlobalTimeSystem time_system_; NiceMock client_context_config_; diff --git a/test/extensions/transport_sockets/tls/cert_validator/BUILD b/test/extensions/transport_sockets/tls/cert_validator/BUILD index e8675ccec187..796f127d7718 100644 --- a/test/extensions/transport_sockets/tls/cert_validator/BUILD +++ b/test/extensions/transport_sockets/tls/cert_validator/BUILD @@ -1,6 +1,7 @@ load( "//bazel:envoy_build_system.bzl", "envoy_cc_test", + "envoy_cc_test_library", "envoy_package", ) @@ -8,6 +9,28 @@ licenses(["notice"]) # Apache 2 envoy_package() +envoy_cc_test( + name = "factory_test", + srcs = [ + "factory_test.cc", + ], + deps = [ + ":util", + "//source/extensions/transport_sockets/tls/cert_validator:cert_validator_lib", + ], +) + +envoy_cc_test_library( + name = "util", + hdrs = ["util.h"], + deps = [ + "//include/envoy/ssl:context_config_interface", + "//include/envoy/ssl:ssl_socket_extended_info_interface", + "//source/common/common:macros", + "//test/test_common:utility_lib", + ], +) + envoy_cc_test( name = "default_validator_test", srcs = [ @@ -19,6 +42,7 @@ envoy_cc_test( deps = [ "//source/extensions/transport_sockets/tls/cert_validator:cert_validator_lib", "//test/extensions/transport_sockets/tls:ssl_test_utils", + "//test/extensions/transport_sockets/tls/cert_validator:util", "//test/test_common:environment_lib", "//test/test_common:test_runtime_lib", ], diff --git a/test/extensions/transport_sockets/tls/cert_validator/factory_test.cc b/test/extensions/transport_sockets/tls/cert_validator/factory_test.cc new file mode 100644 index 000000000000..844ca55ee97a --- /dev/null +++ b/test/extensions/transport_sockets/tls/cert_validator/factory_test.cc @@ -0,0 +1,28 @@ +#include + +#include "extensions/transport_sockets/tls/cert_validator/factory.h" + +#include "test/extensions/transport_sockets/tls/cert_validator/util.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +TEST(FactoryTest, TestGetCertValidatorName) { + EXPECT_EQ("envoy.tls.cert_validator.default", getCertValidatorName(nullptr)); + auto config = std::make_unique(); + EXPECT_EQ("envoy.tls.cert_validator.default", getCertValidatorName(config.get())); + + envoy::config::core::v3::TypedExtensionConfig custom_config = {}; + custom_config.set_name("envoy.tls.cert_validator.spiffe"); + config = std::make_unique(custom_config); + EXPECT_EQ(custom_config.name(), getCertValidatorName(config.get())); +} + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD new file mode 100644 index 000000000000..a3f4decb5685 --- /dev/null +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/BUILD @@ -0,0 +1,28 @@ +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_test", + "envoy_package", +) + +licenses(["notice"]) # Apache 2 + +envoy_package() + +envoy_cc_test( + name = "spiffe_validator_test", + srcs = [ + "spiffe_validator_test.cc", + ], + data = [ + "//test/extensions/transport_sockets/tls/test_data:certs", + ], + deps = [ + "//source/extensions/transport_sockets/tls/cert_validator/spiffe:config", + "//test/extensions/transport_sockets/tls:ssl_test_utils", + "//test/extensions/transport_sockets/tls/cert_validator:util", + "//test/test_common:environment_lib", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:test_runtime_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc new file mode 100644 index 000000000000..c16ed75e27b5 --- /dev/null +++ b/test/extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator_test.cc @@ -0,0 +1,403 @@ +#include +#include +#include +#include + +#include "envoy/common/exception.h" + +#include "common/event/real_time_system.h" + +#include "extensions/transport_sockets/tls/cert_validator/spiffe/spiffe_validator.h" +#include "extensions/transport_sockets/tls/stats.h" + +#include "test/extensions/transport_sockets/tls/cert_validator/util.h" +#include "test/extensions/transport_sockets/tls/ssl_test_utility.h" +#include "test/test_common/environment.h" +#include "test/test_common/simulated_time_system.h" +#include "test/test_common/test_runtime.h" +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" +#include "openssl/ssl.h" +#include "openssl/x509v3.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +using TestCertificateValidationContextConfigPtr = + std::unique_ptr; +using SPIFFEValidatorPtr = std::unique_ptr; +using X509StoreContextPtr = CSmartPtr; +using SSLContextPtr = CSmartPtr; + +class TestSPIFFEValidator : public testing::Test { +public: + TestSPIFFEValidator() : stats_(generateSslStats(store_)) {} + void initialize(std::string yaml, TimeSource& time_source) { + envoy::config::core::v3::TypedExtensionConfig typed_conf; + TestUtility::loadFromYaml(yaml, typed_conf); + config_ = std::make_unique(typed_conf); + validator_ = std::make_unique(config_.get(), stats_, time_source); + } + + void initialize(std::string yaml) { + envoy::config::core::v3::TypedExtensionConfig typed_conf; + TestUtility::loadFromYaml(yaml, typed_conf); + config_ = std::make_unique(typed_conf); + validator_ = + std::make_unique(config_.get(), stats_, config_->api().timeSource()); + }; + + void initializeWithNullptr() { + validator_ = std::make_unique(nullptr, stats_, time_system_); + } + + void initialize() { validator_ = std::make_unique(stats_, time_system_); } + + SPIFFEValidator& validator() { return *validator_; } + SslStats& stats() { return stats_; } + +private: + TestCertificateValidationContextConfigPtr config_; + SPIFFEValidatorPtr validator_; + Stats::TestUtil::TestStore store_; + SslStats stats_; + Event::TestRealTimeSystem time_system_; +}; + +TEST_F(TestSPIFFEValidator, InvalidCA) { + EXPECT_THROW_WITH_MESSAGE(initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: hello.com + trust_bundle: + inline_string: "invalid" + )EOF")), + EnvoyException, "Failed to load trusted CA certificate for hello.com"); +} + +TEST_F(TestSPIFFEValidator, Constructor) { + EXPECT_THROW_WITH_MESSAGE(initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: hello.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert_with_crl.pem" + - name: hello.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert_with_crl.pem" + )EOF")), + EnvoyException, + "Multiple trust bundles are given for one trust domain for hello.com"); + + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: hello.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert_with_crl.pem" + )EOF")); + + EXPECT_EQ(1, validator().trustBundleStores().size()); + EXPECT_NE(validator().getCaFileName().find("test_data/ca_cert_with_crl.pem"), std::string::npos); + EXPECT_NE(validator().getCaFileName().find("hello.com"), std::string::npos); + + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: hello.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + - name: k8s-west.example.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert.pem" + )EOF")); + + EXPECT_EQ(2, validator().trustBundleStores().size()); +} + +TEST(SPIFFEValidator, TestExtractTrustDomain) { + EXPECT_EQ("", SPIFFEValidator::extractTrustDomain("foo")); + EXPECT_EQ("", SPIFFEValidator::extractTrustDomain("abc.com/")); + EXPECT_EQ("", SPIFFEValidator::extractTrustDomain("abc.com/workload/")); + EXPECT_EQ("", SPIFFEValidator::extractTrustDomain("spiffe://")); + EXPECT_EQ("abc.com", SPIFFEValidator::extractTrustDomain("spiffe://abc.com/")); + EXPECT_EQ("dev.envoy.com", + SPIFFEValidator::extractTrustDomain("spiffe://dev.envoy.com/workload1")); + EXPECT_EQ("k8s-west.example.com", SPIFFEValidator::extractTrustDomain( + "spiffe://k8s-west.example.com/ns/staging/sa/default")); +} + +TEST(SPIFFEValidator, TestCertificatePrecheck) { + bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( + // basicConstraints: CA:True, + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); + EXPECT_FALSE(SPIFFEValidator::certificatePrecheck(cert.get())); + + cert = readCertFromFile(TestEnvironment::substitute( + // basicConstraints CA:False, keyUsage has keyCertSign + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert.pem")); + EXPECT_FALSE(SPIFFEValidator::certificatePrecheck(cert.get())); + + cert = readCertFromFile(TestEnvironment::substitute( + // basicConstraints CA:False, keyUsage has cRLSign + "{{ test_rundir " + "}}/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert.pem")); + EXPECT_FALSE(SPIFFEValidator::certificatePrecheck(cert.get())); + + cert = readCertFromFile(TestEnvironment::substitute( + // basicConstraints CA:False, keyUsage does not have keyCertSign and cRLSign + // should be considered valid (i.e. return 1). + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/extensions_cert.pem")); + EXPECT_TRUE(SPIFFEValidator::certificatePrecheck(cert.get())); +} + +TEST_F(TestSPIFFEValidator, TestInitializeSslContexts) { + initialize(); + EXPECT_EQ(SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + validator().initializeSslContexts({}, false)); +} + +TEST_F(TestSPIFFEValidator, TestGetTrustBundleStore) { + initialize(); + + // No SAN + auto cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/extensions_cert.pem")); + EXPECT_FALSE(validator().getTrustBundleStore(cert.get())); + + // Non-SPIFFE SAN + cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert.pem")); + EXPECT_FALSE(validator().getTrustBundleStore(cert.get())); + + // SPIFFE SAN + cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem")); + + // Trust bundle not provided. + EXPECT_FALSE(validator().getTrustBundleStore(cert.get())); + + // Trust bundle provided. + validator().trustBundleStores().emplace("example.com", X509StorePtr(X509_STORE_new())); + EXPECT_TRUE(validator().getTrustBundleStore(cert.get())); +} + +TEST_F(TestSPIFFEValidator, TestDoVerifyCertChainPrecheckFailure) { + initialize(); + X509StoreContextPtr store_ctx = X509_STORE_CTX_new(); + bssl::UniquePtr cert = readCertFromFile(TestEnvironment::substitute( + // basicConstraints: CA:True + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem")); + + TestSslExtendedSocketInfo info; + EXPECT_FALSE(validator().doVerifyCertChain(store_ctx.get(), &info, *cert, nullptr)); + EXPECT_EQ(1, stats().fail_verify_error_.value()); + EXPECT_EQ(info.certificateValidationStatus(), Envoy::Ssl::ClientValidationStatus::Failed); +} + +TEST_F(TestSPIFFEValidator, TestDoVerifyCertChainSingleTrustDomain) { + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: lyft.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + )EOF")); + + X509StorePtr ssl_ctx = X509_STORE_new(); + + // Trust domain match so should be accepted. + auto cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_uri_cert.pem")); + X509StoreContextPtr store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), ssl_ctx.get(), cert.get(), nullptr)); + EXPECT_TRUE(validator().doVerifyCertChain(store_ctx.get(), nullptr, *cert, nullptr)); + + // Different trust domain so should be rejected. + cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem")); + + store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), ssl_ctx.get(), cert.get(), nullptr)); + EXPECT_FALSE(validator().doVerifyCertChain(store_ctx.get(), nullptr, *cert, nullptr)); + + // Does not have san. + cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/extensions_cert.pem")); + + store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), ssl_ctx.get(), cert.get(), nullptr)); + EXPECT_FALSE(validator().doVerifyCertChain(store_ctx.get(), nullptr, *cert, nullptr)); + + EXPECT_EQ(2, stats().fail_verify_error_.value()); +} + +TEST_F(TestSPIFFEValidator, TestDoVerifyCertChainMultipleTrustDomain) { + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: lyft.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + - name: example.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + )EOF")); + + X509StorePtr ssl_ctx = X509_STORE_new(); + + // trust domain match so should be accepted + auto cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_uri_cert.pem")); + X509StoreContextPtr store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), ssl_ctx.get(), cert.get(), nullptr)); + EXPECT_TRUE(validator().doVerifyCertChain(store_ctx.get(), nullptr, *cert, nullptr)); + + cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem")); + + store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), ssl_ctx.get(), cert.get(), nullptr)); + EXPECT_TRUE(validator().doVerifyCertChain(store_ctx.get(), nullptr, *cert, nullptr)); + + // does not have san + cert = readCertFromFile(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/extensions_cert.pem")); + + store_ctx = X509_STORE_CTX_new(); + EXPECT_TRUE(X509_STORE_CTX_init(store_ctx.get(), ssl_ctx.get(), cert.get(), nullptr)); + EXPECT_FALSE(validator().doVerifyCertChain(store_ctx.get(), nullptr, *cert, nullptr)); + + EXPECT_EQ(1, stats().fail_verify_error_.value()); +} + +TEST_F(TestSPIFFEValidator, TestGetCaCertInformation) { + initialize(); + EXPECT_FALSE(validator().getCaCertInformation()); // should be nullptr + + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: lyft.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem" + - name: example.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + )EOF")); + + auto actual = validator().getCaCertInformation(); + EXPECT_TRUE(actual); +} + +TEST_F(TestSPIFFEValidator, TestDaysUntilFirstCertExpires) { + initialize(); + EXPECT_EQ(0, validator().daysUntilFirstCertExpires()); + + Event::SimulatedTimeSystem time_system; + time_system.setSystemTime(std::chrono::milliseconds(0)); + + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: lyft.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem" + - name: example.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/intermediate_ca_cert.pem" + )EOF"), + time_system); + EXPECT_EQ(19231, validator().daysUntilFirstCertExpires()); + time_system.setSystemTime(std::chrono::milliseconds(864000000)); + EXPECT_EQ(19221, validator().daysUntilFirstCertExpires()); +} + +TEST_F(TestSPIFFEValidator, TestAddClientValidationContext) { + Event::TestRealTimeSystem time_system; + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: lyft.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem" + - name: example.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + - name: foo.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + )EOF"), + time_system); + + bool foundTestServer = false; + bool foundTestCA = false; + SSLContextPtr ctx = SSL_CTX_new(TLS_method()); + validator().addClientValidationContext(ctx.get(), false); + for (X509_NAME* name : SSL_CTX_get_client_CA_list(ctx.get())) { + const int cn_index = X509_NAME_get_index_by_NID(name, NID_commonName, -1); + EXPECT_TRUE(cn_index >= 0); + X509_NAME_ENTRY* cn_entry = X509_NAME_get_entry(name, cn_index); + EXPECT_TRUE(cn_entry); + ASN1_STRING* cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry); + EXPECT_TRUE(cn_asn1); + + auto cn_str = std::string(reinterpret_cast(ASN1_STRING_data(cn_asn1))); + if (cn_str == "Test Server") { + foundTestServer = true; + } else if (cn_str == "Test CA") { + foundTestCA = true; + } + } + + EXPECT_TRUE(foundTestServer); + EXPECT_TRUE(foundTestCA); +} + +TEST_F(TestSPIFFEValidator, TestUpdateDigestForSessionId) { + Event::TestRealTimeSystem time_system; + initialize(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: lyft.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem" + - name: example.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/ca_cert.pem" + )EOF"), + time_system); + uint8_t hash_buffer[EVP_MAX_MD_SIZE]; + bssl::ScopedEVP_MD_CTX md; + EVP_DigestInit(md.get(), EVP_sha256()); + validator().updateDigestForSessionId(md, hash_buffer, SHA256_DIGEST_LENGTH); +} + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/cert_validator/util.h b/test/extensions/transport_sockets/tls/cert_validator/util.h new file mode 100644 index 000000000000..7691bf50e44d --- /dev/null +++ b/test/extensions/transport_sockets/tls/cert_validator/util.h @@ -0,0 +1,83 @@ +#include + +#include "envoy/ssl/context_config.h" +#include "envoy/ssl/ssl_socket_extended_info.h" + +#include "common/common/macros.h" + +#include "test/test_common/utility.h" + +namespace Envoy { +namespace Extensions { +namespace TransportSockets { +namespace Tls { + +class TestSslExtendedSocketInfo : public Envoy::Ssl::SslExtendedSocketInfo { +public: + TestSslExtendedSocketInfo() = default; + + void setCertificateValidationStatus(Envoy::Ssl::ClientValidationStatus validated) override { + status_ = validated; + } + Envoy::Ssl::ClientValidationStatus certificateValidationStatus() const override { + return status_; + } + +private: + Envoy::Ssl::ClientValidationStatus status_; +}; + +class TestCertificateValidationContextConfig + : public Envoy::Ssl::CertificateValidationContextConfig { +public: + TestCertificateValidationContextConfig(envoy::config::core::v3::TypedExtensionConfig config) + : api_(Api::createApiForTest()), custom_validator_config_(config){}; + TestCertificateValidationContextConfig() + : api_(Api::createApiForTest()), custom_validator_config_(absl::nullopt){}; + + const std::string& caCert() const override { CONSTRUCT_ON_FIRST_USE(std::string, ""); } + const std::string& caCertPath() const override { CONSTRUCT_ON_FIRST_USE(std::string, ""); } + const std::string& certificateRevocationList() const override { + CONSTRUCT_ON_FIRST_USE(std::string, ""); + } + const std::string& certificateRevocationListPath() const final { + CONSTRUCT_ON_FIRST_USE(std::string, ""); + } + const std::vector& verifySubjectAltNameList() const override { + CONSTRUCT_ON_FIRST_USE(std::vector, {}); + } + const std::vector& + subjectAltNameMatchers() const override { + CONSTRUCT_ON_FIRST_USE(std::vector, {}); + } + const std::vector& verifyCertificateHashList() const override { + CONSTRUCT_ON_FIRST_USE(std::vector, {}); + } + const std::vector& verifyCertificateSpkiList() const override { + CONSTRUCT_ON_FIRST_USE(std::vector, {}); + } + bool allowExpiredCertificate() const override { return false; } + envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext:: + TrustChainVerification + trustChainVerification() const override { + return envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext:: + TrustChainVerification:: + CertificateValidationContext_TrustChainVerification_ACCEPT_UNTRUSTED; + } + + const absl::optional& + customValidatorConfig() const override { + return custom_validator_config_; + } + + Api::Api& api() const override { return *api_; } + +private: + Api::ApiPtr api_; + const absl::optional custom_validator_config_; +}; + +} // namespace Tls +} // namespace TransportSockets +} // namespace Extensions +} // namespace Envoy diff --git a/test/extensions/transport_sockets/tls/integration/BUILD b/test/extensions/transport_sockets/tls/integration/BUILD index 277e6346cb21..884f16890d9d 100644 --- a/test/extensions/transport_sockets/tls/integration/BUILD +++ b/test/extensions/transport_sockets/tls/integration/BUILD @@ -16,6 +16,7 @@ envoy_cc_test( ], data = [ "//test/config/integration/certs", + "//test/extensions/transport_sockets/tls/test_data:certs", ], deps = [ "//source/common/event:dispatcher_includes", @@ -25,6 +26,7 @@ envoy_cc_test( "//source/extensions/transport_sockets/tls:config", "//source/extensions/transport_sockets/tls:context_config_lib", "//source/extensions/transport_sockets/tls:context_lib", + "//source/extensions/transport_sockets/tls/cert_validator/spiffe:config", "//test/extensions/common/tap:common", "//test/integration:http_integration_lib", "//test/mocks/secret:secret_mocks", diff --git a/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc b/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc index 16896443a6a8..d32b1d8dcb67 100644 --- a/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc +++ b/test/extensions/transport_sockets/tls/integration/ssl_integration_test.cc @@ -41,7 +41,8 @@ void SslIntegrationTestBase::initialize() { .setEcdsaCertOcspStaple(server_ecdsa_cert_ocsp_staple_) .setOcspStapleRequired(ocsp_staple_required_) .setTlsV13(server_tlsv1_3_) - .setExpectClientEcdsaCert(client_ecdsa_cert_)); + .setExpectClientEcdsaCert(client_ecdsa_cert_) + .setCustomValidatorConfig(custom_validator_config_)); HttpIntegrationTest::initialize(); context_manager_ = @@ -352,6 +353,98 @@ TEST_P(SslCertficateIntegrationTest, ServerRsa) { checkStats(); } +// Server configured on SPIFFE certificate validation for mTLS +// clientcert.pem's san is "spiffe://lyft.com/frontend-team" so it should be accepted. +TEST_P(SslCertficateIntegrationTest, ServerRsaSPIFFEValidatorAccepted) { + auto typed_conf = new envoy::config::core::v3::TypedExtensionConfig(); + TestUtility::loadFromYaml(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: lyft.com + trust_bundle: + filename: "{{ test_rundir }}/test/config/integration/certs/cacert.pem" + )EOF"), + *typed_conf); + + custom_validator_config_ = typed_conf; + server_rsa_cert_ = true; + ConnectionCreationFunction creator = [&]() -> Network::ClientConnectionPtr { + return makeSslClientConnection({}); + }; + testRouterRequestAndResponseWithBody(1024, 512, false, false, &creator); + checkStats(); + Stats::CounterSharedPtr counter = + test_server_->counter(listenerStatPrefix("ssl.fail_verify_error")); + EXPECT_EQ(0, counter->value()); + counter->reset(); +} +// Server configured on SPIFFE certificate validation for mTLS +// clientcert.pem's san is "spiffe://lyft.com/frontend-team" so it should be rejected. +TEST_P(SslCertficateIntegrationTest, ServerRsaSPIFFEValidatorRejected1) { + auto typed_conf = new envoy::config::core::v3::TypedExtensionConfig(); + TestUtility::loadFromYaml(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: example.com + trust_bundle: + filename: "{{ test_rundir }}/test/config/integration/certs/cacert.pem" + )EOF"), + *typed_conf); + custom_validator_config_ = typed_conf; + server_rsa_cert_ = true; + initialize(); + auto conn = makeSslClientConnection({}); + if (tls_version_ == envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_2) { + auto codec = makeRawHttpConnection(std::move(conn), absl::nullopt); + EXPECT_FALSE(codec->connected()); + } else { + makeHttpConnection(std::move(conn))->close(); + } + + Stats::CounterSharedPtr counter = + test_server_->counter(listenerStatPrefix("ssl.fail_verify_error")); + EXPECT_EQ(1, counter->value()); + counter->reset(); +} + +// Server configured on SPIFFE certificate validation for mTLS +// clientcert.pem's san is "spiffe://lyft.com/frontend-team" but the corresponding trust bundle does +// not match with the client cert. So this should also be rejected. +TEST_P(SslCertficateIntegrationTest, ServerRsaSPIFFEValidatorRejected2) { + auto typed_conf = new envoy::config::core::v3::TypedExtensionConfig(); + TestUtility::loadFromYaml(TestEnvironment::substitute(R"EOF( +name: envoy.tls.cert_validator.spiffe +typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.SPIFFECertValidatorConfig + trust_domains: + - name: lyft.com + trust_bundle: + filename: "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/fake_ca_cert.pem" + - name: example.com + trust_bundle: + filename: "{{ test_rundir }}/test/config/integration/certs/cacert.pem" + )EOF"), + *typed_conf); + custom_validator_config_ = typed_conf; + server_rsa_cert_ = true; + initialize(); + auto conn = makeSslClientConnection({}); + if (tls_version_ == envoy::extensions::transport_sockets::tls::v3::TlsParameters::TLSv1_2) { + auto codec = makeRawHttpConnection(std::move(conn), absl::nullopt); + EXPECT_FALSE(codec->connected()); + } else { + makeHttpConnection(std::move(conn))->close(); + } + Stats::CounterSharedPtr counter = + test_server_->counter(listenerStatPrefix("ssl.fail_verify_error")); + EXPECT_EQ(1, counter->value()); + counter->reset(); +} + // Server with an ECDSA certificate and a client with RSA/ECDSA cipher suites works. TEST_P(SslCertficateIntegrationTest, ServerEcdsa) { server_rsa_cert_ = false; diff --git a/test/extensions/transport_sockets/tls/integration/ssl_integration_test.h b/test/extensions/transport_sockets/tls/integration/ssl_integration_test.h index e7f615c54476..909f72dc08f3 100644 --- a/test/extensions/transport_sockets/tls/integration/ssl_integration_test.h +++ b/test/extensions/transport_sockets/tls/integration/ssl_integration_test.h @@ -29,6 +29,7 @@ class SslIntegrationTestBase : public HttpIntegrationTest { void checkStats(); protected: + envoy::config::core::v3::TypedExtensionConfig* custom_validator_config_{nullptr}; bool server_tlsv1_3_{false}; bool server_rsa_cert_{true}; bool server_rsa_cert_ocsp_staple_{false}; diff --git a/test/extensions/transport_sockets/tls/test_data/certs.sh b/test/extensions/transport_sockets/tls/test_data/certs.sh index b1155f18d9fe..da9ce8f7d9cc 100755 --- a/test/extensions/transport_sockets/tls/test_data/certs.sh +++ b/test/extensions/transport_sockets/tls/test_data/certs.sh @@ -260,3 +260,15 @@ generate_x509_cert_nosubject no_subject ca # Generate unit test certificate generate_rsa_key unittest generate_selfsigned_x509_cert unittest + +generate_rsa_key keyusage_cert_sign +generate_x509_cert keyusage_cert_sign ca + +generate_rsa_key keyusage_crl_sign +generate_x509_cert keyusage_crl_sign ca + +generate_rsa_key spiffe_san +generate_x509_cert spiffe_san ca + +generate_rsa_key non_spiffe_san +generate_x509_cert non_spiffe_san ca \ No newline at end of file diff --git a/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert.cfg b/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert.cfg new file mode 100644 index 000000000000..b5daa7d2ca7d --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert.cfg @@ -0,0 +1,35 @@ +[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 Cert +commonName_default = Test Cert +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment, keyCertSign +extendedKeyUsage = clientAuth, serverAuth +subjectKeyIdentifier = hash +1.2.3.4.5.6.7.8 = ASN1:UTF8String:Something +1.2.3.4.5.6.7.9 = DER:30:03:01:01:FF + +[v3_ca] +basicConstraints = critical, CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment, keyCertSign +extendedKeyUsage = clientAuth, serverAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always +1.2.3.4.5.6.7.8 = ASN1:UTF8String:Something +1.2.3.4.5.6.7.9 = DER:30:03:01:01:FF \ No newline at end of file diff --git a/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert.pem b/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert.pem new file mode 100644 index 000000000000..8f6a5adf8f4d --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJDCCAwygAwIBAgIUGf0AE7012IPTiBn6CB+c3SVvbcgwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjEwMjAzMTExNjM4WhcNMjMw +MjAzMTExNjM4WjB4MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW +MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQ +THlmdCBFbmdpbmVlcmluZzESMBAGA1UEAwwJVGVzdCBDZXJ0MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlePKCulIVDnvv7pvlaDtAfR+PJ504y6Ncxgd +nTyNnRCKHBnwlq56WWYkilc60FKdE4DehXWY9akyIlrA8nna3ckZ1PSM9GdHJRCn +JWUh1RirWS8xdhT7zbFH54BnGjj+7KT6JzsMKrP3vYa1Y5Ff/N6q9KFY5hQUWJ8X +gaWqxySi+yxFGhevjp3Gq0FTX3thoeMYOHt5AfwlFnXaKlc+xupWaAFl5VQAM8VS +G8IG+YOsxZmI8wNdk9BIJCUIubvNykYMrwveVkgnCYB1K3wYwNNjJt9Q5bFKtRfl +MKQ4KzugNlWU/PowIbDlla/NwSO866iYkROIZeW9aty5L+1KhQIDAQABo4GnMIGk +MAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgLkMB0GA1UdJQQWMBQGCCsGAQUFBwMC +BggrBgEFBQcDATAdBgNVHQ4EFgQUkGcFiogsv4LFO6e7juYw1Z/PsvUwHwYDVR0j +BBgwFoAU08ELI+PDqPIyvl9UwT94OBNVA9kwFgYHKgMEBQYHCAQLDAlTb21ldGhp +bmcwEAYHKgMEBQYHCQQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADvpQnMwqH09 +TetXraNmhvXUmhnTCaoStVQpWtEvOnnqHZ760Lc2XG1dKL6v40AovGK4PLlf7kds +Uz5oj65Wb7Z7pxz5Aa4P02VocMoASgYFMQrIJe4e0SIwfKX3UUhvmSG3e4mxu3nV +JHm4A2NZ9weuOIx6+6Hb6S8PfMLUbmmf3/ZCvNVMNlwllfsOwpMsDffT29GrbiZO +3UIQ5nq+aKuGzOCjg1tjB0fETdEE2/MqRUMdB8lOcT49ZhsJq/MLGw29NGyCB+ky +59xUk8U9c/C1NwxtaFFJzhMhDlOxjXXxpGt06QbOoH+B2yIW0r4uVymWhAUq/ewx +9RYva+yssd0= +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert_info.h b/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert_info.h new file mode 100644 index 000000000000..2d1971cf2508 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_KEYUSAGE_CERT_SIGN_CERT_256_HASH[] = + "182894ffefcf77ca5be0907d37cf20142bedfa9f65979ca1b33668e2884732a0"; +constexpr char TEST_KEYUSAGE_CERT_SIGN_CERT_1_HASH[] = "448a4b76b671cc6ebd7db9740958d2e150fc7c5f"; +constexpr char TEST_KEYUSAGE_CERT_SIGN_CERT_SPKI[] = "U4oa9edhO4+vrb95H+HrAM4+laJ/eoQOAtToNDoiciE="; +constexpr char TEST_KEYUSAGE_CERT_SIGN_CERT_SERIAL[] = "19fd0013bd35d883d38819fa081f9cdd256f6dc8"; +constexpr char TEST_KEYUSAGE_CERT_SIGN_CERT_NOT_BEFORE[] = "Feb 3 11:16:38 2021 GMT"; +constexpr char TEST_KEYUSAGE_CERT_SIGN_CERT_NOT_AFTER[] = "Feb 3 11:16:38 2023 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_key.pem b/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_key.pem new file mode 100644 index 000000000000..d6df1edef0f2 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/keyusage_cert_sign_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAlePKCulIVDnvv7pvlaDtAfR+PJ504y6NcxgdnTyNnRCKHBnw +lq56WWYkilc60FKdE4DehXWY9akyIlrA8nna3ckZ1PSM9GdHJRCnJWUh1RirWS8x +dhT7zbFH54BnGjj+7KT6JzsMKrP3vYa1Y5Ff/N6q9KFY5hQUWJ8XgaWqxySi+yxF +Ghevjp3Gq0FTX3thoeMYOHt5AfwlFnXaKlc+xupWaAFl5VQAM8VSG8IG+YOsxZmI +8wNdk9BIJCUIubvNykYMrwveVkgnCYB1K3wYwNNjJt9Q5bFKtRflMKQ4KzugNlWU +/PowIbDlla/NwSO866iYkROIZeW9aty5L+1KhQIDAQABAoIBAFQ4YdYvrgxlYWkB +gKE6gvGOR0AYaOUdyyzYaAtpcsjF+lQ/3wdLkkOZOP7idJGJWekTh/TFVuTx5NGY +3MFh5rCnxnP51RmezkLtUH2ajaAG9IBwHAKVV8cDzbsuUsBRNiwRpt1UOEnmRVWg +01rW3HBhTP2XizP8JFKHUdXvGD48Y5PLoFxDuP4Nth8WlcoYXdT8v0RyN/XGBS7E +w1gX3f4EtWFvFZGBeUrcaRZkdj6GLNr046GAxeY+DIdRFIxevIep06X0xAOTiuwC +tKBFj9KJqAFhCbjen2pUvrOZN3tXKlnlCDpwT/OAk/zoYHwIgJRvnYCNY7TA3AnG +NBYNIgECgYEAxT4kJEwzO8LQERcsVI4j0AXJkjV9v+U3l3xy9mjZ46AiEh146iGy +R/VzVw5f7w9xSICwk76uN4N9p+p/u1RC0HmTP30oLr/JoNmCAh5V00P/GDPVIEUc +S6Kab9NDtUU/fInNOseuu8uo0utLjWBmcWB16zaQI4qA/zhSYsZ++FUCgYEAwop7 +gJNbJ944r21lcAwHl/fnVA9wURxWPwnlnvsV3kC8c9sMPpUkuzdwmPthcwxM7oW+ ++kbSdJmqpYbprtpo90Oj35rx6Ozsp3LqlxHclGfoS/wnT6XOuUSD9VWlntdsRu1U +VjUfx4LMdMoeMY2rk8Ek3vY3uguxJ1lKATv7+XECgYBjCQKInyISXYyvKB2ADyZ4 +Ko+9M9KB6YtyKnBmvNq6agrxYY72sBid/OX+zh7pH63Xo5YFePZstT8AcsPTwUkS ++BgxBpyIbI/Gja+zdJvPShLpigz2+PxuFaTJhSA4Ah8QXviHDP/1FxsbXD1BLSgC +wVYz1d+lmMOQYi0rn1LdSQKBgFp2YOWyIAJTAJL60N+giGtvWL+rCjR9c9GOfZtG +8K1P9xH8ux3i5pi0OAS7aF5CSwfjY6IoCrczubmNGd84KvVIG8zf1TvV6FoZQuMK +6EKOauPiljkgRhe6t43+zKwnSm9U7xHDVErHFOH+Fro+QZnMh6OyZMl7pF5C0/ns +9cfRAoGAWeCaaX/IbVtCrp6IEcyc3vVI+vLxWEh6wW/8dCrxO06S+gy0NlPa8+JZ +mTgz3U5il3f5X33oeQ2zVSDqzICrvpvBdf38pi66NEJnkgVeIijPkVKFnwHcW91t +AQNkEbeiDPqcZoKKm4n8SDfTbk+i9mC/CzUMufwhbPDB71DtvZA= +-----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert.cfg b/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert.cfg new file mode 100644 index 000000000000..3a3e3d04cbd8 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert.cfg @@ -0,0 +1,35 @@ +[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 Cert +commonName_default = Test Cert +commonName_max = 64 + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment, cRLSign +extendedKeyUsage = clientAuth, serverAuth +subjectKeyIdentifier = hash +1.2.3.4.5.6.7.8 = ASN1:UTF8String:Something +1.2.3.4.5.6.7.9 = DER:30:03:01:01:FF + +[v3_ca] +basicConstraints = critical, CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment, cRLSign +extendedKeyUsage = clientAuth, serverAuth +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always +1.2.3.4.5.6.7.8 = ASN1:UTF8String:Something +1.2.3.4.5.6.7.9 = DER:30:03:01:01:FF \ No newline at end of file diff --git a/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert.pem b/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert.pem new file mode 100644 index 000000000000..fadd8b850595 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJDCCAwygAwIBAgIUGf0AE7012IPTiBn6CB+c3SVvbckwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjEwMjAzMTExNjM4WhcNMjMw +MjAzMTExNjM4WjB4MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW +MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQ +THlmdCBFbmdpbmVlcmluZzESMBAGA1UEAwwJVGVzdCBDZXJ0MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6e0liMSEiPQFXUiM+Z6qzajpxGrFZP19v2Ds +1V2W5j/L5RIBfUWT4Cwsq44/r3GHjfCq++uJe828/F7Ukg9pMksRidEs0TUD/lpP +Heil6RGKRGZpzCiXYv0LZPKPDHJZyXLTXnlTAO3jE/jAujCwHNZroRs803g/5RaZ +5P3bhMdRgQDbB15v86aP5uaaXYUzcy1mGq3bSa0tAkhbYngsbSqhpLvtiAN11cgn +sh+TMs6TbcDGJXD5jXnNdHZfbrynrd/sLnvT0FoRdxEo4uO1+LG0khx4v4lAZ66n +GGMhYDBbBHA/9nSrwlZ6nmp3piQD6BKOn1xxqADdKl5Cs1OrrQIDAQABo4GnMIGk +MAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgHiMB0GA1UdJQQWMBQGCCsGAQUFBwMC +BggrBgEFBQcDATAdBgNVHQ4EFgQUnDiVycGw/XZydkQvsH23x3J8h1gwHwYDVR0j +BBgwFoAU08ELI+PDqPIyvl9UwT94OBNVA9kwFgYHKgMEBQYHCAQLDAlTb21ldGhp +bmcwEAYHKgMEBQYHCQQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABC0KwNn/rk0 +vY8YEbMkTErBHt1vM13Lg8bb0Qu/yA9Id4VqW7zzvaapUGNKvvx4t3H6ZBMMNNug +CPi2zW71HYKUEkhJZfpJtsrsRG9ezENjheW2uKDO977Cq78EBMrfWSQwfHP3iYyc +tBCHOb+v7oEZGohw3NgT4HoG3a66i9jpCenVUdWOgRvI6VbVc16C3FYrnDLdRx+h +9AyDOTe68s/tcBgn7DJyiUvXX+DFHcBfeddPfbVQ7luiT5ITPBfkzVkRePCyEySc +UONdq0bGTGEj/8YQLBvTMQ8JAxAHoRJJhKLHDJtsUR+RGTsp9IEqA7me/rvGKSla +jI9kdKxjF7I= +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert_info.h b/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert_info.h new file mode 100644 index 000000000000..67ba22d31c19 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_KEYUSAGE_CRL_SIGN_CERT_256_HASH[] = + "11a37b30d2fa15b30decc49bae67dfcf2b30d2fafa63ee52ebaad4503631c733"; +constexpr char TEST_KEYUSAGE_CRL_SIGN_CERT_1_HASH[] = "1e5a16317bc06aea66f0264ca4da234ba7db1758"; +constexpr char TEST_KEYUSAGE_CRL_SIGN_CERT_SPKI[] = "w5R7NEmc1JYbaSAYDJYRZZKzuI/mNbN0hoODeP2iM98="; +constexpr char TEST_KEYUSAGE_CRL_SIGN_CERT_SERIAL[] = "19fd0013bd35d883d38819fa081f9cdd256f6dc9"; +constexpr char TEST_KEYUSAGE_CRL_SIGN_CERT_NOT_BEFORE[] = "Feb 3 11:16:38 2021 GMT"; +constexpr char TEST_KEYUSAGE_CRL_SIGN_CERT_NOT_AFTER[] = "Feb 3 11:16:38 2023 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_key.pem b/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_key.pem new file mode 100644 index 000000000000..43380d2e195b --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/keyusage_crl_sign_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA6e0liMSEiPQFXUiM+Z6qzajpxGrFZP19v2Ds1V2W5j/L5RIB +fUWT4Cwsq44/r3GHjfCq++uJe828/F7Ukg9pMksRidEs0TUD/lpPHeil6RGKRGZp +zCiXYv0LZPKPDHJZyXLTXnlTAO3jE/jAujCwHNZroRs803g/5RaZ5P3bhMdRgQDb +B15v86aP5uaaXYUzcy1mGq3bSa0tAkhbYngsbSqhpLvtiAN11cgnsh+TMs6TbcDG +JXD5jXnNdHZfbrynrd/sLnvT0FoRdxEo4uO1+LG0khx4v4lAZ66nGGMhYDBbBHA/ +9nSrwlZ6nmp3piQD6BKOn1xxqADdKl5Cs1OrrQIDAQABAoIBAF214MlvYGC00MlT +3RXKmEYXGr7Svwz797oJDBdVjLPkbrvvgKU8kEbHq4V2UNDpvBICjZyp+MOd4c1/ +98wjXFMHe5koMLoGcPkeGH+0yXIa0rcgB9X/lNXU5RGlkeS8knd/BmncVIIUylkf +16U/B+4lf6xkivN0QrR1X2U6xQvlQPtiX5W4ykaogABHSxWzJP32XPmE4ojjfLd5 +DEqeyoxha3it4e+zmFpRIELStN9EboxqFPdqM7MDYjEJz8egBE5utC8Kw3jqEMat +Z3fXzuoIa8+OaEn8ILQus+BKADU46hmNrG/lOKlw+zXadDrg12TPX/tTXW8kUlYX +chKc4wkCgYEA/DGnSTBt8VL1A6ZfdQvptkDC0p6fwqSBQ5BX2dIRaVP1fDWk16+O +BfzXA609nvuSWf0DFlNcIstaJK/fxtlbCRSR3rfdB5iGbL8LmZZATkTZrD/z5K95 +xpq/1G+S7nCkX7Q8fOYvQVVYETav0BCEw0p8Nd/HAIUv9YKumdcdt5MCgYEA7XTq +p09l+TOrxNay1gPLAhdBsPP0FJJQJmr1yxvKJr2+mKZJWt3OikDzOMT8Ps90GUsO +rwujqL3hl8Ici7zB5olcsULpbRJtQxXry+YV34rlh34JEKLnpXtD453GxlZ8XgvZ +wuRqOsvBqEVUy8RMhe2ndiZ2SkiS6e7Ftuugl78CgYEAq0iSBJx232Nnc34o8RcR +Oa5MY75GZW1TOe8sK42IM9BJN347oh3iyOBLrHyaEINuh93WnfAp8JvKcoZc5vIy +6TzmQa0A2qrWCb/LghnRPRd3+4xH+rbPb3sk9IR+96DbkwCX4IB58dakBLTuvdKq +SPUq3XBJ+Wl8BDQon+XBki8CgYATSzWpxITHm9AwHTXIt+Qt1k/rHddOOJk0lepE +x4xEW5R5+MDrFiyrBR3+FdtdCyQmzfdyd6KjmlITL518KSkkHzMd4A7xYtbn5YcU +OSy7ziBaQv5fkKz7wClC/FXjVbGjPplCAac0AcxJbOC38co585Zwvi1MWds+EL2V +4E1bJwKBgQDO7fqUhSjDYNrOWoLQdShv9Ru/7hkhnWM4/Itdy+a70QXg0/uUqh3M +iPu0CcKYR1qU78dUnNJow4Ph9cITBcO1X0VAbysnKl+F3g6Yp9ilxNVtDUteP7mI +AN2Pu8bbYVMeYxdIH8FpVZf7qiESGIkAMnDmZmfVA7UWmRjAABRbzA== +-----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert.cfg b/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert.cfg new file mode 100644 index 000000000000..d9284904cd41 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_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 Server +commonName_default = Test Server +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] +URI.1 = test.com diff --git a/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert.pem b/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert.pem new file mode 100644 index 000000000000..4e93ff690c44 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEETCCAvmgAwIBAgIUasNGF+BDin79dyPNS4BoEOLI5VEwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjEwMjAzMTQ1MTIzWhcNMjMw +MjAzMTQ1MTIzWjB6MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW +MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQ +THlmdCBFbmdpbmVlcmluZzEUMBIGA1UEAwwLVGVzdCBTZXJ2ZXIwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5efz3MfhC5kYFKCmVDKDAQolqKIxsuNEj +P59VdfUgr+u0pArp8lLix2gfMerYN1YkQudW7UYjz3qtSN5miQPCinNhBiqwLear +ZwNJ4MuRIFlYyx96cN+vIsxdhf8EU0l9HV44u3afV1D7OIJWPO6M8W1idyr7lF2r +8IpGeuVjB099PcVbahXRw+9jhXUyYJSpiuOAPA8ONigpMO26itkjU6ByKXf51+Ln +E8hMKLjUL6Wgn12Sab1y/C6nfnVnJ2EdB1H/mVYuuAp9SRYIosOmLCa7logWOcUY +iT4DJBC5XnV5R1U8eozHKkn6CzEr2ZJfqukMj0zJs7YboAC0xw8HAgMBAAGjgZIw +gY8wDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUH +AwIGCCsGAQUFBwMBMBMGA1UdEQQMMAqGCHRlc3QuY29tMB0GA1UdDgQWBBS7M5f/ +9VlsFlRvdGM2KBw21tKOFjAfBgNVHSMEGDAWgBTTwQsj48Oo8jK+X1TBP3g4E1UD +2TANBgkqhkiG9w0BAQsFAAOCAQEAeGAL4on8MvnywhQa8lrjtZ+a6efWBnqHVSY6 +uHX9+WmCv7alwjksD9S5kwwOg1dGYG0lR6280gYnnjiCVRH+H8UukdJFWQ9Zb1Sg +cBITUVvOsWj9Ri7IV1Gi0I9QNcNUmpfy5ikTnbdIKbHrVJxWDa2l7f5whz0KXHgT +oNbdkxmlaJ3dFad4nZg+Fo/AbF7/BO97hfYLacH+gvEWhADMwuNuVfvQmt6+LP9I +YfyCBbdDoI7jOYniAr0eYoy0T+6GPeRGeCfPCT7jlRI+6nCBfQoF+MQtaK27l85C +VnLj4pVPdZFaiFfW8D7V3+QtX8ncAr/sbsvK2Ea6XE55pDbXuA== +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert_info.h b/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert_info.h new file mode 100644 index 000000000000..db4f8d2ac30e --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_NON_SPIFFE_SAN_CERT_256_HASH[] = + "f47a053ad90243ec948df601d156911b0aaf75c9474c989fddd4f6bca44cf692"; +constexpr char TEST_NON_SPIFFE_SAN_CERT_1_HASH[] = "846c80075e0bf5261709e34d9e429a55fb6fd2c3"; +constexpr char TEST_NON_SPIFFE_SAN_CERT_SPKI[] = "Xv64COw0ddwG6Ht4kW+hqYcW8m+xnd4GlPeN8TphvcU="; +constexpr char TEST_NON_SPIFFE_SAN_CERT_SERIAL[] = "6ac34617e0438a7efd7723cd4b806810e2c8e551"; +constexpr char TEST_NON_SPIFFE_SAN_CERT_NOT_BEFORE[] = "Feb 3 14:51:23 2021 GMT"; +constexpr char TEST_NON_SPIFFE_SAN_CERT_NOT_AFTER[] = "Feb 3 14:51:23 2023 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_key.pem b/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_key.pem new file mode 100644 index 000000000000..83e920bfc122 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/non_spiffe_san_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAuXn89zH4QuZGBSgplQygwEKJaiiMbLjRIz+fVXX1IK/rtKQK +6fJS4sdoHzHq2DdWJELnVu1GI896rUjeZokDwopzYQYqsC3mq2cDSeDLkSBZWMsf +enDfryLMXYX/BFNJfR1eOLt2n1dQ+ziCVjzujPFtYncq+5Rdq/CKRnrlYwdPfT3F +W2oV0cPvY4V1MmCUqYrjgDwPDjYoKTDtuorZI1Ogcil3+dfi5xPITCi41C+loJ9d +kmm9cvwup351ZydhHQdR/5lWLrgKfUkWCKLDpiwmu5aIFjnFGIk+AyQQuV51eUdV +PHqMxypJ+gsxK9mSX6rpDI9MybO2G6AAtMcPBwIDAQABAoIBAQCBL41ZY62mcxtM +Fjg4P45ruyxZC5sbUvMgGP1SmhE9TirfK+8KGaVPnVJRgAQxywEtyoe1TRiwcp/g +uENnqYE77BEHADOVeLMUqXBp8a/4Ck8RAJGRR7MVGii770u7aINkKKNq4m9x9nBK +OobVqCUDeFkW3yfKCQHhc23sP0csXEgIj3LCRTDPOhX7AF+a4ECfz8eDcjLP8TKM +wqfTXu9TwiZMibGmJgrM2CIeW9vR0xKrEuhFGoL7I1wX0F+Be7kkbqY0NLJxREwY +QjeABNb3vfF92EyJ7QrYrxLDYdIVnrRyMqcoT3KGR85Bc/aQwkDej107apHyhofm +PIFDcIkxAoGBAOfZtCIxzi4Zw7Xu3XiUKQEifYnm4GV8vlhFOuzF7CEweOOXJ9Ko +7yxVn+aNdv0UqYOmsMd0GTXyUhvJSPLxEUCJC3N7eOuzWNLhiNtOMfvCY2iLhI4a +mHI2UavmWgNx7ZA7Q/vK5MDkEHlCAw8AMZ47hOLNPqqL+IjkODjdvm+ZAoGBAMzL +uUQ3Y4ZhKcqqBEommATE4YIpjEMWepua3g8N9wzdbaD5FtJM/ZfNSwl/wjFPZhK7 +c+pFLOZLl245uj52RS1QQ+6EbKai4jC8NEEb8Zo9rerWFkJRIs705Jxmq0yYAqdV +UJhycJeaVmX1bJJU4iQzCvF8nrLYvZpFLr9a4RefAoGAf1UgSjtiSg1aYBv8xFFS +p83ido83JGW7QE1dTFZzFdNCQXRtqZOgL5AjDoMZG2tyodw1cIVBp1AbailFCC// +Upsxj837Hi/Uk5TMDe3HI8ahw/QD6+uNWASfHDKZsxSp7TGvZ6UJtypKJd5sQZvQ +pF953vnr9cyDxeLZQdn+0dkCgYEAvhyogaEBbO+pwg8OKF+nY1X5GcHECUtGykh7 +t3H5UyIC8RoKi3MZPuA+tjS5atkQIneNZX6N7cNicdp5AB7+nNAUH8kiq5Ytb5xm +zcJJCCwV1RikVS/IpmJEDsRoZJQAcqIKTVp/Ft0ZM1EfVsAhpgUUNZTAJbp6WEm8 +2bpdlnUCgYEApAqC2nnimtbbN4XSyeLqiU9V2BCJoIbbtpM8Vy0gJUGFDUHgJKFz +ag01gHoh9wTSbsEyGi4qpeDLA5m9oRVlpcX6qOt7XCaGyl4mJbTzls8BFc+yh4Ty +2dAoFYZxlVpslZ/khFeVlmYGjU1Nr1gMSluWVrjou02jte2CX/x8ez4= +-----END RSA PRIVATE KEY----- diff --git a/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.cfg b/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.cfg new file mode 100644 index 000000000000..f4578f4f15a9 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/spiffe_san_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 = Test Server +commonName_default = Test Server +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 = envoy.com +URI.1 = spiffe://example.com/workload diff --git a/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem b/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem new file mode 100644 index 000000000000..cdd919911be0 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIULfVpq633gV4mwbFj7nwmn5GYaFgwDQYJKoZIhvcNAQEL +BQAwdjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBEx5ZnQxGTAXBgNVBAsMEEx5ZnQgRW5n +aW5lZXJpbmcxEDAOBgNVBAMMB1Rlc3QgQ0EwHhcNMjEwMzAxMDA1NTU5WhcNMjMw +MzAxMDA1NTU5WjB6MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW +MBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwETHlmdDEZMBcGA1UECwwQ +THlmdCBFbmdpbmVlcmluZzEUMBIGA1UEAwwLVGVzdCBTZXJ2ZXIwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3YjGzHUr6FNKh1PfMPFE7NmAYSeLStc5+ +44lTku418R5ZbJ9+1OhXPbHjrqXM90jLZPLHOOginVVvNtZRk+LJrhUidzNmS69M +N2N9xosif0/zafI1/sToBwC8g/lP/TAmBwIzsbX3D526R1iVvIkyhxAK3AUTXG2x +Ilg2+sn4iIGmmPZNDtmoqLabDRWBToH0ijx+wxpF1IB5Tj2Ymeb7svG17e+MsG7G +bXo6+qT00n5hjpY1Cfj0I2mpUIw4aEqdk7+5MPFnnOmv2LuH73p/7goIs8Mw2ucX +xpF2f8lqFLt5+ZZT9BEdUT/sR23f7vQCaAjz7JATxrr9X2wqwHArAgMBAAGjgbIw +ga8wDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBeAwHQYDVR0lBBYwFAYIKwYBBQUH +AwIGCCsGAQUFBwMBMDMGA1UdEQQsMCqGHXNwaWZmZTovL2V4YW1wbGUuY29tL3dv +cmtsb2FkggllbnZveS5jb20wHQYDVR0OBBYEFEaJC8Z4FIvUx44gnSSHh4uaeWTf +MB8GA1UdIwQYMBaAFNPBCyPjw6jyMr5fVME/eDgTVQPZMA0GCSqGSIb3DQEBCwUA +A4IBAQCEDoSAAvBuwAnrWuXuPgt++fqG6OQVnxRcJXL2GS14KUt2g2IniSTPm8Hi +mZj3hXS/RSpuyz3ObvtfKZ0gsf1QCBAigkQmSPFHTe/rhyWN+Vx+WkfEkrTqTmlN +UMAUlcO0SwbM9UABYPpMQI8Dn/XSiWgZaKHwGbzs2mQ00DosjcIGnav6+aPMCWfj +BTzw+GFzKcDqyhMRjEKBydTrzoTB2SK/hpE2ZDFn4xysfR9lf26MYsKHSRfLgK6E +ou/Tpl9twMoYSu40vdIO16gOK/KviUnCZ2nfcmhzDIcFQSNQQMNBPKPkpjji4xkl +udMoBC5KNZsvpC4gtGbY2X+DLI1P +-----END CERTIFICATE----- diff --git a/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert_info.h b/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert_info.h new file mode 100644 index 000000000000..e368ebf2848e --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/spiffe_san_cert_info.h @@ -0,0 +1,8 @@ +// NOLINT(namespace-envoy) +constexpr char TEST_SPIFFE_SAN_CERT_256_HASH[] = + "52c784aa74dc6aadd581d6a9aad0a379a8cf8fb0b175e30874e19acf924dfe10"; +constexpr char TEST_SPIFFE_SAN_CERT_1_HASH[] = "3c2978c764c322a776bfb8aa04bb9a69e0f8bff2"; +constexpr char TEST_SPIFFE_SAN_CERT_SPKI[] = "7drIzHFClgoBw1tG6lbnubSOw9d9cKhKyUTWT77hpX0="; +constexpr char TEST_SPIFFE_SAN_CERT_SERIAL[] = "2df569abadf7815e26c1b163ee7c269f91986858"; +constexpr char TEST_SPIFFE_SAN_CERT_NOT_BEFORE[] = "Mar 1 00:55:59 2021 GMT"; +constexpr char TEST_SPIFFE_SAN_CERT_NOT_AFTER[] = "Mar 1 00:55:59 2023 GMT"; diff --git a/test/extensions/transport_sockets/tls/test_data/spiffe_san_key.pem b/test/extensions/transport_sockets/tls/test_data/spiffe_san_key.pem new file mode 100644 index 000000000000..0a89f805d095 --- /dev/null +++ b/test/extensions/transport_sockets/tls/test_data/spiffe_san_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAt2Ixsx1K+hTSodT3zDxROzZgGEni0rXOfuOJU5LuNfEeWWyf +ftToVz2x466lzPdIy2TyxzjoIp1VbzbWUZPiya4VInczZkuvTDdjfcaLIn9P82ny +Nf7E6AcAvIP5T/0wJgcCM7G19w+dukdYlbyJMocQCtwFE1xtsSJYNvrJ+IiBppj2 +TQ7ZqKi2mw0VgU6B9Io8fsMaRdSAeU49mJnm+7Lxte3vjLBuxm16Ovqk9NJ+YY6W +NQn49CNpqVCMOGhKnZO/uTDxZ5zpr9i7h+96f+4KCLPDMNrnF8aRdn/JahS7efmW +U/QRHVE/7Edt3+70AmgI8+yQE8a6/V9sKsBwKwIDAQABAoIBAF5MXAoisf9O3dDh +1lprWcn8+AUFWWHIo1qUXnVfRKbwSg7p0EpD6QWTb/oIQLHZJtGQI1dWZ+gEx33c +0PA5/5B9t9h1OzULDiU/BiYTBlDC7rXYcPha/Z3im/pUUstTAoNLb1Jtu4hDu3Oi +ZGb7AAG/efxbjzCZgr5nTr1W0Ky/hWLpP1V1dLvWItlr/ieOPiNDg7dQqytIbSAV +cT1XpimTGSrS1UPEYVnHM9qNBMRJ9CmMigVdipaAPYV5u4SGuHU3oyClE5j4jWeE +vwugRkTap9L8xbG+JDYE5sjtPXxRtPb4DOEY6KElbPAt9S/zGkEu0N/9Qe1BYeeV +Q7uuduECgYEA7Cax4Ptc4C7lYTvGh+5gyylKQdUdfa7d4ixU48aG/X/3EKicSKvW ++ap6zd3olGy9tDPV29VF4f+9UrYuy04UxEpLeo8XDy5iauDAlU8lAPIrr0tkVm6w +ODJEiLgkljLFaxIvqiXE+661o3cBn3KcRcADAuQAzvvB8dGheyhzCY8CgYEAxswX +E2Je+W94iKLkiCNo6ljdiXwQu6TxocqJ8lYAiEzmXjJEnZ8NQbXgw4ESMPk/W1XL +ikJLjLrEEpdu7QSvIVqaw89l6DGpS034Ct8wQLGEJk/D253yumlX4gxyjGLrZV3Q +hHcQ1jbN0q86CWQstk81k20BtWgXJSvuOsdGyaUCgYEAgfuWg1i4OWl2tnt5fo6W +Vp0mk2/jqK9c0EZIf4th+By8eD3msBVt4cSVjcUsZK4qCQtTFoqgyZHDusgun5cd +1SFzxEUIk0GbyGpndoe2vXuO0hD0bKLGelgo4vxAny/Y/GNpOwVJFKOItS4nBYXH +QJk8zxWC4GswyJLziF+uWj8CgYEArePr82rCxNE6z9ocqPDAXuzoq9A4GssHCYzO +6YlM4ezSPWcfGfj8cZQUTS7jqK79Onlrlz6yMyFTTSflQbItNrG4WrtZ2qdF/Lbw +1yGvZYdhntl66unYXjKzSum0cRQ97+cF9DjqI1bA5x+bVoenjLjwlkptii7IwB0T +P5r8UnkCgYBPniQR7jINhnkGdR01GqsstvSgwoGG4+IeO9EuhBePoktI7W48HEs2 +Ng23QoXJRwWo+KM2GqpOD9y3lxaJnawki2PsiUcRE84wHmclOxOadEn6tun7e0Qw +rHu+7hAxcFl9xvK5zE7h1BjZCNR5hNpyMXyAmNpl8mX/mgZPc9HhHg== +-----END RSA PRIVATE KEY----- diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 3cc0ac4e7d55..46ad79bd895a 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -3,6 +3,7 @@ #include #include +#include "envoy/api/api.h" #include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h" #include "envoy/ssl/certificate_validation_context_config.h" #include "envoy/ssl/connection.h" @@ -153,6 +154,9 @@ class MockCertificateValidationContextConfig : public CertificateValidationConte MOCK_METHOD(const std::vector&, verifyCertificateHashList, (), (const)); MOCK_METHOD(const std::vector&, verifyCertificateSpkiList, (), (const)); MOCK_METHOD(bool, allowExpiredCertificate, (), (const)); + MOCK_METHOD(const absl::optional&, + customValidatorConfig, (), (const)); + MOCK_METHOD(Api::Api&, api, (), (const)); MOCK_METHOD(envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext:: TrustChainVerification, trustChainVerification, (), (const)); diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 4d7dfa505214..dc814770771b 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -46,6 +46,7 @@ CHMOD CHLOS CHLOs CIDR +CITT CLA CLI CMSG @@ -314,6 +315,7 @@ STL STRLEN STS SVG +SVID Symbolizer TBD TCLAP @@ -487,7 +489,7 @@ ci ciphersuite ciphersuites circllhist -CITT +clientcert cloneable cloneability cmd @@ -520,6 +522,7 @@ coverity cplusplus cpuset creds +cRLSign crypto cryptographic cryptographically