Skip to content
This repository has been archived by the owner on Nov 20, 2024. It is now read-only.

TLS splicing/bumping #57

Merged
merged 35 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
225f317
TLS bumping integration
Feb 15, 2022
6e62821
Add docs for building TLS bumping
Sep 26, 2022
ed842b0
Add docs for testing TLS bumping without CONNECT
Sep 26, 2022
c7503ab
Add config for bumping with http connect case (#308)
Sep 26, 2022
df331ec
Add config for TLS splicing without HTTP CONNECT case
Sep 26, 2022
cb9d087
Add config for TLS splicing with HTTP CONNECT case
Sep 26, 2022
ab29125
Add config for TLS splicing/bumping w/wo HTTP CONNECT
Sep 27, 2022
81f77b0
Improve doc with testing details (#309)
Sep 27, 2022
638f3b5
Expiration time check (#320)
liverbirdkte Oct 12, 2022
4709f58
Update splicing/bumping config to cover more test cases
Oct 12, 2022
3fa1750
Fix issues of subject and expiration time (#322)
liverbirdkte Oct 13, 2022
de16d66
Update config to cover http2 test case
Oct 13, 2022
a588d06
skip duplicate certs check
Oct 18, 2022
553755f
Fix doc (#325)
liverbirdkte Oct 18, 2022
00a857a
Fix lacking sni of tls handshake in bumping filter (#329)
liverbirdkte Oct 27, 2022
b3f0189
Refactor local certificate provider config (#337)
liverbirdkte Nov 22, 2022
108d402
Add support for different pkey types and sizes (#342)
Nov 22, 2022
b3c56d3
tls: SNI-based cert selection during TLS handshake
Dec 1, 2022
c17e498
Cache size (#347)
liverbirdkte Dec 1, 2022
6ce4aa3
remove useless files
Dec 1, 2022
b6627ab
remove useless docs/notes due to rebase
Dec 1, 2022
ea3ed5e
add mocks to pass test compiling
Dec 1, 2022
b7ae2b7
code cleanup
Dec 2, 2022
b76971b
replace raw pointer with smart pointer
Dec 2, 2022
c549f0c
Code cleanup (#349)
liverbirdkte Dec 2, 2022
1bc698c
tls: allow multiple certs with the same SAN
ggreenway Dec 7, 2022
b65d4f4
bug fix
Dec 12, 2022
bfc4d84
avoid establishing additional connection with upstream
Dec 14, 2022
9c130c1
bug fix: add nullptr check for cert
Dec 13, 2022
92e25a2
fix format
Dec 20, 2022
dccf77d
fix test case
Dec 20, 2022
3e47389
Fix active tcp listener test (#351)
liverbirdkte Dec 20, 2022
e598dbf
fix code format and fix spelling format
Dec 20, 2022
8b3a178
fix clang_tidy warning
Dec 20, 2022
b0c8113
add matt to CODEOWNER to pass format check
Dec 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ proto_library(
"//envoy/extensions/access_loggers/wasm/v3:pkg",
"//envoy/extensions/bootstrap/internal_listener/v3:pkg",
"//envoy/extensions/cache/simple_http_cache/v3:pkg",
"//envoy/extensions/certificate_providers/local_certificate/v3:pkg",
"//envoy/extensions/clusters/aggregate/v3:pkg",
"//envoy/extensions/clusters/dynamic_forward_proxy/v3:pkg",
"//envoy/extensions/clusters/redis/v3:pkg",
Expand Down
3 changes: 1 addition & 2 deletions api/envoy/config/bootstrap/v3/bootstrap.proto
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,8 @@ message Bootstrap {

// Global map of CertificateProvider instances. These instances are referred to by name in the
// :ref:`CommonTlsContext.CertificateProviderInstance.instance_name
// <envoy_v3_api_field_extensions.transport_sockets.tls.v3.CommonTlsContext.CertificateProviderInstance.instance_name>`
// <envoy_v3_api_field_extensions.transport_sockets.tls.v3.CertificateProviderPluginInstance.instance_name>`
// field.
// [#not-implemented-hide:]
map<string, core.v3.TypedExtensionConfig> certificate_provider_instances = 25;

// Specifies a set of headers that need to be registered as inline header. This configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/core/v3:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
syntax = "proto3";

package envoy.extensions.certificate_providers.local_certificate.v3;

import "envoy/config/core/v3/base.proto";

import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";

import "udpa/annotations/status.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.certificate_providers.local_certificate.v3";
option java_outer_classname = "LocalCertificateProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/certificate_providers/local_certificate/v3;local_certificatev3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Local Certificate Provider]

// [#extension: envoy.certificate_providers.local_certificate]
// [#next-free-field: 8]
message LocalCertificate {
enum Pkey {
UNSPECIFIED = 0;
RSA_2048 = 1;
RSA_3072 = 2;
RSA_4096 = 3;
ECDSA_P256 = 4;
}

// Key and cert of root ca used to sign certificates.
config.core.v3.DataSource rootca_cert = 1;

config.core.v3.DataSource rootca_key = 2;

// Default Identity key and cert used for TLS handshake
config.core.v3.DataSource default_identity_cert = 3;

config.core.v3.DataSource default_identity_key = 4;

// Indicates the time at which the certificate expires.
google.protobuf.Timestamp expiration_time = 5;

// The pkey type and size. If not specified, the type/size of original server cert will be copied.
// It supports RSA_2048, RSA_3072, RSA_4096, ECDSA_P256. Otherwise, defaults to RSA_2048.
Pkey pkey = 6;

// The maximum number of hosts that the cache will hold. If not specified defaults to 1024.
google.protobuf.UInt32Value cache_size = 7 [(validate.rules).uint32 = {gt: 0}];
}
13 changes: 13 additions & 0 deletions api/envoy/extensions/filters/network/bumping/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/accesslog/v3:pkg",
"//envoy/extensions/transport_sockets/tls/v3:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
],
)
42 changes: 42 additions & 0 deletions api/envoy/extensions/filters/network/bumping/v3/bumping.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
syntax = "proto3";

package envoy.extensions.filters.network.bumping.v3;

import "envoy/config/accesslog/v3/accesslog.proto";
import "envoy/extensions/transport_sockets/tls/v3/common.proto";

import "google/protobuf/wrappers.proto";

import "udpa/annotations/status.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.filters.network.bumping.v3";
option java_outer_classname = "BumpingProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/bumping/v3;bumpingv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Bumping]
// Bumping :ref:`configuration overview <config_network_filters_bumping>`.
// [#extension: envoy.filters.network.bumping]

// [#next-free-field: 6]
message Bumping {
// The prefix to use when emitting :ref:`statistics
// <config_network_filters_bumping_stats>`.
string stat_prefix = 1 [(validate.rules).string = {min_len: 1}];

// The upstream cluster to connect to.
string cluster = 2;

// Configuration for :ref:`access logs <arch_overview_access_logs>`
// emitted by the this bumping filter.
repeated config.accesslog.v3.AccessLog access_log = 3;

// The maximum number of unsuccessful connection attempts that will be made before
// giving up. If the parameter is not specified, 1 connection attempt will be made.
google.protobuf.UInt32Value max_connect_attempts = 4 [(validate.rules).uint32 = {gte: 1}];

// Certificate provider instance for fetching TLS certificates.
transport_sockets.tls.v3.CertificateProviderPluginInstance tls_certificate_provider_instance = 5;
}
4 changes: 1 addition & 3 deletions api/envoy/extensions/transport_sockets/tls/v3/common.proto
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ message TlsSessionTicketKeys {
// The plugin instances are defined in the client's bootstrap file.
// The plugin allows certificates to be fetched/refreshed over the network asynchronously with
// respect to the TLS handshake.
// [#not-implemented-hide:]
message CertificateProviderPluginInstance {
// Provider instance name. If not present, defaults to "default".
//
Expand Down Expand Up @@ -341,8 +340,7 @@ message CertificateValidationContext {

// Certificate provider instance for fetching TLS certificates.
//
// Only one of ``trusted_ca`` and ``ca_certificate_provider_instance`` may be specified.
// [#not-implemented-hide:]
// Only one of *trusted_ca* and *ca_certificate_provider_instance* may be specified.
CertificateProviderPluginInstance ca_certificate_provider_instance = 13
[(udpa.annotations.field_migrate).oneof_promotion = "ca_cert_source"];

Expand Down
19 changes: 10 additions & 9 deletions api/envoy/extensions/transport_sockets/tls/v3/tls.proto
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ message UpstreamTlsContext {
google.protobuf.UInt32Value max_session_keys = 4;
}

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

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

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

// Only a single TLS certificate is supported in client contexts. In server contexts,
// :ref:`Multiple TLS certificates <arch_overview_ssl_cert_select>` can be associated with the
// same context to allow both RSA and ECDSA certificates.
//
// Only a single TLS certificate is supported in client contexts. In server contexts, the first
// RSA certificate is used for clients that only support RSA and the first ECDSA certificate is
// used for clients that support ECDSA.
// same context to allow both RSA and ECDSA certificates and support SNI-based selection.
//
// Only one of ``tls_certificates``, ``tls_certificate_sds_secret_configs``,
// and ``tls_certificate_provider_instance`` may be used.
Expand All @@ -257,9 +259,8 @@ message CommonTlsContext {

// Certificate provider instance for fetching TLS certs.
//
// Only one of ``tls_certificates``, ``tls_certificate_sds_secret_configs``,
// and ``tls_certificate_provider_instance`` may be used.
// [#not-implemented-hide:]
// Only one of *tls_certificates*, *tls_certificate_sds_secret_configs*,
// and *tls_certificate_provider_instance* may be used.
CertificateProviderPluginInstance tls_certificate_provider_instance = 14;

// Certificate provider for fetching TLS certificates.
Expand Down
2 changes: 2 additions & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ proto_library(
"//envoy/extensions/access_loggers/wasm/v3:pkg",
"//envoy/extensions/bootstrap/internal_listener/v3:pkg",
"//envoy/extensions/cache/simple_http_cache/v3:pkg",
"//envoy/extensions/certificate_providers/local_certificate/v3:pkg",
"//envoy/extensions/clusters/aggregate/v3:pkg",
"//envoy/extensions/clusters/dynamic_forward_proxy/v3:pkg",
"//envoy/extensions/clusters/redis/v3:pkg",
Expand Down Expand Up @@ -139,6 +140,7 @@ proto_library(
"//envoy/extensions/filters/listener/original_src/v3:pkg",
"//envoy/extensions/filters/listener/proxy_protocol/v3:pkg",
"//envoy/extensions/filters/listener/tls_inspector/v3:pkg",
"//envoy/extensions/filters/network/bumping/v3:pkg",
"//envoy/extensions/filters/network/connection_limit/v3:pkg",
"//envoy/extensions/filters/network/direct_response/v3:pkg",
"//envoy/extensions/filters/network/dubbo_proxy/router/v3:pkg",
Expand Down
6 changes: 6 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,11 @@ removed_config_or_runtime:
# *Normally occurs at the end of the* :ref:`deprecation period <deprecated>`

new_features:
- area: tls
change: |
added support for SNI-based cert selection in tls downstream transport socket. Detailed documentation is available :ref:`cert selection<arch_overview_ssl_cert_select>`.
New config option :ref:`full_scan_certs_on_sni_mismatch <envoy_v3_api_field_extensions.transport_sockets.tls.v3.DownstreamTlsContext.full_scan_certs_on_sni_mismatch>`
is introduced to disable or enable full scan when no cert matches to SNI, defaults to false.
New runtime flag ``envoy.reloadable_features.no_full_scan_certs_on_sni_mismatch`` can be used for override the default value.

deprecated:
51 changes: 40 additions & 11 deletions docs/root/intro/arch_overview/security/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,50 @@ Certificate selection
---------------------

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

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

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

Certificate selection rules:

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

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


Only a single TLS certificate is supported today for :ref:`UpstreamTlsContexts
<envoy_v3_api_msg_extensions.transport_sockets.tls.v3.UpstreamTlsContext>`.
Expand Down
95 changes: 95 additions & 0 deletions envoy-bumping-integ.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 1234
listener_filters:
- name: envoy.filters.listener.tls_inspector
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
filter_chains:
- filter_chain_match:
server_names: ["www.baidu.com", "www.google.com", "www.linkedin.com"] # bumping w/ connect or w/o connect
filters:
- name: envoy.filters.network.sni_dynamic_forward_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.sni_dynamic_forward_proxy.v3.FilterConfig
port_value: 443
dns_cache_config:
name: dynamic_forward_proxy_cache_config
dns_lookup_family: V4_ONLY
- name: envoy.filters.network.bumping
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.bumping.v3.Bumping
stat_prefix: destination
cluster: dynamic_forward_proxy_cluster_bumping
tls_certificate_provider_instance:
instance_name: "local_cert_provider"
certificate_name: "ALL_IDENTITY_CERTS"
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: dynamic_forward_proxy_cluster_bumping
http_filters:
- name: envoy.filters.http.dynamic_forward_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.dynamic_forward_proxy.v3.FilterConfig
dns_cache_config:
name: dynamic_forward_proxy_cache_config
dns_lookup_family: V4_ONLY
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificate_provider_instance:
instance_name: "local_cert_provider"
certificate_name: "ALL_IDENTITY_CERTS"
clusters:
- name: dynamic_forward_proxy_cluster_bumping
connect_timeout: 1s
lb_policy: CLUSTER_PROVIDED
cluster_type:
name: envoy.clusters.dynamic_forward_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.clusters.dynamic_forward_proxy.v3.ClusterConfig
dns_cache_config:
name: dynamic_forward_proxy_cache_config
dns_lookup_family: V4_ONLY
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
common_tls_context:
validation_context:
trusted_ca:
filename: /etc/ssl/certs/ca-certificates.crt
certificate_provider_instances:
local_cert_provider:
name: envoy.certificate_providers.local_certificate
typed_config:
"@type": type.googleapis.com/envoy.extensions.certificate_providers.local_certificate.v3.LocalCertificate
rootca_cert:
filename: root-ca.pem
rootca_key:
filename: root-ca.key
default_identity_cert:
filename: baidu.com.pem
default_identity_key:
filename: baidu.com.key
Loading