From 2d1206ffc6281ef4519766ab27f9f0a07b89c828 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 20 Apr 2020 12:01:11 -0400 Subject: [PATCH 01/46] Move internal redirect configs into InternalRedirectPolicy and implement pluggable predicates. Signed-off-by: pengg --- CODEOWNERS | 1 + api/BUILD | 2 + .../config/route/v3/route_components.proto | 74 +++- .../route/v4alpha/route_components.proto | 90 ++++- .../previous_routes/v3/BUILD | 9 + .../v3/previous_routes_config.proto | 19 + .../whitelisted_routes/v3/BUILD | 9 + .../v3/whitelisted_routes_config.proto | 19 + api/versioning/BUILD | 2 + .../http/http_connection_management.rst | 44 ++- docs/root/version_history/current.rst | 5 + .../previous_routes/v3/BUILD | 9 + .../v3/previous_routes_config.proto | 19 + .../whitelisted_routes/v3/BUILD | 9 + .../v3/whitelisted_routes_config.proto | 19 + include/envoy/router/BUILD | 10 + include/envoy/router/internal_redirect.h | 49 +++ include/envoy/router/router.h | 56 ++- source/common/http/async_client_impl.cc | 1 + source/common/http/async_client_impl.h | 8 +- source/common/router/config_impl.cc | 119 +++++- source/common/router/config_impl.h | 68 +++- source/common/router/router.cc | 61 +-- source/extensions/extensions_build_config.bzl | 347 +++++++++++++----- source/extensions/internal_redirect/BUILD | 17 + .../internal_redirect/previous_routes/BUILD | 34 ++ .../previous_routes/config.cc | 14 + .../previous_routes/config.h | 34 ++ .../previous_routes/previous_routes.cc | 52 +++ .../previous_routes/previous_routes.h | 26 ++ .../internal_redirect/well_known_names.h | 25 ++ .../whitelisted_routes/BUILD | 34 ++ .../whitelisted_routes/config.cc | 14 + .../whitelisted_routes/config.h | 41 +++ .../whitelisted_routes/whitelisted_routes.h | 33 ++ test/common/http/async_client_impl_test.cc | 3 +- test/common/router/config_impl_test.cc | 40 +- test/common/router/router_test.cc | 78 +++- .../internal_redirect/previous_routes/BUILD | 24 ++ .../previous_routes/config_test.cc | 83 +++++ test/integration/BUILD | 3 + test/integration/redirect_integration_test.cc | 111 ++++++ test/mocks/router/mocks.cc | 5 + test/mocks/router/mocks.h | 20 +- 44 files changed, 1529 insertions(+), 211 deletions(-) create mode 100644 api/envoy/extensions/internal_redirect/previous_routes/v3/BUILD create mode 100644 api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto create mode 100644 api/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD create mode 100644 api/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto create mode 100644 generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/BUILD create mode 100644 generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto create mode 100644 generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD create mode 100644 generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto create mode 100644 include/envoy/router/internal_redirect.h create mode 100644 source/extensions/internal_redirect/BUILD create mode 100644 source/extensions/internal_redirect/previous_routes/BUILD create mode 100644 source/extensions/internal_redirect/previous_routes/config.cc create mode 100644 source/extensions/internal_redirect/previous_routes/config.h create mode 100644 source/extensions/internal_redirect/previous_routes/previous_routes.cc create mode 100644 source/extensions/internal_redirect/previous_routes/previous_routes.h create mode 100644 source/extensions/internal_redirect/well_known_names.h create mode 100644 source/extensions/internal_redirect/whitelisted_routes/BUILD create mode 100644 source/extensions/internal_redirect/whitelisted_routes/config.cc create mode 100644 source/extensions/internal_redirect/whitelisted_routes/config.h create mode 100644 source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h create mode 100644 test/extensions/internal_redirect/previous_routes/BUILD create mode 100644 test/extensions/internal_redirect/previous_routes/config_test.cc diff --git a/CODEOWNERS b/CODEOWNERS index 37e376e77e79..f371dde58f93 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -86,6 +86,7 @@ extensions/filters/common/original_src @snowp @klarose /*/extensions/filters/listener/tls_inspector @piotrsikora @htuch /*/extensions/grpc_credentials/example @wozz @htuch /*/extensions/grpc_credentials/file_based_metadata @wozz @htuch +/*/extensions/internal_redirect @alyssawilk @penguingao /*/extensions/stat_sinks/dog_statsd @taiki45 @jmarantz /*/extensions/stat_sinks/hystrix @trabetti @jmarantz /*/extensions/stat_sinks/metrics_service @ramaraochavali @jmarantz diff --git a/api/BUILD b/api/BUILD index 97a8554bc520..393598fde8c8 100644 --- a/api/BUILD +++ b/api/BUILD @@ -220,6 +220,8 @@ proto_library( "//envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/v3:pkg", "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", + "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", + "//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", "//envoy/extensions/transport_sockets/alts/v3:pkg", diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 70c52010efa0..21066bb5e5df 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -520,7 +520,7 @@ message CorsPolicy { core.v3.RuntimeFractionalPercent shadow_enabled = 10; } -// [#next-free-field: 34] +// [#next-free-field: 35] message RouteAction { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RouteAction"; @@ -533,6 +533,7 @@ message RouteAction { } // Configures :ref:`internal redirect ` behavior. + // [#next-major-version: remove this definition - it's defined in the InternalRedirectPolicy message.] enum InternalRedirectAction { PASS_THROUGH_INTERNAL_REDIRECT = 0; HANDLE_INTERNAL_REDIRECT = 1; @@ -957,7 +958,9 @@ message RouteAction { repeated UpgradeConfig upgrade_configs = 25; - InternalRedirectAction internal_redirect_action = 26; + InternalRedirectPolicy internal_redirect_policy = 34; + + InternalRedirectAction internal_redirect_action = 26 [deprecated = true]; // An internal redirect is handled, iff the number of previous internal redirects that a // downstream request has encountered is lower than this value, and @@ -973,7 +976,7 @@ message RouteAction { // will pass the redirect back to downstream. // // If not specified, at most one redirect will be followed. - google.protobuf.UInt32Value max_internal_redirects = 31; + google.protobuf.UInt32Value max_internal_redirects = 31 [deprecated = true]; // Indicates that the route has a hedge policy. Note that if this is set, // it'll take precedence over the virtual host level hedge policy entirely @@ -1564,3 +1567,68 @@ message QueryParameterMatcher { bool present_match = 6; } } + +// HTTP Internal Redirect :ref:`architecture overview `. +// [#next-free-field: 6] +message InternalRedirectPolicy { + // Configures :ref:`internal redirect ` behavior. + enum InternalRedirectAction { + PASS_THROUGH_INTERNAL_REDIRECT = 0; + HANDLE_INTERNAL_REDIRECT = 1; + } + + // HTTP scheme conbinations that are allowed to be handled as internal + // redirect. Downstream scheme comes first and redirect target URL scheme + // follows. + enum DownstreamAndRedirectTargetSchemePair { + HTTP_HTTP = 0; + HTTP_HTTPS = 1; + HTTPS_HTTP = 2; + HTTPS_HTTPS = 3; + } + + message Predicate { + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + google.protobuf.Any typed_config = 2; + } + + InternalRedirectAction internal_redirect_action = 1; + + // An internal redirect is handled, iff the number of previous internal redirects that a + // downstream request has encountered is lower than this value, and + // :ref:`internal_redirect_action + // ` + // is set to + // :ref:`HANDLE_INTERNAL_REDIRECT + // ` + // In the case where a downstream request is bounced among multiple routes by internal redirect, + // the first route that hits this threshold, or has + // :ref:`internal_redirect_action + // ` + // set to + // :ref:`PASS_THROUGH_INTERNAL_REDIRECT + // ` + // will pass the redirect back to downstream. + // + // If not specified, at most one redirect will be followed. + google.protobuf.UInt32Value max_internal_redirects = 2; + + // Defines what upstream response codes can trigger the internal redirect + // behavior. This is ignored unless + // :ref:`internal_redirect_action + // ` + // is set to + // :ref:`HANDLE_INTERNAL_REDIRECT + // ` + repeated uint32 redirect_response_codes = 3; + + // Specifies a list of predicates that is queries when an upstream response is deemed to be able + // to trigger an internal redirect by all other criterias. Any predicate in the list can reject + // the redirect. + repeated Predicate predicates = 4; + + // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an + // internal redirect. + repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; +} diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index e813b632edb0..1f9aa9da4eed 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -521,7 +521,7 @@ message CorsPolicy { core.v4alpha.RuntimeFractionalPercent shadow_enabled = 10; } -// [#next-free-field: 34] +// [#next-free-field: 35] message RouteAction { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.RouteAction"; @@ -534,6 +534,7 @@ message RouteAction { } // Configures :ref:`internal redirect ` behavior. + // [#next-major-version: remove this definition - it's defined in the InternalRedirectPolicy message.] enum InternalRedirectAction { PASS_THROUGH_INTERNAL_REDIRECT = 0; HANDLE_INTERNAL_REDIRECT = 1; @@ -716,9 +717,9 @@ message RouteAction { google.protobuf.BoolValue enabled = 2; } - reserved 12, 18, 19, 16, 22, 21, 10; + reserved 12, 18, 19, 16, 22, 21, 10, 26, 31; - reserved "request_mirror_policy"; + reserved "request_mirror_policy", "internal_redirect_action", "max_internal_redirects"; oneof cluster_specifier { option (validate.required) = true; @@ -958,23 +959,7 @@ message RouteAction { repeated UpgradeConfig upgrade_configs = 25; - InternalRedirectAction internal_redirect_action = 26; - - // An internal redirect is handled, iff the number of previous internal redirects that a - // downstream request has encountered is lower than this value, and - // :ref:`internal_redirect_action ` - // is set to :ref:`HANDLE_INTERNAL_REDIRECT - // ` - // In the case where a downstream request is bounced among multiple routes by internal redirect, - // the first route that hits this threshold, or has - // :ref:`internal_redirect_action ` - // set to - // :ref:`PASS_THROUGH_INTERNAL_REDIRECT - // ` - // will pass the redirect back to downstream. - // - // If not specified, at most one redirect will be followed. - google.protobuf.UInt32Value max_internal_redirects = 31; + InternalRedirectPolicy internal_redirect_policy = 34; // Indicates that the route has a hedge policy. Note that if this is set, // it'll take precedence over the virtual host level hedge policy entirely @@ -1568,3 +1553,68 @@ message QueryParameterMatcher { bool present_match = 6; } } + +// HTTP Internal Redirect :ref:`architecture overview `. +// [#next-free-field: 6] +message InternalRedirectPolicy { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.route.v3.InternalRedirectPolicy"; + + // Configures :ref:`internal redirect ` behavior. + enum InternalRedirectAction { + PASS_THROUGH_INTERNAL_REDIRECT = 0; + HANDLE_INTERNAL_REDIRECT = 1; + } + + // HTTP scheme conbinations that are allowed to be handled as internal + // redirect. Downstream scheme comes first and redirect target URL scheme + // follows. + enum DownstreamAndRedirectTargetSchemePair { + HTTP_HTTP = 0; + HTTP_HTTPS = 1; + HTTPS_HTTP = 2; + HTTPS_HTTPS = 3; + } + + message Predicate { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.route.v3.InternalRedirectPolicy.Predicate"; + + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + google.protobuf.Any typed_config = 2; + } + + InternalRedirectAction internal_redirect_action = 1; + + // An internal redirect is handled, iff the number of previous internal redirects that a + // downstream request has encountered is lower than this value, and + // :ref:`internal_redirect_action ` + // is set to + // :ref:`HANDLE_INTERNAL_REDIRECT ` + // In the case where a downstream request is bounced among multiple routes by internal redirect, + // the first route that hits this threshold, or has + // :ref:`internal_redirect_action + // ` + // set to + // :ref:`PASS_THROUGH_INTERNAL_REDIRECT + // ` + // will pass the redirect back to downstream. + // + // If not specified, at most one redirect will be followed. + google.protobuf.UInt32Value max_internal_redirects = 2; + + // Defines what upstream response codes can trigger the internal redirect + // behavior. This is ignored unless + // :ref:`internal_redirect_action ` + // is set to + // :ref:`HANDLE_INTERNAL_REDIRECT ` + repeated uint32 redirect_response_codes = 3; + + // Specifies a list of predicates that is queries when an upstream response is deemed to be able to trigger an internal redirect by all other criterias. + // Any predicate in the list can reject the redirect. + repeated Predicate predicates = 4; + + // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an internal redirect + repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; +} diff --git a/api/envoy/extensions/internal_redirect/previous_routes/v3/BUILD b/api/envoy/extensions/internal_redirect/previous_routes/v3/BUILD new file mode 100644 index 000000000000..ef3541ebcb1d --- /dev/null +++ b/api/envoy/extensions/internal_redirect/previous_routes/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto b/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto new file mode 100644 index 000000000000..2b241fdfe56e --- /dev/null +++ b/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package envoy.extensions.internal_redirect.previous_routes.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.previous_routes.v3"; +option java_outer_classname = "PreviousRoutesConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Previous routes internal redirect predicate] + +// An internal redirect predicate that rejects redirect targets that are pointing +// to a route that has been follwed from the current route. +// [#extension: envoy.internal_redirect_predicates.previous_routes] +message PreviousRoutesConfig { +} diff --git a/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD b/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD new file mode 100644 index 000000000000..ef3541ebcb1d --- /dev/null +++ b/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto b/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto new file mode 100644 index 000000000000..cfe9bdbe8bc5 --- /dev/null +++ b/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package envoy.extensions.internal_redirect.whitelisted_routes.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.whitelisted_routes.v3"; +option java_outer_classname = "WhitelistedRoutesConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Whitelisted routes internal redirect predicate] + +// An internal redirect predicate that accepts only whitelisted target routes. +// [#extension: envoy.internal_redirect_predicates.whitelisted_routes] +message WhitelistedRoutesConfig { + repeated string whitelisted_route_names = 1; +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index bbb683d8bd08..a421833696fd 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -102,6 +102,8 @@ proto_library( "//envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/v3:pkg", "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", + "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", + "//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", "//envoy/extensions/transport_sockets/alts/v3:pkg", diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index 93c4ebb33600..593acfb48987 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -151,25 +151,33 @@ previously attempted priorities. Internal redirects -------------------------- -Envoy supports handling 302 redirects internally, that is capturing a 302 redirect response, -synthesizing a new request, sending it to the upstream specified by the new route match, and -returning the redirected response as the response to the original request. +Envoy supports handling 3xx redirects internally, that is capturing a configurable 3xx redirect +response, synthesizing a new request, sending it to the upstream specified by the new route match, +and returning the redirected response as the response to the original request. -Internal redirects are configured via the ref:`internal redirect action -` field and -`max internal redirects ` field in -route configuration. When redirect handling is on, any 302 response from upstream is -subject to the redirect being handled by Envoy. +Internal redirects are configured via the ref:`internal redirect policy +` field in route configuration. +When redirect handling is on, any 3xx response from upstream, that matches +ref:`redirect_response_codes ` +is subject to the redirect being handled by Envoy. For a redirect to be handled successfully it must pass the following checks: -1. Be a 302 response. -2. Have a *location* header with a valid, fully qualified URL matching the scheme of the original request. +1. Have a response code matching one of ref:`redirect_response_codes + ` +2. Have a *location* header with a valid, fully qualified URL matching the scheme of the original + request. 3. The request must have been fully processed by Envoy. 4. The request must not have a body. -5. The number of previously handled internal redirect within a given downstream request does not exceed - `max internal redirects ` of the route - that the request or redirected request is hitting. +5. The scheme pair of the downstream request and the *location* header is allowed by + `allowed downstream and target scheme pair + ` +6. The number of previously handled internal redirect within a given downstream request does not + exceed `max internal redirects + ` + of the route that the request or redirected request is hitting. +7. All ref:`predicates ` accept + the target route. Any failure will result in redirect being passed downstream instead. @@ -179,9 +187,19 @@ Since a redirected request may be bounced between different routes, any route in 2. or has a `max internal redirects ` smaller or equal to the redirect chain length when the redirect chain hits it +3. or is disallowed by any of the ref:`predicates + ` will cause the redirect to be passed downstream. +Two predicates can be used to create a DAG that defines the redirect chain, the ref:`previous routes +`. * upstream: fixed a bug where Envoy would panic when receiving a GRPC SERVICE_UNKNOWN status on the health check. +* router: internal redirect configs are moved to the :ref`internal_redirect_policy + ` field, which defines more fine + grained control of the internal redirect behavior. +* tracing: tracing configuration has been made fully dynamic and every HTTP connection manager + can now have a separate :ref:`tracing provider `. Deprecated ---------- diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/BUILD b/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/BUILD new file mode 100644 index 000000000000..ef3541ebcb1d --- /dev/null +++ b/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto new file mode 100644 index 000000000000..4081ca346141 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package envoy.extensions.internal_redirect.previous_routes.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.previous_routes.v3"; +option java_outer_classname = "PreviousRoutesConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Previous routes internal redirect predicate] + +// An internal redirect predicat that rejects redirect targets that are pointing +// to a route that has been visited in the same downstream request. +// [#extension: envoy.internal_redirect_predicates.previous_routes] +message PreviousRoutesConfig { +} diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD b/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD new file mode 100644 index 000000000000..ef3541ebcb1d --- /dev/null +++ b/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto new file mode 100644 index 000000000000..63eafe17f274 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package envoy.extensions.internal_redirect.whitelisted_routes.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.whitelisted_routes.v3"; +option java_outer_classname = "WhitelistedRoutesConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Whitelisted routes internal redirect predicate] + +// An internal redirect predicat that accepts only whitelisted target routes. +// [#extension: envoy.internal_redirect_predicates.whitelisted_routes] +message WhitelistedRoutesConfig { + repeated string whitelisted_route_names = 1; +} diff --git a/include/envoy/router/BUILD b/include/envoy/router/BUILD index 6ed49171af71..e1b98f52f68a 100644 --- a/include/envoy/router/BUILD +++ b/include/envoy/router/BUILD @@ -53,6 +53,7 @@ envoy_cc_library( hdrs = ["router.h"], external_deps = ["abseil_optional"], deps = [ + ":internal_redirect_interface", "//include/envoy/access_log:access_log_interface", "//include/envoy/common:matchers_interface", "//include/envoy/config:typed_metadata_interface", @@ -107,3 +108,12 @@ envoy_cc_library( "//include/envoy/stream_info:filter_state_interface", ], ) + +envoy_cc_library( + name = "internal_redirect_interface", + hdrs = ["internal_redirect.h"], + deps = [ + "//include/envoy/config:typed_config_interface", + "//include/envoy/stream_info:filter_state_interface", + ], +) diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h new file mode 100644 index 000000000000..ea976ccc4127 --- /dev/null +++ b/include/envoy/router/internal_redirect.h @@ -0,0 +1,49 @@ +#pragma once + +#include "envoy/config/typed_config.h" +#include "envoy/stream_info/filter_state.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Router { + +/** + * Used to decide if a internal redirect is allowed to be followed based on the target route. + */ +class InternalRedirectPredicate { +public: + virtual ~InternalRedirectPredicate() = default; + + /** + * @return whether the route specified by target_route_name is allowed to be followed. Any + * predicate returning false will prevent the redirect to be followed, causing the response to be + * proxied to the downstream. A FilterState is provided so that predicate implementation can use + * it to preserve state across internal redirects. + */ + virtual bool acceptTargetRoute(StreamInfo::FilterState& filter_State, + absl::string_view target_route_name) PURE; +}; + +using InternalRedirectPredicateSharedPtr = std::shared_ptr; + +/** + * Factory for InternalRedirectPredicte. + */ +class InternalRedirectPredicateFactory : public Config::TypedFactory { +public: + ~InternalRedirectPredicateFactory() override = default; + + /** + * @return an InternalRedirectPredicate. The given current_route_name is useful for predicates + * that need to create per-route FilterState. + */ + virtual InternalRedirectPredicateSharedPtr + createInternalRedirectPredicate(const Protobuf::Message& config, + absl::string_view current_route_name) PURE; + + std::string category() const override { return "envoy.internal_redirect_predicates"; } +}; + +} // namespace Router +} // namespace Envoy diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index 13032173d929..03a24ca303f8 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -16,6 +16,7 @@ #include "envoy/http/codes.h" #include "envoy/http/hash_policy.h" #include "envoy/http/header_map.h" +#include "envoy/router/internal_redirect.h" #include "envoy/tracing/http_tracer.h" #include "envoy/type/v3/percent.pb.h" #include "envoy/upstream/resource_manager.h" @@ -236,9 +237,42 @@ class RetryPolicy { enum class RetryStatus { No, NoOverflow, NoRetryLimitExceeded, Yes }; /** - * InternalRedirectAction from the route configuration. + * InternalRedirectPolicy from the route configuration. */ -enum class InternalRedirectAction { PassThrough, Handle }; +class InternalRedirectPolicy { +public: + virtual ~InternalRedirectPolicy() = default; + + /** + * @return whether internal redirect is enabled on this route. + */ + virtual bool enabled() const PURE; + + /** + * @return whether the given response_code should trigger an internal redirect on this route. + */ + virtual bool shouldRedirectForCode(const Http::Code& response_code) const PURE; + + /** + * Creates the target route predicates. This should really be called only once for each upstream + * redirect response. Creating the predicates lazily to avoid wasting CPU cycles on non-redirect + * responses, which should be the most common case. + * @return a vector of newly constructed InternalRedirectPredicate instances. + */ + virtual std::vector predicates() const PURE; + + /** + * @return the maximum number of allowed internal redirects on this route. + */ + virtual uint32_t maxInternalRedirects() const PURE; + + /** + * @return if the pair of HTTP scheme from the downstream request and the target url is allowed + * for internal redirect. + */ + virtual bool isDownstreamAndRedirectTargetSchemePairAllowed(bool downstream_is_https, + bool target_is_https) const PURE; +}; /** * Wraps retry state for an active routed request. @@ -683,6 +717,13 @@ class RouteEntry : public ResponseEntry { */ virtual const RetryPolicy& retryPolicy() const PURE; + /** + * @return const InternalRedirectPolicy& the internal redirect policy for the route. All routes + * have a internal redirect policy even if it is not enabled, which means redirects from + * the upstream are not followed. + * */ + virtual const InternalRedirectPolicy& internalRedirectPolicy() const PURE; + /** * @return uint32_t any route cap on bytes which should be buffered for shadowing or retries. * This is an upper bound so does not necessarily reflect the bytes which will be buffered @@ -825,17 +866,6 @@ class RouteEntry : public ResponseEntry { */ virtual const UpgradeMap& upgradeMap() const PURE; - /** - * @returns the internal redirect action which should be taken on this route. - */ - virtual InternalRedirectAction internalRedirectAction() const PURE; - - /** - * @returns the threshold of number of previously handled internal redirects, for this route to - * stop handle internal redirects. - */ - virtual uint32_t maxInternalRedirects() const PURE; - /** * @return std::string& the name of the route. */ diff --git a/source/common/http/async_client_impl.cc b/source/common/http/async_client_impl.cc index b2e40ab397f6..6058a7d4f551 100644 --- a/source/common/http/async_client_impl.cc +++ b/source/common/http/async_client_impl.cc @@ -20,6 +20,7 @@ const std::vector> const AsyncStreamImpl::NullHedgePolicy AsyncStreamImpl::RouteEntryImpl::hedge_policy_; const AsyncStreamImpl::NullRateLimitPolicy AsyncStreamImpl::RouteEntryImpl::rate_limit_policy_; const AsyncStreamImpl::NullRetryPolicy AsyncStreamImpl::RouteEntryImpl::retry_policy_; +const Router::InternalRedirectPolicyImpl AsyncStreamImpl::RouteEntryImpl::internal_redirect_policy_; const std::vector AsyncStreamImpl::RouteEntryImpl::shadow_policies_; const AsyncStreamImpl::NullVirtualHost AsyncStreamImpl::RouteEntryImpl::virtual_host_; const AsyncStreamImpl::NullRateLimitPolicy AsyncStreamImpl::NullVirtualHost::rate_limit_policy_; diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h index 608b2188dc1b..6131baa34aa0 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h @@ -227,6 +227,9 @@ class AsyncStreamImpl : public AsyncClient::Stream, } const Router::RateLimitPolicy& rateLimitPolicy() const override { return rate_limit_policy_; } const Router::RetryPolicy& retryPolicy() const override { return retry_policy_; } + const Router::InternalRedirectPolicy& internalRedirectPolicy() const override { + return internal_redirect_policy_; + } uint32_t retryShadowBufferLimit() const override { return std::numeric_limits::max(); } @@ -272,15 +275,12 @@ class AsyncStreamImpl : public AsyncClient::Stream, bool includeAttemptCountInRequest() const override { return false; } bool includeAttemptCountInResponse() const override { return false; } const Router::RouteEntry::UpgradeMap& upgradeMap() const override { return upgrade_map_; } - Router::InternalRedirectAction internalRedirectAction() const override { - return Router::InternalRedirectAction::PassThrough; - } - uint32_t maxInternalRedirects() const override { return 1; } const std::string& routeName() const override { return route_name_; } std::unique_ptr hash_policy_; static const NullHedgePolicy hedge_policy_; static const NullRateLimitPolicy rate_limit_policy_; static const NullRetryPolicy retry_policy_; + static const Router::InternalRedirectPolicyImpl internal_redirect_policy_; static const std::vector shadow_policies_; static const NullVirtualHost virtual_host_; static const std::multimap opaque_config_; diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 0287a5b6c974..6d857c38e5ef 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -45,18 +45,6 @@ namespace Envoy { namespace Router { namespace { -InternalRedirectAction -convertInternalRedirectAction(const envoy::config::route::v3::RouteAction& route) { - switch (route.internal_redirect_action()) { - case envoy::config::route::v3::RouteAction::PASS_THROUGH_INTERNAL_REDIRECT: - return InternalRedirectAction::PassThrough; - case envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT: - return InternalRedirectAction::Handle; - default: - return InternalRedirectAction::PassThrough; - } -} - const std::string DEPRECATED_ROUTER_NAME = "envoy.router"; const absl::string_view getPath(const Http::RequestHeaderMap& headers) { @@ -156,6 +144,79 @@ Upstream::RetryPrioritySharedPtr RetryPolicyImpl::retryPriority() const { *validation_visitor_, num_retries_); } +InternalRedirectPolicyImpl::InternalRedirectPolicyImpl( + const envoy::config::route::v3::InternalRedirectPolicy& policy_config, + ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) + : enabled_(internalRedirectEnabled(policy_config)), current_route_name_(current_route_name), + redirect_response_codes_(buildRedirectResponseCodes(policy_config)), + max_internal_redirects_( + PROTOBUF_GET_WRAPPED_OR_DEFAULT(policy_config, max_internal_redirects, 1)), + allowed_scheme_pairs_(buildAllowedSchemePairs(policy_config)) { + for (const auto& predicate : policy_config.predicates()) { + auto& factory = + Envoy::Config::Utility::getAndCheckFactory(predicate); + auto config = factory.createEmptyConfigProto(); + Envoy::Config::Utility::translateOpaqueConfig(predicate.typed_config(), {}, validator, *config); + predicate_factories_.emplace_back(factory, std::move(config)); + } +} + +std::vector InternalRedirectPolicyImpl::predicates() const { + std::vector predicates; + for (const auto& predicate_factory : predicate_factories_) { + predicates.emplace_back(predicate_factory.first.createInternalRedirectPredicate( + *predicate_factory.second, current_route_name_)); + } + return predicates; +} + +bool InternalRedirectPolicyImpl::isDownstreamAndRedirectTargetSchemePairAllowed( + bool downstream_is_https, bool target_is_https) const { + return allowed_scheme_pairs_.contains(compactSchemePair(downstream_is_https, target_is_https)); +} + +bool InternalRedirectPolicyImpl::internalRedirectEnabled( + const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const { + switch (policy_config.internal_redirect_action()) { + case envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT: + return true; + case envoy::config::route::v3::InternalRedirectPolicy::PASS_THROUGH_INTERNAL_REDIRECT: + return false; + default: + return false; + } +} + +absl::flat_hash_set InternalRedirectPolicyImpl::buildRedirectResponseCodes( + const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const { + if (policy_config.redirect_response_codes_size() == 0) { + return absl::flat_hash_set{Http::Code::Found}; + } + absl::flat_hash_set ret; + std::for_each(policy_config.redirect_response_codes().begin(), + policy_config.redirect_response_codes().end(), [&ret](uint32_t response_code) { + ret.insert(static_cast(response_code)); + }); + return ret; +} + +absl::flat_hash_set InternalRedirectPolicyImpl::buildAllowedSchemePairs( + const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const { + if (policy_config.allowed_downstream_and_target_scheme_pairs_size() == 0) { + return absl::flat_hash_set{compactSchemePair(true, true), + compactSchemePair(true, false), + compactSchemePair(false, false)}; + } + return absl::flat_hash_set( + policy_config.allowed_downstream_and_target_scheme_pairs().begin(), + policy_config.allowed_downstream_and_target_scheme_pairs().end()); +} + +uint8_t InternalRedirectPolicyImpl::compactSchemePair(bool downstream_is_https, + bool target_is_https) const { + return (downstream_is_https ? 0x2 : 0x0) | (target_is_https ? 0x1 : 0x0); +} + CorsPolicyImpl::CorsPolicyImpl(const envoy::config::route::v3::CorsPolicy& config, Runtime::Loader& loader) : config_(config), loader_(loader), allow_methods_(config.allow_methods()), @@ -278,6 +339,8 @@ RouteEntryImplBase::RouteEntryImplBase(const VirtualHostImpl& vhost, strip_query_(route.redirect().strip_query()), hedge_policy_(buildHedgePolicy(vhost.hedgePolicy(), route.route())), retry_policy_(buildRetryPolicy(vhost.retryPolicy(), route.route(), validator)), + internal_redirect_policy_( + buildInternalRedirectPolicy(route.route(), validator, route.name())), rate_limit_policy_(route.route().rate_limits()), priority_(ConfigUtility::parsePriority(route.route().priority())), config_headers_(Http::HeaderUtility::buildHeaderDataVector(route.match().headers())), @@ -297,10 +360,7 @@ RouteEntryImplBase::RouteEntryImplBase(const VirtualHostImpl& vhost, per_filter_configs_(route.typed_per_filter_config(), route.hidden_envoy_deprecated_per_filter_config(), factory_context, validator), - route_name_(route.name()), time_source_(factory_context.dispatcher().timeSource()), - internal_redirect_action_(convertInternalRedirectAction(route.route())), - max_internal_redirects_( - PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.route(), max_internal_redirects, 1)) { + route_name_(route.name()), time_source_(factory_context.dispatcher().timeSource()) { if (route.route().has_metadata_match()) { const auto filter_it = route.route().metadata_match().filter_metadata().find( Envoy::Config::MetadataFilters::get().ENVOY_LB); @@ -703,6 +763,33 @@ RetryPolicyImpl RouteEntryImplBase::buildRetryPolicy( return RetryPolicyImpl(); } +InternalRedirectPolicyImpl RouteEntryImplBase::buildInternalRedirectPolicy( + const envoy::config::route::v3::RouteAction& route_config, + ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) const { + if (route_config.has_internal_redirect_policy()) { + return InternalRedirectPolicyImpl(route_config.internal_redirect_policy(), validator, + current_route_name); + } + envoy::config::route::v3::InternalRedirectPolicy policy_config; + switch (route_config.internal_redirect_action()) { + case envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT: + policy_config.set_internal_redirect_action( + envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); + break; + case envoy::config::route::v3::RouteAction::PASS_THROUGH_INTERNAL_REDIRECT: + policy_config.set_internal_redirect_action( + envoy::config::route::v3::InternalRedirectPolicy::PASS_THROUGH_INTERNAL_REDIRECT); + break; + default: + policy_config.set_internal_redirect_action( + envoy::config::route::v3::InternalRedirectPolicy::PASS_THROUGH_INTERNAL_REDIRECT); + } + if (route_config.has_max_internal_redirects()) { + *policy_config.mutable_max_internal_redirects() = route_config.max_internal_redirects(); + } + return InternalRedirectPolicyImpl(policy_config, validator, current_route_name); +} + DecoratorConstPtr RouteEntryImplBase::parseDecorator(const envoy::config::route::v3::Route& route) { DecoratorConstPtr ret; if (route.has_decorator()) { diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index 89add8a903e5..d1ed06f9669f 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -381,6 +381,52 @@ class RouteTracingImpl : public RouteTracing { Tracing::CustomTagMap custom_tags_; }; +/** + * Implementation of InternalRedirectPolicy that reads from the proto + * InternalRedirectPolicy of the RouteAction. + */ +class InternalRedirectPolicyImpl : public InternalRedirectPolicy { +public: + explicit InternalRedirectPolicyImpl( + const envoy::config::route::v3::InternalRedirectPolicy& policy_config, + ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name); + InternalRedirectPolicyImpl() = default; + + bool enabled() const override { return enabled_; } + + bool shouldRedirectForCode(const Http::Code& response_code) const override { + return redirect_response_codes_.contains(response_code); + } + + std::vector predicates() const override; + + uint32_t maxInternalRedirects() const override { return max_internal_redirects_; } + + bool isDownstreamAndRedirectTargetSchemePairAllowed(bool downstream_is_https, + bool target_is_https) const override; + +private: + bool internalRedirectEnabled( + const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const; + + absl::flat_hash_set buildRedirectResponseCodes( + const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const; + + absl::flat_hash_set buildAllowedSchemePairs( + const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const; + + uint8_t compactSchemePair(bool downstream_is_https, bool target_is_https) const; + + const bool enabled_{false}; + const std::string current_route_name_; + const absl::flat_hash_set redirect_response_codes_; + const uint32_t max_internal_redirects_{1}; + const absl::flat_hash_set allowed_scheme_pairs_; + + std::vector> + predicate_factories_; +}; + /** * Base implementation for all route entries. */ @@ -437,6 +483,9 @@ class RouteEntryImplBase : public RouteEntry, Upstream::ResourcePriority priority() const override { return priority_; } const RateLimitPolicy& rateLimitPolicy() const override { return rate_limit_policy_; } const RetryPolicy& retryPolicy() const override { return retry_policy_; } + const InternalRedirectPolicy& internalRedirectPolicy() const override { + return internal_redirect_policy_; + } uint32_t retryShadowBufferLimit() const override { return retry_shadow_buffer_limit_; } const std::vector& shadowPolicies() const override { return shadow_policies_; } const VirtualCluster* virtualCluster(const Http::HeaderMap& headers) const override { @@ -466,10 +515,6 @@ class RouteEntryImplBase : public RouteEntry, return vhost_.includeAttemptCountInResponse(); } const UpgradeMap& upgradeMap() const override { return upgrade_map_; } - InternalRedirectAction internalRedirectAction() const override { - return internal_redirect_action_; - } - uint32_t maxInternalRedirects() const override { return max_internal_redirects_; } // Router::DirectResponseEntry std::string newPath(const Http::RequestHeaderMap& headers) const override; @@ -541,6 +586,9 @@ class RouteEntryImplBase : public RouteEntry, Upstream::ResourcePriority priority() const override { return parent_->priority(); } const RateLimitPolicy& rateLimitPolicy() const override { return parent_->rateLimitPolicy(); } const RetryPolicy& retryPolicy() const override { return parent_->retryPolicy(); } + const InternalRedirectPolicy& internalRedirectPolicy() const override { + return parent_->internalRedirectPolicy(); + } uint32_t retryShadowBufferLimit() const override { return parent_->retryShadowBufferLimit(); } const std::vector& shadowPolicies() const override { return parent_->shadowPolicies(); @@ -592,10 +640,6 @@ class RouteEntryImplBase : public RouteEntry, return parent_->includeAttemptCountInResponse(); } const UpgradeMap& upgradeMap() const override { return parent_->upgradeMap(); } - InternalRedirectAction internalRedirectAction() const override { - return parent_->internalRedirectAction(); - } - uint32_t maxInternalRedirects() const override { return parent_->maxInternalRedirects(); } // Router::Route const DirectResponseEntry* directResponseEntry() const override { return nullptr; } @@ -684,6 +728,11 @@ class RouteEntryImplBase : public RouteEntry, const envoy::config::route::v3::RouteAction& route_config, ProtobufMessage::ValidationVisitor& validation_visitor) const; + InternalRedirectPolicyImpl + buildInternalRedirectPolicy(const envoy::config::route::v3::RouteAction& route_config, + ProtobufMessage::ValidationVisitor& validator, + absl::string_view current_route_name) const; + // Default timeout is 15s if nothing is specified in the route config. static const uint64_t DEFAULT_ROUTE_TIMEOUT_MS = 15000; @@ -710,6 +759,7 @@ class RouteEntryImplBase : public RouteEntry, const bool strip_query_; const HedgePolicyImpl hedge_policy_; const RetryPolicyImpl retry_policy_; + const InternalRedirectPolicyImpl internal_redirect_policy_; const RateLimitPolicyImpl rate_limit_policy_; std::vector shadow_policies_; const Upstream::ResourcePriority priority_; @@ -739,8 +789,6 @@ class RouteEntryImplBase : public RouteEntry, PerFilterConfigs per_filter_configs_; const std::string route_name_; TimeSource& time_source_; - InternalRedirectAction internal_redirect_action_; - uint32_t max_internal_redirects_{1}; }; /** diff --git a/source/common/router/router.cc b/source/common/router/router.cc index e507e06a4a5c..4de404510bc9 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -46,22 +46,22 @@ constexpr char NumInternalRedirectsFilterStateName[] = "num_internal_redirects"; uint32_t getLength(const Buffer::Instance* instance) { return instance ? instance->length() : 0; } -bool schemeIsHttp(const Http::RequestHeaderMap& downstream_headers, - const Network::Connection& connection) { +bool schemeIsHttps(const Http::RequestHeaderMap& downstream_headers, + const Network::Connection& connection) { + if (!connection.ssl()) { + return false; + } if (downstream_headers.ForwardedProto() && downstream_headers.ForwardedProto()->value().getStringView() == Http::Headers::get().SchemeValues.Http) { - return true; - } - if (!connection.ssl()) { - return true; + return false; } - return false; + return true; } bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream_headers, - StreamInfo::FilterState& filter_state, - uint32_t max_internal_redirects, + Http::StreamDecoderFilterCallbacks* callbacks, + const InternalRedirectPolicy& policy, const Http::HeaderEntry& internal_redirect, const Network::Connection& connection) { // Make sure the redirect response contains a URL to redirect to. @@ -78,30 +78,33 @@ bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream } // Don't allow serving TLS responses over plaintext. - bool scheme_is_http = schemeIsHttp(downstream_headers, connection); - if (scheme_is_http && absolute_url.scheme() == Http::Headers::get().SchemeValues.Https) { + bool downstream_is_https = schemeIsHttps(downstream_headers, connection); + bool target_is_https = absolute_url.scheme() == Http::Headers::get().SchemeValues.Https; + if (!policy.isDownstreamAndRedirectTargetSchemePairAllowed(downstream_is_https, + target_is_https)) { return false; } + const StreamInfo::FilterStateSharedPtr& filter_state = callbacks->streamInfo().filterState(); // Make sure that performing the redirect won't result in exceeding the configured number of // redirects allowed for this route. - if (!filter_state.hasData(NumInternalRedirectsFilterStateName)) { - filter_state.setData( + if (!filter_state->hasData(NumInternalRedirectsFilterStateName)) { + filter_state->setData( NumInternalRedirectsFilterStateName, std::make_shared(0), StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Request); } StreamInfo::UInt32Accessor& num_internal_redirect = - filter_state.getDataMutable(NumInternalRedirectsFilterStateName); + filter_state->getDataMutable(NumInternalRedirectsFilterStateName); - if (num_internal_redirect.value() >= max_internal_redirects) { + if (num_internal_redirect.value() >= policy.maxInternalRedirects()) { return false; } num_internal_redirect.increment(); // Preserve the original request URL for the second pass. downstream_headers.setEnvoyOriginalUrl( - absl::StrCat(scheme_is_http ? Http::Headers::get().SchemeValues.Http - : Http::Headers::get().SchemeValues.Https, + absl::StrCat(downstream_is_https ? Http::Headers::get().SchemeValues.Https + : Http::Headers::get().SchemeValues.Http, "://", downstream_headers.Host()->value().getStringView(), downstream_headers.Path()->value().getStringView())); @@ -110,6 +113,19 @@ bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream downstream_headers.setHost(absolute_url.hostAndPort()); downstream_headers.setPath(absolute_url.pathAndQueryParams()); + callbacks->clearRouteCache(); + auto route = callbacks->route(); + // Don't allow a redirect to a non existing route. + if (!route) { + return false; + } + + auto& route_name = route->routeEntry()->routeName(); + for (auto& predicate : policy.predicates()) { + if (!predicate->acceptTargetRoute(*filter_state, route_name)) { + return false; + } + } return true; } @@ -1242,8 +1258,9 @@ void Filter::onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPt } } - if (static_cast(response_code) == Http::Code::Found && - route_entry_->internalRedirectAction() == InternalRedirectAction::Handle && + if (route_entry_->internalRedirectPolicy().enabled() && + route_entry_->internalRedirectPolicy().shouldRedirectForCode( + static_cast(response_code)) && setupRedirect(*headers, upstream_request)) { return; // If the redirect could not be handled, fail open and let it pass to the @@ -1426,14 +1443,12 @@ bool Filter::setupRedirect(const Http::ResponseHeaderMap& headers, attempting_internal_redirect_with_complete_stream_ = upstream_request.upstreamTiming().last_upstream_rx_byte_received_ && downstream_end_stream_; - const StreamInfo::FilterStateSharedPtr& filter_state = callbacks_->streamInfo().filterState(); - // Redirects are not supported for streaming requests yet. if (downstream_end_stream_ && !callbacks_->decodingBuffer() && // Redirects with body not yet supported. location != nullptr && - convertRequestHeadersForInternalRedirect(*downstream_headers_, *filter_state, - route_entry_->maxInternalRedirects(), *location, + convertRequestHeadersForInternalRedirect(*downstream_headers_, callbacks_, + route_entry_->internalRedirectPolicy(), *location, *callbacks_->connection()) && callbacks_->recreateStream()) { cluster_->stats().upstream_internal_redirect_succeeded_total_.inc(); diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 1bb72dfb7e1d..c1705ab4d381 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -3,91 +3,85 @@ EXTENSIONS = { # # Access loggers # - - "envoy.access_loggers.file": "//source/extensions/access_loggers/file:config", - "envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config", - "envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config", + "envoy.access_loggers.file": "//source/extensions/access_loggers/file:config", + "envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config", + "envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config", # # Clusters # - - "envoy.clusters.aggregate": "//source/extensions/clusters/aggregate:cluster", - "envoy.clusters.dynamic_forward_proxy": "//source/extensions/clusters/dynamic_forward_proxy:cluster", - "envoy.clusters.redis": "//source/extensions/clusters/redis:redis_cluster", + "envoy.clusters.aggregate": "//source/extensions/clusters/aggregate:cluster", + "envoy.clusters.dynamic_forward_proxy": "//source/extensions/clusters/dynamic_forward_proxy:cluster", + "envoy.clusters.redis": "//source/extensions/clusters/redis:redis_cluster", # # gRPC Credentials Plugins # - - "envoy.grpc_credentials.file_based_metadata": "//source/extensions/grpc_credentials/file_based_metadata:config", - "envoy.grpc_credentials.aws_iam": "//source/extensions/grpc_credentials/aws_iam:config", + "envoy.grpc_credentials.file_based_metadata": "//source/extensions/grpc_credentials/file_based_metadata:config", + "envoy.grpc_credentials.aws_iam": "//source/extensions/grpc_credentials/aws_iam:config", # # Health checkers # - - "envoy.health_checkers.redis": "//source/extensions/health_checkers/redis:config", + "envoy.health_checkers.redis": "//source/extensions/health_checkers/redis:config", # # HTTP filters # - - "envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config", - "envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config", - "envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config", - "envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config", - "envoy.filters.http.cache": "//source/extensions/filters/http/cache:config", - "envoy.filters.http.cors": "//source/extensions/filters/http/cors:config", - "envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config", - "envoy.filters.http.dynamic_forward_proxy": "//source/extensions/filters/http/dynamic_forward_proxy:config", - "envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config", - "envoy.filters.http.ext_authz": "//source/extensions/filters/http/ext_authz:config", - "envoy.filters.http.fault": "//source/extensions/filters/http/fault:config", - "envoy.filters.http.grpc_http1_bridge": "//source/extensions/filters/http/grpc_http1_bridge:config", - "envoy.filters.http.grpc_http1_reverse_bridge": "//source/extensions/filters/http/grpc_http1_reverse_bridge:config", - "envoy.filters.http.grpc_json_transcoder": "//source/extensions/filters/http/grpc_json_transcoder:config", - "envoy.filters.http.grpc_stats": "//source/extensions/filters/http/grpc_stats:config", - "envoy.filters.http.grpc_web": "//source/extensions/filters/http/grpc_web:config", - "envoy.filters.http.gzip": "//source/extensions/filters/http/gzip:config", - "envoy.filters.http.header_to_metadata": "//source/extensions/filters/http/header_to_metadata:config", - "envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config", - "envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config", - "envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", - "envoy.filters.http.lua": "//source/extensions/filters/http/lua:config", - "envoy.filters.http.on_demand": "//source/extensions/filters/http/on_demand:config", - "envoy.filters.http.original_src": "//source/extensions/filters/http/original_src:config", - "envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config", - "envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config", - "envoy.filters.http.router": "//source/extensions/filters/http/router:config", - "envoy.filters.http.squash": "//source/extensions/filters/http/squash:config", - "envoy.filters.http.tap": "//source/extensions/filters/http/tap:config", + "envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config", + "envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config", + "envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config", + "envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config", + "envoy.filters.http.cache": "//source/extensions/filters/http/cache:config", + "envoy.filters.http.cors": "//source/extensions/filters/http/cors:config", + "envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config", + "envoy.filters.http.dynamic_forward_proxy": "//source/extensions/filters/http/dynamic_forward_proxy:config", + "envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config", + "envoy.filters.http.ext_authz": "//source/extensions/filters/http/ext_authz:config", + "envoy.filters.http.fault": "//source/extensions/filters/http/fault:config", + "envoy.filters.http.grpc_http1_bridge": "//source/extensions/filters/http/grpc_http1_bridge:config", + "envoy.filters.http.grpc_http1_reverse_bridge": "//source/extensions/filters/http/grpc_http1_reverse_bridge:config", + "envoy.filters.http.grpc_json_transcoder": "//source/extensions/filters/http/grpc_json_transcoder:config", + "envoy.filters.http.grpc_stats": "//source/extensions/filters/http/grpc_stats:config", + "envoy.filters.http.grpc_web": "//source/extensions/filters/http/grpc_web:config", + "envoy.filters.http.gzip": "//source/extensions/filters/http/gzip:config", + "envoy.filters.http.header_to_metadata": "//source/extensions/filters/http/header_to_metadata:config", + "envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config", + "envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config", + "envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", + "envoy.filters.http.lua": "//source/extensions/filters/http/lua:config", + "envoy.filters.http.on_demand": "//source/extensions/filters/http/on_demand:config", + "envoy.filters.http.original_src": "//source/extensions/filters/http/original_src:config", + "envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config", + "envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config", + "envoy.filters.http.router": "//source/extensions/filters/http/router:config", + "envoy.filters.http.squash": "//source/extensions/filters/http/squash:config", + "envoy.filters.http.tap": "//source/extensions/filters/http/tap:config", # # Listener filters # - - "envoy.filters.listener.http_inspector": "//source/extensions/filters/listener/http_inspector:config", + "envoy.filters.listener.http_inspector": "//source/extensions/filters/listener/http_inspector:config", # NOTE: The original_dst filter is implicitly loaded if original_dst functionality is # configured on the listener. Do not remove it in that case or configs will fail to load. - "envoy.filters.listener.original_dst": "//source/extensions/filters/listener/original_dst:config", - "envoy.filters.listener.original_src": "//source/extensions/filters/listener/original_src:config", + "envoy.filters.listener.original_dst": "//source/extensions/filters/listener/original_dst:config", + "envoy.filters.listener.original_src": "//source/extensions/filters/listener/original_src:config", # NOTE: The proxy_protocol filter is implicitly loaded if proxy_protocol functionality is # configured on the listener. Do not remove it in that case or configs will fail to load. - "envoy.filters.listener.proxy_protocol": "//source/extensions/filters/listener/proxy_protocol:config", - "envoy.filters.listener.tls_inspector": "//source/extensions/filters/listener/tls_inspector:config", + "envoy.filters.listener.proxy_protocol": "//source/extensions/filters/listener/proxy_protocol:config", + "envoy.filters.listener.tls_inspector": "//source/extensions/filters/listener/tls_inspector:config", # # Network filters # - - "envoy.filters.network.client_ssl_auth": "//source/extensions/filters/network/client_ssl_auth:config", - "envoy.filters.network.direct_response": "//source/extensions/filters/network/direct_response:config", - "envoy.filters.network.dubbo_proxy": "//source/extensions/filters/network/dubbo_proxy:config", - "envoy.filters.network.echo": "//source/extensions/filters/network/echo:config", - "envoy.filters.network.ext_authz": "//source/extensions/filters/network/ext_authz:config", - "envoy.filters.network.http_connection_manager": "//source/extensions/filters/network/http_connection_manager:config", + "envoy.filters.network.client_ssl_auth": "//source/extensions/filters/network/client_ssl_auth:config", + "envoy.filters.network.direct_response": "//source/extensions/filters/network/direct_response:config", + "envoy.filters.network.dubbo_proxy": "//source/extensions/filters/network/dubbo_proxy:config", + "envoy.filters.network.echo": "//source/extensions/filters/network/echo:config", + "envoy.filters.network.ext_authz": "//source/extensions/filters/network/ext_authz:config", + "envoy.filters.network.http_connection_manager": "//source/extensions/filters/network/http_connection_manager:config", # WiP +<<<<<<< HEAD "envoy.filters.network.kafka_broker": "//source/extensions/filters/network/kafka:kafka_broker_config_lib", "envoy.filters.network.local_ratelimit": "//source/extensions/filters/network/local_ratelimit:config", "envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config", @@ -101,74 +95,255 @@ EXTENSIONS = { "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", "envoy.filters.network.sni_dynamic_forward_proxy": "//source/extensions/filters/network/sni_dynamic_forward_proxy:config", "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", +======= + "envoy.filters.network.kafka_broker": "//source/extensions/filters/network/kafka:kafka_broker_config_lib", + "envoy.filters.network.local_ratelimit": "//source/extensions/filters/network/local_ratelimit:config", + "envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config", + "envoy.filters.network.mysql_proxy": "//source/extensions/filters/network/mysql_proxy:config", + "envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config", + "envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config", + "envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config", + "envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config", + "envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config", + "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", + "envoy.filters.network.sni_dynamic_forward_proxy": "//source/extensions/filters/network/sni_dynamic_forward_proxy:config", + "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", +>>>>>>> cdc5e08ee... Implemented two initial internal redirect predicates: previous_routes # # UDP filters # - - "envoy.filters.udp_listener.dns_filter": "//source/extensions/filters/udp/dns_filter:config", - "envoy.filters.udp_listener.udp_proxy": "//source/extensions/filters/udp/udp_proxy:config", + "envoy.filters.udp_listener.dns_filter": "//source/extensions/filters/udp/dns_filter:config", + "envoy.filters.udp_listener.udp_proxy": "//source/extensions/filters/udp/udp_proxy:config", # # Resource monitors # - - "envoy.resource_monitors.fixed_heap": "//source/extensions/resource_monitors/fixed_heap:config", - "envoy.resource_monitors.injected_resource": "//source/extensions/resource_monitors/injected_resource:config", + "envoy.resource_monitors.fixed_heap": "//source/extensions/resource_monitors/fixed_heap:config", + "envoy.resource_monitors.injected_resource": "//source/extensions/resource_monitors/injected_resource:config", # # Stat sinks # - - "envoy.stat_sinks.dog_statsd": "//source/extensions/stat_sinks/dog_statsd:config", - "envoy.stat_sinks.hystrix": "//source/extensions/stat_sinks/hystrix:config", - "envoy.stat_sinks.metrics_service": "//source/extensions/stat_sinks/metrics_service:config", - "envoy.stat_sinks.statsd": "//source/extensions/stat_sinks/statsd:config", + "envoy.stat_sinks.dog_statsd": "//source/extensions/stat_sinks/dog_statsd:config", + "envoy.stat_sinks.hystrix": "//source/extensions/stat_sinks/hystrix:config", + "envoy.stat_sinks.metrics_service": "//source/extensions/stat_sinks/metrics_service:config", + "envoy.stat_sinks.statsd": "//source/extensions/stat_sinks/statsd:config", # # Thrift filters # + "envoy.filters.thrift.router": "//source/extensions/filters/network/thrift_proxy/router:config", + "envoy.filters.thrift.ratelimit": "//source/extensions/filters/network/thrift_proxy/filters/ratelimit:config", + + # + # Tracers + # + "envoy.tracers.dynamic_ot": "//source/extensions/tracers/dynamic_ot:config", + "envoy.tracers.lightstep": "//source/extensions/tracers/lightstep:config", + "envoy.tracers.datadog": "//source/extensions/tracers/datadog:config", + "envoy.tracers.zipkin": "//source/extensions/tracers/zipkin:config", + "envoy.tracers.opencensus": "//source/extensions/tracers/opencensus:config", + # WiP + "envoy.tracers.xray": "//source/extensions/tracers/xray:config", + + # + # Transport sockets + # + "envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config", + "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", + "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", + + # + # Retry host predicates + # + "envoy.retry_host_predicates.previous_hosts": "//source/extensions/retry/host/previous_hosts:config", + "envoy.retry_host_predicates.omit_canary_hosts": "//source/extensions/retry/host/omit_canary_hosts:config", + "envoy.retry_host_predicates.omit_host_metadata": "//source/extensions/retry/host/omit_host_metadata:config", + + # + # Retry priorities + # + "envoy.retry_priorities.previous_priorities": "//source/extensions/retry/priority/previous_priorities:config", + + # + # CacheFilter plugins + # + "envoy.filters.http.cache.simple_http_cache": "//source/extensions/filters/http/cache/simple_http_cache:simple_http_cache_lib", + + # + # Internal redirect predicates + # + "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", + "envoy.internal_redirect_predicates.whitelisted_routes": "//source/extensions/internal_redirect/whitelisted_routes:config", +} +<<<<<<< HEAD +======= + +WINDOWS_EXTENSIONS = { + # + # Access loggers + # + "envoy.access_loggers.file": "//source/extensions/access_loggers/file:config", + "envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config", + "envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config", + + # + # Clusters + # + "envoy.clusters.aggregate": "//source/extensions/clusters/aggregate:cluster", + "envoy.clusters.dynamic_forward_proxy": "//source/extensions/clusters/dynamic_forward_proxy:cluster", + # "envoy.clusters.redis": "//source/extensions/clusters/redis:redis_cluster", + + # + # gRPC Credentials Plugins + # + "envoy.grpc_credentials.file_based_metadata": "//source/extensions/grpc_credentials/file_based_metadata:config", + "envoy.grpc_credentials.aws_iam": "//source/extensions/grpc_credentials/aws_iam:config", + + # + # Health checkers + # - "envoy.filters.thrift.router": "//source/extensions/filters/network/thrift_proxy/router:config", - "envoy.filters.thrift.ratelimit": "//source/extensions/filters/network/thrift_proxy/filters/ratelimit:config", + # "envoy.health_checkers.redis": "//source/extensions/health_checkers/redis:config", + + # + # HTTP filters + # + + # "envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config", + "envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config", + "envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config", + "envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config", + "envoy.filters.http.cache": "//source/extensions/filters/http/cache:config", + "envoy.filters.http.cors": "//source/extensions/filters/http/cors:config", + "envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config", + "envoy.filters.http.dynamic_forward_proxy": "//source/extensions/filters/http/dynamic_forward_proxy:config", + "envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config", + "envoy.filters.http.ext_authz": "//source/extensions/filters/http/ext_authz:config", + "envoy.filters.http.fault": "//source/extensions/filters/http/fault:config", + "envoy.filters.http.grpc_http1_bridge": "//source/extensions/filters/http/grpc_http1_bridge:config", + "envoy.filters.http.grpc_http1_reverse_bridge": "//source/extensions/filters/http/grpc_http1_reverse_bridge:config", + "envoy.filters.http.grpc_json_transcoder": "//source/extensions/filters/http/grpc_json_transcoder:config", + "envoy.filters.http.grpc_stats": "//source/extensions/filters/http/grpc_stats:config", + "envoy.filters.http.grpc_web": "//source/extensions/filters/http/grpc_web:config", + "envoy.filters.http.gzip": "//source/extensions/filters/http/gzip:config", + "envoy.filters.http.header_to_metadata": "//source/extensions/filters/http/header_to_metadata:config", + "envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config", + "envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config", + "envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", + # "envoy.filters.http.lua": "//source/extensions/filters/http/lua:config", + "envoy.filters.http.on_demand": "//source/extensions/filters/http/on_demand:config", + "envoy.filters.http.original_src": "//source/extensions/filters/http/original_src:config", + "envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config", + "envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config", + "envoy.filters.http.router": "//source/extensions/filters/http/router:config", + "envoy.filters.http.squash": "//source/extensions/filters/http/squash:config", + "envoy.filters.http.tap": "//source/extensions/filters/http/tap:config", + + # + # Listener filters + # + "envoy.filters.listener.http_inspector": "//source/extensions/filters/listener/http_inspector:config", + # NOTE: The original_dst filter is implicitly loaded if original_dst functionality is + # configured on the listener. Do not remove it in that case or configs will fail to load. + "envoy.filters.listener.original_dst": "//source/extensions/filters/listener/original_dst:config", + "envoy.filters.listener.original_src": "//source/extensions/filters/listener/original_src:config", + # NOTE: The proxy_protocol filter is implicitly loaded if proxy_protocol functionality is + # configured on the listener. Do not remove it in that case or configs will fail to load. + "envoy.filters.listener.proxy_protocol": "//source/extensions/filters/listener/proxy_protocol:config", + "envoy.filters.listener.tls_inspector": "//source/extensions/filters/listener/tls_inspector:config", + + # + # Network filters + # + "envoy.filters.network.client_ssl_auth": "//source/extensions/filters/network/client_ssl_auth:config", + "envoy.filters.network.direct_response": "//source/extensions/filters/network/direct_response:config", + "envoy.filters.network.dubbo_proxy": "//source/extensions/filters/network/dubbo_proxy:config", + "envoy.filters.network.echo": "//source/extensions/filters/network/echo:config", + "envoy.filters.network.ext_authz": "//source/extensions/filters/network/ext_authz:config", + "envoy.filters.network.http_connection_manager": "//source/extensions/filters/network/http_connection_manager:config", + # WiP + # "envoy.filters.network.kafka_broker": "//source/extensions/filters/network/kafka:kafka_broker_config_lib", + "envoy.filters.network.local_ratelimit": "//source/extensions/filters/network/local_ratelimit:config", + "envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config", + # "envoy.filters.network.mysql_proxy": "//source/extensions/filters/network/mysql_proxy:config", + "envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config", + "envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config", + # "envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config", + "envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config", + "envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config", + "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", + "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", + + # + # UDP filters + # + "envoy.filters.udp_listener.udp_proxy": "//source/extensions/filters/udp/udp_proxy:config", + + # + # Resource monitors + # + "envoy.resource_monitors.fixed_heap": "//source/extensions/resource_monitors/fixed_heap:config", + "envoy.resource_monitors.injected_resource": "//source/extensions/resource_monitors/injected_resource:config", + + # + # Stat sinks + # + "envoy.stat_sinks.dog_statsd": "//source/extensions/stat_sinks/dog_statsd:config", + # "envoy.stat_sinks.hystrix": "//source/extensions/stat_sinks/hystrix:config", + "envoy.stat_sinks.metrics_service": "//source/extensions/stat_sinks/metrics_service:config", + # "envoy.stat_sinks.statsd": "//source/extensions/stat_sinks/statsd:config", + + # + # Thrift filters + # + "envoy.filters.thrift.router": "//source/extensions/filters/network/thrift_proxy/router:config", + "envoy.filters.thrift.ratelimit": "//source/extensions/filters/network/thrift_proxy/filters/ratelimit:config", # # Tracers # - "envoy.tracers.dynamic_ot": "//source/extensions/tracers/dynamic_ot:config", - "envoy.tracers.lightstep": "//source/extensions/tracers/lightstep:config", - "envoy.tracers.datadog": "//source/extensions/tracers/datadog:config", - "envoy.tracers.zipkin": "//source/extensions/tracers/zipkin:config", - "envoy.tracers.opencensus": "//source/extensions/tracers/opencensus:config", + # These first three require opentracing to build on windows, but it is + # a foreign_cc build based on a linux autotools build schema. + # "envoy.tracers.dynamic_ot": "//source/extensions/tracers/dynamic_ot:config", + # "envoy.tracers.lightstep": "//source/extensions/tracers/lightstep:config", + # "envoy.tracers.datadog": "//source/extensions/tracers/datadog:config", + "envoy.tracers.zipkin": "//source/extensions/tracers/zipkin:config", + # The opencensus tool needs windows porting (unistd.h isn't c++) + # "envoy.tracers.opencensus": "//source/extensions/tracers/opencensus:config", # WiP - "envoy.tracers.xray": "//source/extensions/tracers/xray:config", + "envoy.tracers.xray": "//source/extensions/tracers/xray:config", # # Transport sockets # - - "envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config", - "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", - "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", + "envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config", + "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", + "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", # # Retry host predicates # - - "envoy.retry_host_predicates.previous_hosts": "//source/extensions/retry/host/previous_hosts:config", - "envoy.retry_host_predicates.omit_canary_hosts": "//source/extensions/retry/host/omit_canary_hosts:config", - "envoy.retry_host_predicates.omit_host_metadata": "//source/extensions/retry/host/omit_host_metadata:config", + "envoy.retry_host_predicates.previous_hosts": "//source/extensions/retry/host/previous_hosts:config", + "envoy.retry_host_predicates.omit_canary_hosts": "//source/extensions/retry/host/omit_canary_hosts:config", + "envoy.retry_host_predicates.omit_host_metadata": "//source/extensions/retry/host/omit_host_metadata:config", # # Retry priorities # - - "envoy.retry_priorities.previous_priorities": "//source/extensions/retry/priority/previous_priorities:config", + "envoy.retry_priorities.previous_priorities": "//source/extensions/retry/priority/previous_priorities:config", # # CacheFilter plugins # + "envoy.filters.http.cache.simple_http_cache": "//source/extensions/filters/http/cache/simple_http_cache:simple_http_cache_lib", - "envoy.filters.http.cache.simple_http_cache": "//source/extensions/filters/http/cache/simple_http_cache:simple_http_cache_lib", + # + # Internal redirect predicates + # + "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", + "envoy.internal_redirect_predicates.whitelisted_routes": "//source/extensions/internal_redirect/whitelisted_routes:config", } +>>>>>>> cdc5e08ee... Implemented two initial internal redirect predicates: previous_routes diff --git a/source/extensions/internal_redirect/BUILD b/source/extensions/internal_redirect/BUILD new file mode 100644 index 000000000000..6156949edef6 --- /dev/null +++ b/source/extensions/internal_redirect/BUILD @@ -0,0 +1,17 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "well_known_names", + hdrs = ["well_known_names.h"], + deps = [ + "//source/common/singleton:const_singleton", + ], +) diff --git a/source/extensions/internal_redirect/previous_routes/BUILD b/source/extensions/internal_redirect/previous_routes/BUILD new file mode 100644 index 000000000000..a3b5005ad5eb --- /dev/null +++ b/source/extensions/internal_redirect/previous_routes/BUILD @@ -0,0 +1,34 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "previous_routes_lib", + srcs = ["previous_routes.cc"], + hdrs = ["previous_routes.h"], + deps = [ + "//include/envoy/router:internal_redirect_interface", + "//include/envoy/stream_info:filter_state_interface", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + security_posture = "robust_to_untrusted_downstream_and_upstream", + deps = [ + ":previous_routes_lib", + "//include/envoy/registry", + "//include/envoy/router:internal_redirect_interface", + "//source/extensions/internal_redirect:well_known_names", + "@envoy_api//envoy/extensions/internal_redirect/previous_routes/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/internal_redirect/previous_routes/config.cc b/source/extensions/internal_redirect/previous_routes/config.cc new file mode 100644 index 000000000000..d5d4b67c491e --- /dev/null +++ b/source/extensions/internal_redirect/previous_routes/config.cc @@ -0,0 +1,14 @@ +#include "extensions/internal_redirect/previous_routes/config.h" + +#include "envoy/registry/registry.h" +#include "envoy/router/internal_redirect.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +REGISTER_FACTORY(PreviousRoutesPredicateFactory, Router::InternalRedirectPredicateFactory); + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/previous_routes/config.h b/source/extensions/internal_redirect/previous_routes/config.h new file mode 100644 index 000000000000..21ccb3c1646b --- /dev/null +++ b/source/extensions/internal_redirect/previous_routes/config.h @@ -0,0 +1,34 @@ +#pragma once + +#include "envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.pb.validate.h" +#include "envoy/router/internal_redirect.h" + +#include "extensions/internal_redirect/previous_routes/previous_routes.h" +#include "extensions/internal_redirect/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +class PreviousRoutesPredicateFactory : public Router::InternalRedirectPredicateFactory { +public: + Router::InternalRedirectPredicateSharedPtr + createInternalRedirectPredicate(const Protobuf::Message&, + absl::string_view current_route_name) override { + return std::make_shared(current_route_name); + } + + std::string name() const override { + return InternalRedirectPredicateValues::get().PreviousRoutesPredicate; + } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique< + envoy::extensions::internal_redirect::previous_routes::v3::PreviousRoutesConfig>(); + } +}; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.cc b/source/extensions/internal_redirect/previous_routes/previous_routes.cc new file mode 100644 index 000000000000..a99cf13d40cc --- /dev/null +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.cc @@ -0,0 +1,52 @@ +#include "extensions/internal_redirect/previous_routes/previous_routes.h" + +#include "envoy/router/internal_redirect.h" +#include "envoy/stream_info/filter_state.h" + +#include "absl/container/flat_hash_set.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +namespace { + +constexpr absl::string_view PreviousRoutesPredicateStateNamePrefix = + "envoy.internal_redirect.previous_routes_predicate_state"; + +class PreviousRoutesPredicateState : public StreamInfo::FilterState::Object { +public: + PreviousRoutesPredicateState() = default; + // Disallow copy so that we don't accidentally take a copy of the state + // through FilterState::getDataMutable, which will cause confusing bug that + // states are not updated in the original copy. + PreviousRoutesPredicateState(const PreviousRoutesPredicateState&) = delete; + PreviousRoutesPredicateState& operator=(const PreviousRoutesPredicateState&) = delete; + + bool insertRouteIfUnexist(absl::string_view route) { + return previous_routes_.insert(std::string(route)).second; + } + +private: + absl::flat_hash_set previous_routes_; +}; + +} // namespace + +bool PreviousRoutesPredicate::acceptTargetRoute(StreamInfo::FilterState& filter_state, + absl::string_view route_name) { + auto filter_state_name = + absl::StrCat(PreviousRoutesPredicateStateNamePrefix, ".", current_route_name_); + if (!filter_state.hasData(filter_state_name)) { + filter_state.setData(filter_state_name, std::make_unique(), + StreamInfo::FilterState::StateType::Mutable, + StreamInfo::FilterState::LifeSpan::DownstreamRequest); + } + auto& predicate_state = + filter_state.getDataMutable(filter_state_name); + return predicate_state.insertRouteIfUnexist(route_name); +} + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.h b/source/extensions/internal_redirect/previous_routes/previous_routes.h new file mode 100644 index 000000000000..293c3be6d5e6 --- /dev/null +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.h @@ -0,0 +1,26 @@ +#pragma once + +#include "envoy/router/internal_redirect.h" +#include "envoy/stream_info/filter_state.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +class PreviousRoutesPredicate : public Router::InternalRedirectPredicate { +public: + explicit PreviousRoutesPredicate(absl::string_view current_route_name) + : current_route_name_(current_route_name) {} + + bool acceptTargetRoute(StreamInfo::FilterState& filter_state, + absl::string_view route_name) override; + +private: + const std::string current_route_name_; +}; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/well_known_names.h b/source/extensions/internal_redirect/well_known_names.h new file mode 100644 index 000000000000..009eaf766fe8 --- /dev/null +++ b/source/extensions/internal_redirect/well_known_names.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "common/singleton/const_singleton.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +/** + * Well-known internal redirect predidate names. + */ +class InternalRedirectPredicatesNameValues { +public: + const std::string PreviousRoutesPredicate = "envoy.internal_redirect_predicates.previous_routes"; + const std::string WhitelistedRoutesPredicate = + "envoy.internal_redirect_predicates.whitelisted_routes"; +}; + +using InternalRedirectPredicateValues = ConstSingleton; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/whitelisted_routes/BUILD b/source/extensions/internal_redirect/whitelisted_routes/BUILD new file mode 100644 index 000000000000..6ce7f251edae --- /dev/null +++ b/source/extensions/internal_redirect/whitelisted_routes/BUILD @@ -0,0 +1,34 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "whitelisted_routes_lib", + hdrs = ["whitelisted_routes.h"], + deps = [ + "//include/envoy/router:internal_redirect_interface", + "//include/envoy/stream_info:filter_state_interface", + "@envoy_api//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg_cc_proto", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + security_posture = "robust_to_untrusted_downstream_and_upstream", + deps = [ + ":whitelisted_routes_lib", + "//include/envoy/registry", + "//include/envoy/router:internal_redirect_interface", + "//source/extensions/internal_redirect:well_known_names", + "@envoy_api//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/internal_redirect/whitelisted_routes/config.cc b/source/extensions/internal_redirect/whitelisted_routes/config.cc new file mode 100644 index 000000000000..5906f48d3e89 --- /dev/null +++ b/source/extensions/internal_redirect/whitelisted_routes/config.cc @@ -0,0 +1,14 @@ +#include "extensions/internal_redirect/whitelisted_routes/config.h" + +#include "envoy/registry/registry.h" +#include "envoy/router/internal_redirect.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +REGISTER_FACTORY(WhitelistedRoutesPredicateFactory, Router::InternalRedirectPredicateFactory); + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/whitelisted_routes/config.h b/source/extensions/internal_redirect/whitelisted_routes/config.h new file mode 100644 index 000000000000..5e3952035c7d --- /dev/null +++ b/source/extensions/internal_redirect/whitelisted_routes/config.h @@ -0,0 +1,41 @@ +#pragma once + +#include "envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.pb.validate.h" +#include "envoy/router/internal_redirect.h" + +#include "common/protobuf/message_validator_impl.h" +#include "common/protobuf/utility.h" +#include "extensions/internal_redirect/well_known_names.h" +#include "extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +class WhitelistedRoutesPredicateFactory : public Router::InternalRedirectPredicateFactory { +public: + Router::InternalRedirectPredicateSharedPtr + createInternalRedirectPredicate(const Protobuf::Message& config, + absl::string_view current_route_name) override { + auto whitelisted_routes_config = + MessageUtil::downcastAndValidate( + config, ProtobufMessage::getStrictValidationVisitor()); + return std::make_shared(current_route_name, + whitelisted_routes_config); + } + + std::string name() const override { + return InternalRedirectPredicateValues::get().WhitelistedRoutesPredicate; + } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique< + envoy::extensions::internal_redirect::whitelisted_routes::v3::WhitelistedRoutesConfig>(); + } +}; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h b/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h new file mode 100644 index 000000000000..2c5e0b1bec27 --- /dev/null +++ b/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h @@ -0,0 +1,33 @@ +#pragma once + +#include "envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.pb.h" +#include "envoy/router/internal_redirect.h" +#include "envoy/stream_info/filter_state.h" + +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +class WhitelistedRoutesPredicate : public Router::InternalRedirectPredicate { +public: + WhitelistedRoutesPredicate( + absl::string_view, + const envoy::extensions::internal_redirect::whitelisted_routes::v3::WhitelistedRoutesConfig& + config) + : whitelisted_routes_(config.whitelisted_route_names().begin(), + config.whitelisted_route_names().end()) {} + + bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name) { + return whitelisted_routes_.contains(route_name); + } + +private: + const absl::flat_hash_set whitelisted_routes_; +}; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/test/common/http/async_client_impl_test.cc b/test/common/http/async_client_impl_test.cc index fc46f8a5ef43..c7e6b15912df 100644 --- a/test/common/http/async_client_impl_test.cc +++ b/test/common/http/async_client_impl_test.cc @@ -1394,8 +1394,7 @@ TEST_F(AsyncClientImplUnitTest, RouteImplInitTest) { route_impl_.routeEntry()->typedMetadata().get("bar")); EXPECT_EQ(nullptr, route_impl_.routeEntry()->perFilterConfig("bar")); EXPECT_TRUE(route_impl_.routeEntry()->upgradeMap().empty()); - EXPECT_EQ(Router::InternalRedirectAction::PassThrough, - route_impl_.routeEntry()->internalRedirectAction()); + EXPECT_EQ(false, route_impl_.routeEntry()->internalRedirectPolicy().enabled()); EXPECT_TRUE(route_impl_.routeEntry()->shadowPolicies().empty()); EXPECT_TRUE(route_impl_.routeEntry()->virtualHost().rateLimitPolicy().empty()); EXPECT_EQ(nullptr, route_impl_.routeEntry()->virtualHost().corsPolicy()); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index e282f6b9ae8c..a23cdd93811a 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -2627,8 +2627,7 @@ TEST_F(RouteMatcherTest, ClusterHeader) { route->routeEntry()->maxGrpcTimeout(); route->routeEntry()->grpcTimeoutOffset(); route->routeEntry()->upgradeMap(); - route->routeEntry()->internalRedirectAction(); - route->routeEntry()->maxInternalRedirects(); + route->routeEntry()->internalRedirectPolicy(); } } @@ -6750,6 +6749,43 @@ name: RetriableStatusCodes EXPECT_NE(predicates1, predicates2); } +TEST_F(RouteConfigurationV2, DefaultInternalRedirctPolicyIsSensible) { + const std::string InternalRedirectEnabled = R"EOF( +name: InternalRedirectEnabled +virtual_hosts: + - name: regex + domains: [idle.lyft.com] + routes: + - match: + safe_regex: + google_re2: {} + regex: "/regex" + route: + cluster: some-cluster + internal_redirect_policy: + internal_redirect_action: HANDLE_INTERNAL_REDIRECT + + )EOF"; + + TestConfigImpl config(parseRouteConfigurationFromV2Yaml(InternalRedirectEnabled), + factory_context_, true); + Http::TestRequestHeaderMapImpl headers = + genRedirectHeaders("idle.lyft.com", "/regex", true, false); + const auto& internal_redirect_policy = + config.route(headers, 0)->routeEntry()->internalRedirectPolicy(); + EXPECT_TRUE(internal_redirect_policy.enabled()); + EXPECT_TRUE(internal_redirect_policy.shouldRedirectForCode(static_cast(302))); + EXPECT_FALSE(internal_redirect_policy.shouldRedirectForCode(static_cast(200))); + EXPECT_EQ(1, internal_redirect_policy.maxInternalRedirects()); + EXPECT_TRUE(internal_redirect_policy.predicates().empty()); + EXPECT_TRUE(internal_redirect_policy.isDownstreamAndRedirectTargetSchemePairAllowed(true, true)); + EXPECT_TRUE(internal_redirect_policy.isDownstreamAndRedirectTargetSchemePairAllowed(true, false)); + EXPECT_TRUE( + internal_redirect_policy.isDownstreamAndRedirectTargetSchemePairAllowed(false, false)); + EXPECT_FALSE( + internal_redirect_policy.isDownstreamAndRedirectTargetSchemePairAllowed(false, true)); +} + class PerFilterConfigsTest : public testing::Test, public ConfigImplTestBase { public: PerFilterConfigsTest() diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 1a0b47b730c5..b3876553e745 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -288,16 +288,17 @@ class RouterTestBase : public testing::Test { router_.decodeHeaders(default_request_headers_, end_stream); } - void enableRedirects() { - ON_CALL(callbacks_.route_->route_entry_, internalRedirectAction()) - .WillByDefault(Return(InternalRedirectAction::Handle)); - ON_CALL(callbacks_, connection()).WillByDefault(Return(&connection_)); - setMaxInternalRedirects(1); - } - - void setMaxInternalRedirects(uint32_t max_internal_redirects) { - ON_CALL(callbacks_.route_->route_entry_, maxInternalRedirects()) + void enableRedirects(uint32_t max_internal_redirects = 1) { + ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, enabled()) + .WillByDefault(Return(true)); + ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, shouldRedirectForCode(_)) + .WillByDefault(Return(true)); + ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, maxInternalRedirects()) .WillByDefault(Return(max_internal_redirects)); + ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, + isDownstreamAndRedirectTargetSchemePairAllowed(_, _)) + .WillByDefault(Return(true)); + ON_CALL(callbacks_, connection()).WillByDefault(Return(&connection_)); } void setNumPreviousRedirect(uint32_t num_previous_redirects) { @@ -4279,11 +4280,12 @@ TEST_F(RouterTest, RetryRespectsRetryHostPredicate) { } TEST_F(RouterTest, InternalRedirectRejectedWhenReachingMaxInternalRedirect) { - enableRedirects(); - setMaxInternalRedirects(3); + enableRedirects(3); setNumPreviousRedirect(3); sendRequest(); + EXPECT_CALL(callbacks_, recreateStream()).Times(0); + response_decoder_->decodeHeaders(std::move(redirect_headers_), false); Buffer::OwnedImpl data("1234567890"); @@ -4298,6 +4300,9 @@ TEST_F(RouterTest, InternalRedirectRejectedWithEmptyLocation) { sendRequest(); redirect_headers_->setLocation(""); + + EXPECT_CALL(callbacks_, recreateStream()).Times(0); + response_decoder_->decodeHeaders(std::move(redirect_headers_), false); Buffer::OwnedImpl data("1234567890"); @@ -4312,6 +4317,9 @@ TEST_F(RouterTest, InternalRedirectRejectedWithInvalidLocation) { sendRequest(); redirect_headers_->setLocation("h"); + + EXPECT_CALL(callbacks_, recreateStream()).Times(0); + response_decoder_->decodeHeaders(std::move(redirect_headers_), false); Buffer::OwnedImpl data("1234567890"); @@ -4326,6 +4334,8 @@ TEST_F(RouterTest, InternalRedirectRejectedWithoutCompleteRequest) { sendRequest(false); + EXPECT_CALL(callbacks_, recreateStream()).Times(0); + response_decoder_->decodeHeaders(std::move(redirect_headers_), false); Buffer::OwnedImpl data("1234567890"); @@ -4341,6 +4351,9 @@ TEST_F(RouterTest, InternalRedirectRejectedWithoutLocation) { sendRequest(); redirect_headers_->removeLocation(); + + EXPECT_CALL(callbacks_, recreateStream()).Times(0); + response_decoder_->decodeHeaders(std::move(redirect_headers_), false); Buffer::OwnedImpl data("1234567890"); response_decoder_->decodeData(data, true); @@ -4354,7 +4367,10 @@ TEST_F(RouterTest, InternalRedirectRejectedWithBody) { sendRequest(); - EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + Buffer::InstancePtr body_data(new Buffer::OwnedImpl("random_fake_data")); + EXPECT_CALL(callbacks_, decodingBuffer()).WillOnce(Return(body_data.get())); + EXPECT_CALL(callbacks_, recreateStream()).Times(0); + response_decoder_->decodeHeaders(std::move(redirect_headers_), false); Buffer::OwnedImpl data("1234567890"); response_decoder_->decodeData(data, true); @@ -4363,12 +4379,40 @@ TEST_F(RouterTest, InternalRedirectRejectedWithBody) { .value()); } -TEST_F(RouterTest, InternalRedirectRejectedWithCrossSchemeRedirect) { +TEST_F(RouterTest, InternalRedirectRejectedByDownstreamAndTargetSchemePair) { + enableRedirects(); + + sendRequest(); + + redirect_headers_->setLocation("https://www.foo.com"); + + EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, + isDownstreamAndRedirectTargetSchemePairAllowed(false, true)) + .WillOnce(Return(false)); + EXPECT_CALL(callbacks_, recreateStream()).Times(0); + + response_decoder_->decodeHeaders(std::move(redirect_headers_), true); + EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ + .counter("upstream_internal_redirect_failed_total") + .value()); +} + +TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { enableRedirects(); sendRequest(); redirect_headers_->setLocation("https://www.foo.com"); + + auto mock_predicate = std::make_shared(); + + EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, predicates()) + .WillOnce(Return(std::vector({mock_predicate}))); + EXPECT_CALL(*mock_predicate, acceptTargetRoute(_, _)).WillOnce(Return(false)); + EXPECT_CALL(callbacks_, recreateStream()).Times(0); + response_decoder_->decodeHeaders(std::move(redirect_headers_), true); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_failed_total") @@ -4376,13 +4420,13 @@ TEST_F(RouterTest, InternalRedirectRejectedWithCrossSchemeRedirect) { } TEST_F(RouterTest, HttpInternalRedirectSucceeded) { - enableRedirects(); - setMaxInternalRedirects(3); + enableRedirects(3); setNumPreviousRedirect(2); default_request_headers_.setForwardedProto("http"); sendRequest(); EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); EXPECT_CALL(callbacks_, recreateStream()).Times(1).WillOnce(Return(true)); response_decoder_->decodeHeaders(std::move(redirect_headers_), false); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ @@ -4399,8 +4443,7 @@ TEST_F(RouterTest, HttpInternalRedirectSucceeded) { TEST_F(RouterTest, HttpsInternalRedirectSucceeded) { auto ssl_connection = std::make_shared(); - enableRedirects(); - setMaxInternalRedirects(3); + enableRedirects(3); setNumPreviousRedirect(1); sendRequest(); @@ -4408,6 +4451,7 @@ TEST_F(RouterTest, HttpsInternalRedirectSucceeded) { redirect_headers_->setLocation("https://www.foo.com"); EXPECT_CALL(connection_, ssl()).Times(1).WillOnce(Return(ssl_connection)); EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); EXPECT_CALL(callbacks_, recreateStream()).Times(1).WillOnce(Return(true)); response_decoder_->decodeHeaders(std::move(redirect_headers_), false); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ diff --git a/test/extensions/internal_redirect/previous_routes/BUILD b/test/extensions/internal_redirect/previous_routes/BUILD new file mode 100644 index 000000000000..8425dec9126c --- /dev/null +++ b/test/extensions/internal_redirect/previous_routes/BUILD @@ -0,0 +1,24 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_package", +) +load( + "//test/extensions:extensions_build_system.bzl", + "envoy_extension_cc_test", +) + +envoy_package() + +envoy_extension_cc_test( + name = "config_test", + srcs = ["config_test.cc"], + extension_name = "envoy.internal_redirect_predicates.previous_routes", + deps = [ + "//source/common/stream_info:filter_state_lib", + "//source/extensions/internal_redirect:well_known_names", + "//source/extensions/internal_redirect/previous_routes:config", + "@envoy_api//envoy/extensions/internal_redirect/previous_routes/v3:pkg_cc_proto", + ], +) diff --git a/test/extensions/internal_redirect/previous_routes/config_test.cc b/test/extensions/internal_redirect/previous_routes/config_test.cc new file mode 100644 index 000000000000..57cc95b6649c --- /dev/null +++ b/test/extensions/internal_redirect/previous_routes/config_test.cc @@ -0,0 +1,83 @@ +#include "envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.pb.h" +#include "envoy/registry/registry.h" +#include "envoy/router/internal_redirect.h" + +#include "common/stream_info/filter_state_impl.h" + +#include "extensions/internal_redirect/previous_routes/config.h" +#include "extensions/internal_redirect/well_known_names.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace testing; + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { +namespace { + +class ConfigTest : public testing::Test { +protected: + ConfigTest() : filter_state_(StreamInfo::FilterState::LifeSpan::FilterChain) { + factory_ = Registry::FactoryRegistry::getFactory( + InternalRedirectPredicateValues::get().PreviousRoutesPredicate); + config_ = factory_->createEmptyConfigProto(); + } + + StreamInfo::FilterStateImpl filter_state_; + Router::InternalRedirectPredicateFactory* factory_; + ProtobufTypes::MessagePtr config_; +}; + +TEST_F(ConfigTest, TargetIsOnlyTakenOnce) { + std::string current_route_name = "fake_current_route"; + // Create the predicate for the first time. It should remember nothing in the + // filter state, so it allows the redirect. + { + auto predicate = factory_->createInternalRedirectPredicate(*config_, current_route_name); + ASSERT(predicate); + + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_1")); + // New filter state data is created with route name. + EXPECT_TRUE(filter_state_.hasDataWithName( + "envoy.internal_redirect.previous_routes_predicate_state.fake_current_route")); + } + + // The second predicate should see the previously taken route. + { + auto predicate = factory_->createInternalRedirectPredicate(*config_, current_route_name); + ASSERT(predicate); + + EXPECT_FALSE(predicate->acceptTargetRoute(filter_state_, "route_1")); + } +} + +TEST_F(ConfigTest, RoutesAreIndependent) { + // Create the predicate on route_0. + { + auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_0"); + ASSERT(predicate); + + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_2")); + // New filter state data is created with route name. + EXPECT_TRUE(filter_state_.hasDataWithName( + "envoy.internal_redirect.previous_routes_predicate_state.route_0")); + } + + // The predicate created on route_1 should also allow a redirect to route_2 + { + auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_1"); + ASSERT(predicate); + + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_2")); + // New filter state data is created with route name. + EXPECT_TRUE(filter_state_.hasDataWithName( + "envoy.internal_redirect.previous_routes_predicate_state.route_1")); + } +} + +} // namespace +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/test/integration/BUILD b/test/integration/BUILD index ad714ee65167..768d944bc2f8 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -612,9 +612,12 @@ envoy_cc_test( deps = [ ":http_protocol_integration_lib", "//source/common/http:header_map_lib", + "//source/extensions/internal_redirect/previous_routes:config", + "//source/extensions/internal_redirect/whitelisted_routes:config", "//test/test_common:utility_lib", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg_cc_proto", ], ) diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index 14c4fa5da7e2..aee360282626 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -1,5 +1,6 @@ #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" +#include "envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.pb.h" #include "test/integration/http_protocol_integration.h" @@ -19,12 +20,14 @@ class RedirectIntegrationTest : public HttpProtocolIntegrationTest { config_helper_.addVirtualHost(pass_through); auto handle = config_helper_.createVirtualHost("handle.internal.redirect"); + handle.mutable_routes(0)->set_name("redirect"); handle.mutable_routes(0)->mutable_route()->set_internal_redirect_action( envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT); config_helper_.addVirtualHost(handle); auto handle_max_3_hop = config_helper_.createVirtualHost("handle.internal.redirect.max.three.hop"); + handle_max_3_hop.mutable_routes(0)->set_name("max_three_hop"); handle_max_3_hop.mutable_routes(0)->mutable_route()->set_internal_redirect_action( envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT); handle_max_3_hop.mutable_routes(0) @@ -220,6 +223,114 @@ TEST_P(RedirectIntegrationTest, InternalRedirectToDestinationWithBody) { ->value()); } +TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByPreviousRoutesPredicate) { + auto handle_prevent_repeated_target = + config_helper_.createVirtualHost("handle.internal.redirect.no.repeated.target"); + auto* internal_redirect_policy = handle_prevent_repeated_target.mutable_routes(0) + ->mutable_route() + ->mutable_internal_redirect_policy(); + internal_redirect_policy->set_internal_redirect_action( + envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); + internal_redirect_policy->add_predicates()->set_name( + "envoy.internal_redirect_predicates.previous_routes"); + internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); + config_helper_.addVirtualHost(handle_prevent_repeated_target); + + // Validate that header sanitization is only called once. + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { hcm.set_via("via_value"); }); + initialize(); + fake_upstreams_[0]->set_allow_unexpected_disconnects(true); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + default_request_headers_.setHost("handle.internal.redirect.no.repeated.target"); + IntegrationStreamDecoderPtr response = + codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + auto first_request = waitForNextStream(); + // Redirect to another route + redirect_response_.setLocation("http://handle.internal.redirect.max.three.hop/random/path"); + first_request->encodeHeaders(redirect_response_, true); + + auto second_request = waitForNextStream(); + // Redirect back to the original route. + redirect_response_.setLocation("http://handle.internal.redirect.no.repeated.target/another/path"); + second_request->encodeHeaders(redirect_response_, true); + + auto third_request = waitForNextStream(); + // Redirect to the same route as the first redirect. This should fail. + redirect_response_.setLocation("http://handle.internal.redirect.max.three.hop/yet/another/path"); + third_request->encodeHeaders(redirect_response_, true); + + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("302", response->headers().Status()->value().getStringView()); + EXPECT_EQ("http://handle.internal.redirect.max.three.hop/yet/another/path", + response->headers().Location()->value().getStringView()); + EXPECT_EQ(2, test_server_->counter("cluster.cluster_0.upstream_internal_redirect_succeeded_total") + ->value()); +} + +TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByWhitelistedRoutesPredicate) { + auto handle_whitelisted_redirect_route = + config_helper_.createVirtualHost("handle.internal.redirect.only.whitelisted.target"); + auto* internal_redirect_policy = handle_whitelisted_redirect_route.mutable_routes(0) + ->mutable_route() + ->mutable_internal_redirect_policy(); + internal_redirect_policy->set_internal_redirect_action( + envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); + + auto* whitelisted_routes_predicate = internal_redirect_policy->add_predicates(); + whitelisted_routes_predicate->set_name("envoy.internal_redirect_predicates.whitelisted_routes"); + envoy::extensions::internal_redirect::whitelisted_routes::v3::WhitelistedRoutesConfig + whitelisted_routes_config; + *whitelisted_routes_config.add_whitelisted_route_names() = "max_three_hop"; + whitelisted_routes_predicate->mutable_typed_config()->PackFrom(whitelisted_routes_config); + + internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); + + config_helper_.addVirtualHost(handle_whitelisted_redirect_route); + + // Validate that header sanitization is only called once. + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { hcm.set_via("via_value"); }); + initialize(); + fake_upstreams_[0]->set_allow_unexpected_disconnects(true); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + default_request_headers_.setHost("handle.internal.redirect.only.whitelisted.target"); + IntegrationStreamDecoderPtr response = + codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + auto first_request = waitForNextStream(); + // Redirect to another route + redirect_response_.setLocation("http://handle.internal.redirect.max.three.hop/random/path"); + first_request->encodeHeaders(redirect_response_, true); + + auto second_request = waitForNextStream(); + // Redirect back to the original route. + redirect_response_.setLocation( + "http://handle.internal.redirect.only.whitelisted.target/another/path"); + second_request->encodeHeaders(redirect_response_, true); + + auto third_request = waitForNextStream(); + // Redirect to the non-whitelisted route. This should fail. + redirect_response_.setLocation("http://handle.internal.redirect/yet/another/path"); + third_request->encodeHeaders(redirect_response_, true); + + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("302", response->headers().Status()->value().getStringView()); + EXPECT_EQ("http://handle.internal.redirect/yet/another/path", + response->headers().Location()->value().getStringView()); + EXPECT_EQ(2, test_server_->counter("cluster.cluster_0.upstream_internal_redirect_succeeded_total") + ->value()); +} + TEST_P(RedirectIntegrationTest, InvalidRedirect) { initialize(); diff --git a/test/mocks/router/mocks.cc b/test/mocks/router/mocks.cc index 5f4f8d487c1e..0e075f529fd2 100644 --- a/test/mocks/router/mocks.cc +++ b/test/mocks/router/mocks.cc @@ -22,6 +22,10 @@ TestRetryPolicy::TestRetryPolicy() { num_retries_ = 1; } TestRetryPolicy::~TestRetryPolicy() = default; +MockInternalRedirectPolicy::MockInternalRedirectPolicy() { + ON_CALL(*this, enabled()).WillByDefault(Return(false)); +} + MockRetryState::MockRetryState() = default; void MockRetryState::expectHeadersRetry() { @@ -85,6 +89,7 @@ MockRouteEntry::MockRouteEntry() { ON_CALL(*this, opaqueConfig()).WillByDefault(ReturnRef(opaque_config_)); ON_CALL(*this, rateLimitPolicy()).WillByDefault(ReturnRef(rate_limit_policy_)); ON_CALL(*this, retryPolicy()).WillByDefault(ReturnRef(retry_policy_)); + ON_CALL(*this, internalRedirectPolicy()).WillByDefault(ReturnRef(internal_redirect_policy_)); ON_CALL(*this, retryShadowBufferLimit()) .WillByDefault(Return(std::numeric_limits::max())); ON_CALL(*this, shadowPolicies()).WillByDefault(ReturnRef(shadow_policies_)); diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index 9ed7b8ead74b..b835901263ee 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -131,6 +131,22 @@ class TestRetryPolicy : public RetryPolicy { absl::optional max_interval_{}; }; +class MockInternalRedirectPolicy : public InternalRedirectPolicy { +public: + MockInternalRedirectPolicy(); + MOCK_METHOD(bool, enabled, (), (const)); + MOCK_METHOD(bool, shouldRedirectForCode, (const Http::Code& response_code), (const)); + MOCK_METHOD(std::vector, predicates, (), (const)); + MOCK_METHOD(uint32_t, maxInternalRedirects, (), (const)); + MOCK_METHOD(bool, isDownstreamAndRedirectTargetSchemePairAllowed, + (bool downstream_is_https, bool target_is_https), (const)); +}; + +class MockInternalRedirectPredicate : public InternalRedirectPredicate { +public: + MOCK_METHOD(bool, acceptTargetRoute, (StreamInfo::FilterState&, absl::string_view)); +}; + class MockRetryState : public RetryState { public: MockRetryState(); @@ -334,6 +350,7 @@ class MockRouteEntry : public RouteEntry { MOCK_METHOD(Upstream::ResourcePriority, priority, (), (const)); MOCK_METHOD(const RateLimitPolicy&, rateLimitPolicy, (), (const)); MOCK_METHOD(const RetryPolicy&, retryPolicy, (), (const)); + MOCK_METHOD(const InternalRedirectPolicy&, internalRedirectPolicy, (), (const)); MOCK_METHOD(uint32_t, retryShadowBufferLimit, (), (const)); MOCK_METHOD(const std::vector&, shadowPolicies, (), (const)); MOCK_METHOD(std::chrono::milliseconds, timeout, (), (const)); @@ -354,8 +371,6 @@ class MockRouteEntry : public RouteEntry { MOCK_METHOD(bool, includeAttemptCountInRequest, (), (const)); MOCK_METHOD(bool, includeAttemptCountInResponse, (), (const)); MOCK_METHOD(const UpgradeMap&, upgradeMap, (), (const)); - MOCK_METHOD(InternalRedirectAction, internalRedirectAction, (), (const)); - MOCK_METHOD(uint32_t, maxInternalRedirects, (), (const)); MOCK_METHOD(const std::string&, routeName, (), (const)); std::string cluster_name_{"fake_cluster"}; @@ -363,6 +378,7 @@ class MockRouteEntry : public RouteEntry { std::multimap opaque_config_; TestVirtualCluster virtual_cluster_; TestRetryPolicy retry_policy_; + testing::NiceMock internal_redirect_policy_; TestHedgePolicy hedge_policy_; testing::NiceMock rate_limit_policy_; std::vector shadow_policies_; From 15f1510021d81cb3b0cb13a22ab584419e827628 Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 17:19:23 -0400 Subject: [PATCH 02/46] Generate protobuf. Signed-off-by: pengg --- .../route/v4alpha/route_components.proto | 20 +++-- .../config/route/v3/route_components.proto | 90 ++++++++++++++++--- .../route/v4alpha/route_components.proto | 81 ++++++++++++++++- .../v3/previous_routes_config.proto | 4 +- .../v3/whitelisted_routes_config.proto | 2 +- source/extensions/extensions_build_config.bzl | 21 +---- .../previous_routes/previous_routes.cc | 2 +- 7 files changed, 175 insertions(+), 45 deletions(-) diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 1f9aa9da4eed..1d7158c8dd3f 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1589,9 +1589,11 @@ message InternalRedirectPolicy { // An internal redirect is handled, iff the number of previous internal redirects that a // downstream request has encountered is lower than this value, and - // :ref:`internal_redirect_action ` + // :ref:`internal_redirect_action + // ` // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT ` + // :ref:`HANDLE_INTERNAL_REDIRECT + // ` // In the case where a downstream request is bounced among multiple routes by internal redirect, // the first route that hits this threshold, or has // :ref:`internal_redirect_action @@ -1606,15 +1608,19 @@ message InternalRedirectPolicy { // Defines what upstream response codes can trigger the internal redirect // behavior. This is ignored unless - // :ref:`internal_redirect_action ` + // :ref:`internal_redirect_action + // ` // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT ` + // :ref:`HANDLE_INTERNAL_REDIRECT + // ` repeated uint32 redirect_response_codes = 3; - // Specifies a list of predicates that is queries when an upstream response is deemed to be able to trigger an internal redirect by all other criterias. - // Any predicate in the list can reject the redirect. + // Specifies a list of predicates that is queries when an upstream response is deemed to be able + // to trigger an internal redirect by all other criterias. Any predicate in the list can reject + // the redirect. repeated Predicate predicates = 4; - // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an internal redirect + // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an + // internal redirect. repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; } diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 616e76af302e..c6843d36dd17 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -532,7 +532,7 @@ message CorsPolicy { } } -// [#next-free-field: 34] +// [#next-free-field: 35] message RouteAction { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.route.RouteAction"; @@ -545,6 +545,7 @@ message RouteAction { } // Configures :ref:`internal redirect ` behavior. + // [#next-major-version: remove this definition - it's defined in the InternalRedirectPolicy message.] enum InternalRedirectAction { PASS_THROUGH_INTERNAL_REDIRECT = 0; HANDLE_INTERNAL_REDIRECT = 1; @@ -906,7 +907,7 @@ message RouteAction { // limits. By default, if the route configured rate limits, the virtual host // :ref:`rate_limits ` are not applied to the // request. - InternalRedirectAction internal_redirect_action = 26; + InternalRedirectPolicy internal_redirect_policy = 34; // Specifies a list of hash policies to use for ring hash load balancing. Each // hash policy is evaluated individually and the combined result is used to @@ -920,10 +921,10 @@ message RouteAction { // backend). If a hash policy has the "terminal" attribute set to true, and // there is already a hash generated, the hash is returned immediately, // ignoring the rest of the hash policy list. - google.protobuf.UInt32Value max_internal_redirects = 31; + InternalRedirectAction internal_redirect_action = 26 [deprecated = true]; // Indicates that the route has a CORS policy. - HedgePolicy hedge_policy = 27; + google.protobuf.UInt32Value max_internal_redirects = 31 [deprecated = true]; // If present, and the request is a gRPC request, use the // `grpc-timeout header `_, @@ -944,18 +945,20 @@ message RouteAction { // :ref:`config_http_filters_router_x-envoy-upstream-rq-timeout-ms`, // :ref:`config_http_filters_router_x-envoy-upstream-rq-per-try-timeout-ms`, and the // :ref:`retry overview `. + HedgePolicy hedge_policy = 27; + + // If present, Envoy will adjust the timeout provided by the `grpc-timeout` header by subtracting + // the provided duration from the header. This is useful in allowing Envoy to set its global + // timeout to be less than that of the deadline imposed by the calling client, which makes it more + // likely that Envoy will handle the timeout instead of having the call canceled by the client. + // The offset will only be applied if the provided grpc_timeout is greater than the offset. This + // ensures that the offset will only ever decrease the timeout and never set it to 0 (meaning + // infinity). RequestMirrorPolicy hidden_envoy_deprecated_request_mirror_policy = 10 [deprecated = true]; oneof cluster_specifier { option (validate.required) = true; - // If present, Envoy will adjust the timeout provided by the `grpc-timeout` header by subtracting - // the provided duration from the header. This is useful in allowing Envoy to set its global - // timeout to be less than that of the deadline imposed by the calling client, which makes it more - // likely that Envoy will handle the timeout instead of having the call canceled by the client. - // The offset will only be applied if the provided grpc_timeout is greater than the offset. This - // ensures that the offset will only ever decrease the timeout and never set it to 0 (meaning - // infinity). string cluster = 1 [(validate.rules).string = {min_bytes: 1}]; string cluster_header = 2 @@ -1582,3 +1585,68 @@ message QueryParameterMatcher { bool present_match = 6; } } + +// HTTP Internal Redirect :ref:`architecture overview `. +// [#next-free-field: 6] +message InternalRedirectPolicy { + // Configures :ref:`internal redirect ` behavior. + enum InternalRedirectAction { + PASS_THROUGH_INTERNAL_REDIRECT = 0; + HANDLE_INTERNAL_REDIRECT = 1; + } + + // HTTP scheme conbinations that are allowed to be handled as internal + // redirect. Downstream scheme comes first and redirect target URL scheme + // follows. + enum DownstreamAndRedirectTargetSchemePair { + HTTP_HTTP = 0; + HTTP_HTTPS = 1; + HTTPS_HTTP = 2; + HTTPS_HTTPS = 3; + } + + message Predicate { + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + google.protobuf.Any typed_config = 2; + } + + InternalRedirectAction internal_redirect_action = 1; + + // An internal redirect is handled, iff the number of previous internal redirects that a + // downstream request has encountered is lower than this value, and + // :ref:`internal_redirect_action + // ` + // is set to + // :ref:`HANDLE_INTERNAL_REDIRECT + // ` + // In the case where a downstream request is bounced among multiple routes by internal redirect, + // the first route that hits this threshold, or has + // :ref:`internal_redirect_action + // ` + // set to + // :ref:`PASS_THROUGH_INTERNAL_REDIRECT + // ` + // will pass the redirect back to downstream. + // + // If not specified, at most one redirect will be followed. + google.protobuf.UInt32Value max_internal_redirects = 2; + + // Defines what upstream response codes can trigger the internal redirect + // behavior. This is ignored unless + // :ref:`internal_redirect_action + // ` + // is set to + // :ref:`HANDLE_INTERNAL_REDIRECT + // ` + repeated uint32 redirect_response_codes = 3; + + // Specifies a list of predicates that is queries when an upstream response is deemed to be able + // to trigger an internal redirect by all other criterias. Any predicate in the list can reject + // the redirect. + repeated Predicate predicates = 4; + + // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an + // internal redirect. + repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; +} diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index e813b632edb0..f48e16f2937f 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -521,7 +521,7 @@ message CorsPolicy { core.v4alpha.RuntimeFractionalPercent shadow_enabled = 10; } -// [#next-free-field: 34] +// [#next-free-field: 35] message RouteAction { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.RouteAction"; @@ -534,6 +534,7 @@ message RouteAction { } // Configures :ref:`internal redirect ` behavior. + // [#next-major-version: remove this definition - it's defined in the InternalRedirectPolicy message.] enum InternalRedirectAction { PASS_THROUGH_INTERNAL_REDIRECT = 0; HANDLE_INTERNAL_REDIRECT = 1; @@ -958,7 +959,9 @@ message RouteAction { repeated UpgradeConfig upgrade_configs = 25; - InternalRedirectAction internal_redirect_action = 26; + InternalRedirectPolicy internal_redirect_policy = 34; + + InternalRedirectAction hidden_envoy_deprecated_internal_redirect_action = 26 [deprecated = true]; // An internal redirect is handled, iff the number of previous internal redirects that a // downstream request has encountered is lower than this value, and @@ -974,7 +977,8 @@ message RouteAction { // will pass the redirect back to downstream. // // If not specified, at most one redirect will be followed. - google.protobuf.UInt32Value max_internal_redirects = 31; + google.protobuf.UInt32Value hidden_envoy_deprecated_max_internal_redirects = 31 + [deprecated = true]; // Indicates that the route has a hedge policy. Note that if this is set, // it'll take precedence over the virtual host level hedge policy entirely @@ -1568,3 +1572,74 @@ message QueryParameterMatcher { bool present_match = 6; } } + +// HTTP Internal Redirect :ref:`architecture overview `. +// [#next-free-field: 6] +message InternalRedirectPolicy { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.route.v3.InternalRedirectPolicy"; + + // Configures :ref:`internal redirect ` behavior. + enum InternalRedirectAction { + PASS_THROUGH_INTERNAL_REDIRECT = 0; + HANDLE_INTERNAL_REDIRECT = 1; + } + + // HTTP scheme conbinations that are allowed to be handled as internal + // redirect. Downstream scheme comes first and redirect target URL scheme + // follows. + enum DownstreamAndRedirectTargetSchemePair { + HTTP_HTTP = 0; + HTTP_HTTPS = 1; + HTTPS_HTTP = 2; + HTTPS_HTTPS = 3; + } + + message Predicate { + option (udpa.annotations.versioning).previous_message_type = + "envoy.config.route.v3.InternalRedirectPolicy.Predicate"; + + string name = 1 [(validate.rules).string = {min_bytes: 1}]; + + google.protobuf.Any typed_config = 2; + } + + InternalRedirectAction internal_redirect_action = 1; + + // An internal redirect is handled, iff the number of previous internal redirects that a + // downstream request has encountered is lower than this value, and + // :ref:`internal_redirect_action + // ` + // is set to + // :ref:`HANDLE_INTERNAL_REDIRECT + // ` + // In the case where a downstream request is bounced among multiple routes by internal redirect, + // the first route that hits this threshold, or has + // :ref:`internal_redirect_action + // ` + // set to + // :ref:`PASS_THROUGH_INTERNAL_REDIRECT + // ` + // will pass the redirect back to downstream. + // + // If not specified, at most one redirect will be followed. + google.protobuf.UInt32Value max_internal_redirects = 2; + + // Defines what upstream response codes can trigger the internal redirect + // behavior. This is ignored unless + // :ref:`internal_redirect_action + // ` + // is set to + // :ref:`HANDLE_INTERNAL_REDIRECT + // ` + repeated uint32 redirect_response_codes = 3; + + // Specifies a list of predicates that is queries when an upstream response is deemed to be able + // to trigger an internal redirect by all other criterias. Any predicate in the list can reject + // the redirect. + repeated Predicate predicates = 4; + + // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an + // internal redirect. + repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; +} diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto index 4081ca346141..2b241fdfe56e 100644 --- a/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto +++ b/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto @@ -12,8 +12,8 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Previous routes internal redirect predicate] -// An internal redirect predicat that rejects redirect targets that are pointing -// to a route that has been visited in the same downstream request. +// An internal redirect predicate that rejects redirect targets that are pointing +// to a route that has been follwed from the current route. // [#extension: envoy.internal_redirect_predicates.previous_routes] message PreviousRoutesConfig { } diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto index 63eafe17f274..cfe9bdbe8bc5 100644 --- a/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto +++ b/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto @@ -12,7 +12,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Whitelisted routes internal redirect predicate] -// An internal redirect predicat that accepts only whitelisted target routes. +// An internal redirect predicate that accepts only whitelisted target routes. // [#extension: envoy.internal_redirect_predicates.whitelisted_routes] message WhitelistedRoutesConfig { repeated string whitelisted_route_names = 1; diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index c1705ab4d381..a155e44586e8 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -81,25 +81,11 @@ EXTENSIONS = { "envoy.filters.network.ext_authz": "//source/extensions/filters/network/ext_authz:config", "envoy.filters.network.http_connection_manager": "//source/extensions/filters/network/http_connection_manager:config", # WiP -<<<<<<< HEAD - "envoy.filters.network.kafka_broker": "//source/extensions/filters/network/kafka:kafka_broker_config_lib", - "envoy.filters.network.local_ratelimit": "//source/extensions/filters/network/local_ratelimit:config", - "envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config", - "envoy.filters.network.mysql_proxy": "//source/extensions/filters/network/mysql_proxy:config", - "envoy.filters.network.postgres_proxy": "//source/extensions/filters/network/postgres_proxy:config", - "envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config", - "envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config", - "envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config", - "envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config", - "envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config", - "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", - "envoy.filters.network.sni_dynamic_forward_proxy": "//source/extensions/filters/network/sni_dynamic_forward_proxy:config", - "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", -======= "envoy.filters.network.kafka_broker": "//source/extensions/filters/network/kafka:kafka_broker_config_lib", "envoy.filters.network.local_ratelimit": "//source/extensions/filters/network/local_ratelimit:config", "envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config", "envoy.filters.network.mysql_proxy": "//source/extensions/filters/network/mysql_proxy:config", + "envoy.filters.network.postgres_proxy": "//source/extensions/filters/network/postgres_proxy:config", "envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config", "envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config", "envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config", @@ -108,8 +94,6 @@ EXTENSIONS = { "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", "envoy.filters.network.sni_dynamic_forward_proxy": "//source/extensions/filters/network/sni_dynamic_forward_proxy:config", "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", ->>>>>>> cdc5e08ee... Implemented two initial internal redirect predicates: previous_routes - # # UDP filters # @@ -177,8 +161,6 @@ EXTENSIONS = { "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", "envoy.internal_redirect_predicates.whitelisted_routes": "//source/extensions/internal_redirect/whitelisted_routes:config", } -<<<<<<< HEAD -======= WINDOWS_EXTENSIONS = { # @@ -346,4 +328,3 @@ WINDOWS_EXTENSIONS = { "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", "envoy.internal_redirect_predicates.whitelisted_routes": "//source/extensions/internal_redirect/whitelisted_routes:config", } ->>>>>>> cdc5e08ee... Implemented two initial internal redirect predicates: previous_routes diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.cc b/source/extensions/internal_redirect/previous_routes/previous_routes.cc index a99cf13d40cc..cac79ce3c853 100644 --- a/source/extensions/internal_redirect/previous_routes/previous_routes.cc +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.cc @@ -40,7 +40,7 @@ bool PreviousRoutesPredicate::acceptTargetRoute(StreamInfo::FilterState& filter_ if (!filter_state.hasData(filter_state_name)) { filter_state.setData(filter_state_name, std::make_unique(), StreamInfo::FilterState::StateType::Mutable, - StreamInfo::FilterState::LifeSpan::DownstreamRequest); + StreamInfo::FilterState::LifeSpan::Request); } auto& predicate_state = filter_state.getDataMutable(filter_state_name); From 81debc8c2e169ae540b53739f29ba2821cd846fd Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 17:45:34 -0400 Subject: [PATCH 03/46] Fix typos. Signed-off-by: pengg --- .../config/route/v3/route_components.proto | 9 +- .../v3/previous_routes_config.proto | 2 +- docs/root/version_history/current.rst | 4 +- source/extensions/extensions_build_config.bzl | 347 +++++------------- 4 files changed, 104 insertions(+), 258 deletions(-) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 21066bb5e5df..67366f78173e 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1614,8 +1614,7 @@ message InternalRedirectPolicy { // If not specified, at most one redirect will be followed. google.protobuf.UInt32Value max_internal_redirects = 2; - // Defines what upstream response codes can trigger the internal redirect - // behavior. This is ignored unless + // Defines what upstream response codes are allowed to trigger internal redirect. This is ignored unless // :ref:`internal_redirect_action // ` // is set to @@ -1623,12 +1622,12 @@ message InternalRedirectPolicy { // ` repeated uint32 redirect_response_codes = 3; - // Specifies a list of predicates that is queries when an upstream response is deemed to be able + // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criterias. Any predicate in the list can reject - // the redirect. + // the redirect, causing the response to be proxied to downstream. repeated Predicate predicates = 4; - // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an + // Specifies what combinations of downstream and redirect target scheme are allowed to trigger an // internal redirect. repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; } diff --git a/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto b/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto index 2b241fdfe56e..6cc5fba871ea 100644 --- a/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto +++ b/api/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto @@ -13,7 +13,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Previous routes internal redirect predicate] // An internal redirect predicate that rejects redirect targets that are pointing -// to a route that has been follwed from the current route. +// to a route that has been followed by a previous redirect from the current route. // [#extension: envoy.internal_redirect_predicates.previous_routes] message PreviousRoutesConfig { } diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index 95cb24132821..3ab16d68d001 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -24,14 +24,12 @@ Changes tracing is not forced. * router: allow retries of streaming or incomplete requests. This removes stat `rq_retry_skipped_request_not_complete`. * router: allow retries by default when upstream responds with :ref:`x-envoy-overloaded `. -* tracing: tracing configuration has been made fully dynamic and every HTTP connection manager - can now have a separate :ref:`tracing provider `. -* upstream: fixed a bug where Envoy would panic when receiving a GRPC SERVICE_UNKNOWN status on the health check. * router: internal redirect configs are moved to the :ref`internal_redirect_policy ` field, which defines more fine grained control of the internal redirect behavior. * tracing: tracing configuration has been made fully dynamic and every HTTP connection manager can now have a separate :ref:`tracing provider `. +* upstream: fixed a bug where Envoy would panic when receiving a GRPC SERVICE_UNKNOWN status on the health check. Deprecated ---------- diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index a155e44586e8..453b7d814009 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -3,326 +3,175 @@ EXTENSIONS = { # # Access loggers # - "envoy.access_loggers.file": "//source/extensions/access_loggers/file:config", - "envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config", - "envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config", - # - # Clusters - # - "envoy.clusters.aggregate": "//source/extensions/clusters/aggregate:cluster", - "envoy.clusters.dynamic_forward_proxy": "//source/extensions/clusters/dynamic_forward_proxy:cluster", - "envoy.clusters.redis": "//source/extensions/clusters/redis:redis_cluster", - - # - # gRPC Credentials Plugins - # - "envoy.grpc_credentials.file_based_metadata": "//source/extensions/grpc_credentials/file_based_metadata:config", - "envoy.grpc_credentials.aws_iam": "//source/extensions/grpc_credentials/aws_iam:config", - - # - # Health checkers - # - "envoy.health_checkers.redis": "//source/extensions/health_checkers/redis:config", - - # - # HTTP filters - # - "envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config", - "envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config", - "envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config", - "envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config", - "envoy.filters.http.cache": "//source/extensions/filters/http/cache:config", - "envoy.filters.http.cors": "//source/extensions/filters/http/cors:config", - "envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config", - "envoy.filters.http.dynamic_forward_proxy": "//source/extensions/filters/http/dynamic_forward_proxy:config", - "envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config", - "envoy.filters.http.ext_authz": "//source/extensions/filters/http/ext_authz:config", - "envoy.filters.http.fault": "//source/extensions/filters/http/fault:config", - "envoy.filters.http.grpc_http1_bridge": "//source/extensions/filters/http/grpc_http1_bridge:config", - "envoy.filters.http.grpc_http1_reverse_bridge": "//source/extensions/filters/http/grpc_http1_reverse_bridge:config", - "envoy.filters.http.grpc_json_transcoder": "//source/extensions/filters/http/grpc_json_transcoder:config", - "envoy.filters.http.grpc_stats": "//source/extensions/filters/http/grpc_stats:config", - "envoy.filters.http.grpc_web": "//source/extensions/filters/http/grpc_web:config", - "envoy.filters.http.gzip": "//source/extensions/filters/http/gzip:config", - "envoy.filters.http.header_to_metadata": "//source/extensions/filters/http/header_to_metadata:config", - "envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config", - "envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config", - "envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", - "envoy.filters.http.lua": "//source/extensions/filters/http/lua:config", - "envoy.filters.http.on_demand": "//source/extensions/filters/http/on_demand:config", - "envoy.filters.http.original_src": "//source/extensions/filters/http/original_src:config", - "envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config", - "envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config", - "envoy.filters.http.router": "//source/extensions/filters/http/router:config", - "envoy.filters.http.squash": "//source/extensions/filters/http/squash:config", - "envoy.filters.http.tap": "//source/extensions/filters/http/tap:config", - - # - # Listener filters - # - "envoy.filters.listener.http_inspector": "//source/extensions/filters/listener/http_inspector:config", - # NOTE: The original_dst filter is implicitly loaded if original_dst functionality is - # configured on the listener. Do not remove it in that case or configs will fail to load. - "envoy.filters.listener.original_dst": "//source/extensions/filters/listener/original_dst:config", - "envoy.filters.listener.original_src": "//source/extensions/filters/listener/original_src:config", - # NOTE: The proxy_protocol filter is implicitly loaded if proxy_protocol functionality is - # configured on the listener. Do not remove it in that case or configs will fail to load. - "envoy.filters.listener.proxy_protocol": "//source/extensions/filters/listener/proxy_protocol:config", - "envoy.filters.listener.tls_inspector": "//source/extensions/filters/listener/tls_inspector:config", - - # - # Network filters - # - "envoy.filters.network.client_ssl_auth": "//source/extensions/filters/network/client_ssl_auth:config", - "envoy.filters.network.direct_response": "//source/extensions/filters/network/direct_response:config", - "envoy.filters.network.dubbo_proxy": "//source/extensions/filters/network/dubbo_proxy:config", - "envoy.filters.network.echo": "//source/extensions/filters/network/echo:config", - "envoy.filters.network.ext_authz": "//source/extensions/filters/network/ext_authz:config", - "envoy.filters.network.http_connection_manager": "//source/extensions/filters/network/http_connection_manager:config", - # WiP - "envoy.filters.network.kafka_broker": "//source/extensions/filters/network/kafka:kafka_broker_config_lib", - "envoy.filters.network.local_ratelimit": "//source/extensions/filters/network/local_ratelimit:config", - "envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config", - "envoy.filters.network.mysql_proxy": "//source/extensions/filters/network/mysql_proxy:config", - "envoy.filters.network.postgres_proxy": "//source/extensions/filters/network/postgres_proxy:config", - "envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config", - "envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config", - "envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config", - "envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config", - "envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config", - "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", - "envoy.filters.network.sni_dynamic_forward_proxy": "//source/extensions/filters/network/sni_dynamic_forward_proxy:config", - "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", - # - # UDP filters - # - "envoy.filters.udp_listener.dns_filter": "//source/extensions/filters/udp/dns_filter:config", - "envoy.filters.udp_listener.udp_proxy": "//source/extensions/filters/udp/udp_proxy:config", - - # - # Resource monitors - # - "envoy.resource_monitors.fixed_heap": "//source/extensions/resource_monitors/fixed_heap:config", - "envoy.resource_monitors.injected_resource": "//source/extensions/resource_monitors/injected_resource:config", - - # - # Stat sinks - # - "envoy.stat_sinks.dog_statsd": "//source/extensions/stat_sinks/dog_statsd:config", - "envoy.stat_sinks.hystrix": "//source/extensions/stat_sinks/hystrix:config", - "envoy.stat_sinks.metrics_service": "//source/extensions/stat_sinks/metrics_service:config", - "envoy.stat_sinks.statsd": "//source/extensions/stat_sinks/statsd:config", - - # - # Thrift filters - # - "envoy.filters.thrift.router": "//source/extensions/filters/network/thrift_proxy/router:config", - "envoy.filters.thrift.ratelimit": "//source/extensions/filters/network/thrift_proxy/filters/ratelimit:config", - - # - # Tracers - # - "envoy.tracers.dynamic_ot": "//source/extensions/tracers/dynamic_ot:config", - "envoy.tracers.lightstep": "//source/extensions/tracers/lightstep:config", - "envoy.tracers.datadog": "//source/extensions/tracers/datadog:config", - "envoy.tracers.zipkin": "//source/extensions/tracers/zipkin:config", - "envoy.tracers.opencensus": "//source/extensions/tracers/opencensus:config", - # WiP - "envoy.tracers.xray": "//source/extensions/tracers/xray:config", - - # - # Transport sockets - # - "envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config", - "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", - "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", - - # - # Retry host predicates - # - "envoy.retry_host_predicates.previous_hosts": "//source/extensions/retry/host/previous_hosts:config", - "envoy.retry_host_predicates.omit_canary_hosts": "//source/extensions/retry/host/omit_canary_hosts:config", - "envoy.retry_host_predicates.omit_host_metadata": "//source/extensions/retry/host/omit_host_metadata:config", - - # - # Retry priorities - # - "envoy.retry_priorities.previous_priorities": "//source/extensions/retry/priority/previous_priorities:config", - - # - # CacheFilter plugins - # - "envoy.filters.http.cache.simple_http_cache": "//source/extensions/filters/http/cache/simple_http_cache:simple_http_cache_lib", - - # - # Internal redirect predicates - # - "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", - "envoy.internal_redirect_predicates.whitelisted_routes": "//source/extensions/internal_redirect/whitelisted_routes:config", -} - -WINDOWS_EXTENSIONS = { - # - # Access loggers - # - "envoy.access_loggers.file": "//source/extensions/access_loggers/file:config", - "envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config", - "envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config", + "envoy.access_loggers.file": "//source/extensions/access_loggers/file:config", + "envoy.access_loggers.http_grpc": "//source/extensions/access_loggers/grpc:http_config", + "envoy.access_loggers.tcp_grpc": "//source/extensions/access_loggers/grpc:tcp_config", # # Clusters # - "envoy.clusters.aggregate": "//source/extensions/clusters/aggregate:cluster", - "envoy.clusters.dynamic_forward_proxy": "//source/extensions/clusters/dynamic_forward_proxy:cluster", - # "envoy.clusters.redis": "//source/extensions/clusters/redis:redis_cluster", + + "envoy.clusters.aggregate": "//source/extensions/clusters/aggregate:cluster", + "envoy.clusters.dynamic_forward_proxy": "//source/extensions/clusters/dynamic_forward_proxy:cluster", + "envoy.clusters.redis": "//source/extensions/clusters/redis:redis_cluster", # # gRPC Credentials Plugins # - "envoy.grpc_credentials.file_based_metadata": "//source/extensions/grpc_credentials/file_based_metadata:config", - "envoy.grpc_credentials.aws_iam": "//source/extensions/grpc_credentials/aws_iam:config", + + "envoy.grpc_credentials.file_based_metadata": "//source/extensions/grpc_credentials/file_based_metadata:config", + "envoy.grpc_credentials.aws_iam": "//source/extensions/grpc_credentials/aws_iam:config", # # Health checkers # - # "envoy.health_checkers.redis": "//source/extensions/health_checkers/redis:config", + "envoy.health_checkers.redis": "//source/extensions/health_checkers/redis:config", # # HTTP filters # - # "envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config", - "envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config", - "envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config", - "envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config", - "envoy.filters.http.cache": "//source/extensions/filters/http/cache:config", - "envoy.filters.http.cors": "//source/extensions/filters/http/cors:config", - "envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config", - "envoy.filters.http.dynamic_forward_proxy": "//source/extensions/filters/http/dynamic_forward_proxy:config", - "envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config", - "envoy.filters.http.ext_authz": "//source/extensions/filters/http/ext_authz:config", - "envoy.filters.http.fault": "//source/extensions/filters/http/fault:config", - "envoy.filters.http.grpc_http1_bridge": "//source/extensions/filters/http/grpc_http1_bridge:config", - "envoy.filters.http.grpc_http1_reverse_bridge": "//source/extensions/filters/http/grpc_http1_reverse_bridge:config", - "envoy.filters.http.grpc_json_transcoder": "//source/extensions/filters/http/grpc_json_transcoder:config", - "envoy.filters.http.grpc_stats": "//source/extensions/filters/http/grpc_stats:config", - "envoy.filters.http.grpc_web": "//source/extensions/filters/http/grpc_web:config", - "envoy.filters.http.gzip": "//source/extensions/filters/http/gzip:config", - "envoy.filters.http.header_to_metadata": "//source/extensions/filters/http/header_to_metadata:config", - "envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config", - "envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config", - "envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", - # "envoy.filters.http.lua": "//source/extensions/filters/http/lua:config", - "envoy.filters.http.on_demand": "//source/extensions/filters/http/on_demand:config", - "envoy.filters.http.original_src": "//source/extensions/filters/http/original_src:config", - "envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config", - "envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config", - "envoy.filters.http.router": "//source/extensions/filters/http/router:config", - "envoy.filters.http.squash": "//source/extensions/filters/http/squash:config", - "envoy.filters.http.tap": "//source/extensions/filters/http/tap:config", + "envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config", + "envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config", + "envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config", + "envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config", + "envoy.filters.http.cache": "//source/extensions/filters/http/cache:config", + "envoy.filters.http.cors": "//source/extensions/filters/http/cors:config", + "envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config", + "envoy.filters.http.dynamic_forward_proxy": "//source/extensions/filters/http/dynamic_forward_proxy:config", + "envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config", + "envoy.filters.http.ext_authz": "//source/extensions/filters/http/ext_authz:config", + "envoy.filters.http.fault": "//source/extensions/filters/http/fault:config", + "envoy.filters.http.grpc_http1_bridge": "//source/extensions/filters/http/grpc_http1_bridge:config", + "envoy.filters.http.grpc_http1_reverse_bridge": "//source/extensions/filters/http/grpc_http1_reverse_bridge:config", + "envoy.filters.http.grpc_json_transcoder": "//source/extensions/filters/http/grpc_json_transcoder:config", + "envoy.filters.http.grpc_stats": "//source/extensions/filters/http/grpc_stats:config", + "envoy.filters.http.grpc_web": "//source/extensions/filters/http/grpc_web:config", + "envoy.filters.http.gzip": "//source/extensions/filters/http/gzip:config", + "envoy.filters.http.header_to_metadata": "//source/extensions/filters/http/header_to_metadata:config", + "envoy.filters.http.health_check": "//source/extensions/filters/http/health_check:config", + "envoy.filters.http.ip_tagging": "//source/extensions/filters/http/ip_tagging:config", + "envoy.filters.http.jwt_authn": "//source/extensions/filters/http/jwt_authn:config", + "envoy.filters.http.lua": "//source/extensions/filters/http/lua:config", + "envoy.filters.http.on_demand": "//source/extensions/filters/http/on_demand:config", + "envoy.filters.http.original_src": "//source/extensions/filters/http/original_src:config", + "envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config", + "envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config", + "envoy.filters.http.router": "//source/extensions/filters/http/router:config", + "envoy.filters.http.squash": "//source/extensions/filters/http/squash:config", + "envoy.filters.http.tap": "//source/extensions/filters/http/tap:config", # # Listener filters # - "envoy.filters.listener.http_inspector": "//source/extensions/filters/listener/http_inspector:config", + + "envoy.filters.listener.http_inspector": "//source/extensions/filters/listener/http_inspector:config", # NOTE: The original_dst filter is implicitly loaded if original_dst functionality is # configured on the listener. Do not remove it in that case or configs will fail to load. - "envoy.filters.listener.original_dst": "//source/extensions/filters/listener/original_dst:config", - "envoy.filters.listener.original_src": "//source/extensions/filters/listener/original_src:config", + "envoy.filters.listener.original_dst": "//source/extensions/filters/listener/original_dst:config", + "envoy.filters.listener.original_src": "//source/extensions/filters/listener/original_src:config", # NOTE: The proxy_protocol filter is implicitly loaded if proxy_protocol functionality is # configured on the listener. Do not remove it in that case or configs will fail to load. - "envoy.filters.listener.proxy_protocol": "//source/extensions/filters/listener/proxy_protocol:config", - "envoy.filters.listener.tls_inspector": "//source/extensions/filters/listener/tls_inspector:config", + "envoy.filters.listener.proxy_protocol": "//source/extensions/filters/listener/proxy_protocol:config", + "envoy.filters.listener.tls_inspector": "//source/extensions/filters/listener/tls_inspector:config", # # Network filters # - "envoy.filters.network.client_ssl_auth": "//source/extensions/filters/network/client_ssl_auth:config", - "envoy.filters.network.direct_response": "//source/extensions/filters/network/direct_response:config", - "envoy.filters.network.dubbo_proxy": "//source/extensions/filters/network/dubbo_proxy:config", - "envoy.filters.network.echo": "//source/extensions/filters/network/echo:config", - "envoy.filters.network.ext_authz": "//source/extensions/filters/network/ext_authz:config", - "envoy.filters.network.http_connection_manager": "//source/extensions/filters/network/http_connection_manager:config", + + "envoy.filters.network.client_ssl_auth": "//source/extensions/filters/network/client_ssl_auth:config", + "envoy.filters.network.direct_response": "//source/extensions/filters/network/direct_response:config", + "envoy.filters.network.dubbo_proxy": "//source/extensions/filters/network/dubbo_proxy:config", + "envoy.filters.network.echo": "//source/extensions/filters/network/echo:config", + "envoy.filters.network.ext_authz": "//source/extensions/filters/network/ext_authz:config", + "envoy.filters.network.http_connection_manager": "//source/extensions/filters/network/http_connection_manager:config", # WiP - # "envoy.filters.network.kafka_broker": "//source/extensions/filters/network/kafka:kafka_broker_config_lib", - "envoy.filters.network.local_ratelimit": "//source/extensions/filters/network/local_ratelimit:config", - "envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config", - # "envoy.filters.network.mysql_proxy": "//source/extensions/filters/network/mysql_proxy:config", - "envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config", - "envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config", - # "envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config", - "envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config", - "envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config", - "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", - "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", + "envoy.filters.network.kafka_broker": "//source/extensions/filters/network/kafka:kafka_broker_config_lib", + "envoy.filters.network.local_ratelimit": "//source/extensions/filters/network/local_ratelimit:config", + "envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config", + "envoy.filters.network.mysql_proxy": "//source/extensions/filters/network/mysql_proxy:config", + "envoy.filters.network.postgres_proxy": "//source/extensions/filters/network/postgres_proxy:config", + "envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config", + "envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config", + "envoy.filters.network.redis_proxy": "//source/extensions/filters/network/redis_proxy:config", + "envoy.filters.network.tcp_proxy": "//source/extensions/filters/network/tcp_proxy:config", + "envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config", + "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", + "envoy.filters.network.sni_dynamic_forward_proxy": "//source/extensions/filters/network/sni_dynamic_forward_proxy:config", + "envoy.filters.network.zookeeper_proxy": "//source/extensions/filters/network/zookeeper_proxy:config", # # UDP filters # - "envoy.filters.udp_listener.udp_proxy": "//source/extensions/filters/udp/udp_proxy:config", + + "envoy.filters.udp_listener.dns_filter": "//source/extensions/filters/udp/dns_filter:config", + "envoy.filters.udp_listener.udp_proxy": "//source/extensions/filters/udp/udp_proxy:config", # # Resource monitors # - "envoy.resource_monitors.fixed_heap": "//source/extensions/resource_monitors/fixed_heap:config", - "envoy.resource_monitors.injected_resource": "//source/extensions/resource_monitors/injected_resource:config", + + "envoy.resource_monitors.fixed_heap": "//source/extensions/resource_monitors/fixed_heap:config", + "envoy.resource_monitors.injected_resource": "//source/extensions/resource_monitors/injected_resource:config", # # Stat sinks # - "envoy.stat_sinks.dog_statsd": "//source/extensions/stat_sinks/dog_statsd:config", - # "envoy.stat_sinks.hystrix": "//source/extensions/stat_sinks/hystrix:config", - "envoy.stat_sinks.metrics_service": "//source/extensions/stat_sinks/metrics_service:config", - # "envoy.stat_sinks.statsd": "//source/extensions/stat_sinks/statsd:config", + + "envoy.stat_sinks.dog_statsd": "//source/extensions/stat_sinks/dog_statsd:config", + "envoy.stat_sinks.hystrix": "//source/extensions/stat_sinks/hystrix:config", + "envoy.stat_sinks.metrics_service": "//source/extensions/stat_sinks/metrics_service:config", + "envoy.stat_sinks.statsd": "//source/extensions/stat_sinks/statsd:config", # # Thrift filters # - "envoy.filters.thrift.router": "//source/extensions/filters/network/thrift_proxy/router:config", - "envoy.filters.thrift.ratelimit": "//source/extensions/filters/network/thrift_proxy/filters/ratelimit:config", + + "envoy.filters.thrift.router": "//source/extensions/filters/network/thrift_proxy/router:config", + "envoy.filters.thrift.ratelimit": "//source/extensions/filters/network/thrift_proxy/filters/ratelimit:config", # # Tracers # - # These first three require opentracing to build on windows, but it is - # a foreign_cc build based on a linux autotools build schema. - # "envoy.tracers.dynamic_ot": "//source/extensions/tracers/dynamic_ot:config", - # "envoy.tracers.lightstep": "//source/extensions/tracers/lightstep:config", - # "envoy.tracers.datadog": "//source/extensions/tracers/datadog:config", - "envoy.tracers.zipkin": "//source/extensions/tracers/zipkin:config", - # The opencensus tool needs windows porting (unistd.h isn't c++) - # "envoy.tracers.opencensus": "//source/extensions/tracers/opencensus:config", + "envoy.tracers.dynamic_ot": "//source/extensions/tracers/dynamic_ot:config", + "envoy.tracers.lightstep": "//source/extensions/tracers/lightstep:config", + "envoy.tracers.datadog": "//source/extensions/tracers/datadog:config", + "envoy.tracers.zipkin": "//source/extensions/tracers/zipkin:config", + "envoy.tracers.opencensus": "//source/extensions/tracers/opencensus:config", # WiP - "envoy.tracers.xray": "//source/extensions/tracers/xray:config", + "envoy.tracers.xray": "//source/extensions/tracers/xray:config", # # Transport sockets # - "envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config", - "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", - "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", + + "envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config", + "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", + "envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config", # # Retry host predicates # - "envoy.retry_host_predicates.previous_hosts": "//source/extensions/retry/host/previous_hosts:config", - "envoy.retry_host_predicates.omit_canary_hosts": "//source/extensions/retry/host/omit_canary_hosts:config", - "envoy.retry_host_predicates.omit_host_metadata": "//source/extensions/retry/host/omit_host_metadata:config", + + "envoy.retry_host_predicates.previous_hosts": "//source/extensions/retry/host/previous_hosts:config", + "envoy.retry_host_predicates.omit_canary_hosts": "//source/extensions/retry/host/omit_canary_hosts:config", + "envoy.retry_host_predicates.omit_host_metadata": "//source/extensions/retry/host/omit_host_metadata:config", # # Retry priorities # - "envoy.retry_priorities.previous_priorities": "//source/extensions/retry/priority/previous_priorities:config", + + "envoy.retry_priorities.previous_priorities": "//source/extensions/retry/priority/previous_priorities:config", # # CacheFilter plugins # - "envoy.filters.http.cache.simple_http_cache": "//source/extensions/filters/http/cache/simple_http_cache:simple_http_cache_lib", - # + "envoy.filters.http.cache.simple_http_cache": "//source/extensions/filters/http/cache/simple_http_cache:simple_http_cache_lib", + # Internal redirect predicates # "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", From e3eee181e729074df5d8578d1909051b5e537c01 Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 17:54:12 -0400 Subject: [PATCH 04/46] Fix more typo in the comments. Signed-off-by: pengg --- include/envoy/router/internal_redirect.h | 10 ++++++---- include/envoy/router/router.h | 2 +- source/extensions/extensions_build_config.bzl | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index ea976ccc4127..9afcd7a97420 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -16,10 +16,12 @@ class InternalRedirectPredicate { virtual ~InternalRedirectPredicate() = default; /** + * A FilterState is provided so that predicate implementation canuse it to preserve state across + * internal redirects. + * * @return whether the route specified by target_route_name is allowed to be followed. Any - * predicate returning false will prevent the redirect to be followed, causing the response to be - * proxied to the downstream. A FilterState is provided so that predicate implementation can use - * it to preserve state across internal redirects. + * predicate returning false will prevent the redirect from being followed, causing the + * response to be proxied to the downstream. */ virtual bool acceptTargetRoute(StreamInfo::FilterState& filter_State, absl::string_view target_route_name) PURE; @@ -36,7 +38,7 @@ class InternalRedirectPredicateFactory : public Config::TypedFactory { /** * @return an InternalRedirectPredicate. The given current_route_name is useful for predicates - * that need to create per-route FilterState. + * that need to create per-route FilterState. */ virtual InternalRedirectPredicateSharedPtr createInternalRedirectPredicate(const Protobuf::Message& config, diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index 03a24ca303f8..7818dca92c9c 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -721,7 +721,7 @@ class RouteEntry : public ResponseEntry { * @return const InternalRedirectPolicy& the internal redirect policy for the route. All routes * have a internal redirect policy even if it is not enabled, which means redirects from * the upstream are not followed. - * */ + */ virtual const InternalRedirectPolicy& internalRedirectPolicy() const PURE; /** diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 453b7d814009..97cf7c037c97 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -174,6 +174,6 @@ EXTENSIONS = { # Internal redirect predicates # - "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", + "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", "envoy.internal_redirect_predicates.whitelisted_routes": "//source/extensions/internal_redirect/whitelisted_routes:config", } From c89a6b434d2339a6d6e60d62e627d7fbbad1e4ba Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 18:22:17 -0400 Subject: [PATCH 05/46] Fix ref links. Signed-off-by: pengg --- .../http/http_connection_management.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index 593acfb48987..ecff545b7883 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -155,28 +155,28 @@ Envoy supports handling 3xx redirects internally, that is capturing a configurab response, synthesizing a new request, sending it to the upstream specified by the new route match, and returning the redirected response as the response to the original request. -Internal redirects are configured via the ref:`internal redirect policy +Internal redirects are configured via the :ref:`internal redirect policy ` field in route configuration. When redirect handling is on, any 3xx response from upstream, that matches -ref:`redirect_response_codes ` +:ref:`redirect_response_codes ` is subject to the redirect being handled by Envoy. For a redirect to be handled successfully it must pass the following checks: -1. Have a response code matching one of ref:`redirect_response_codes +1. Have a response code matching one of :ref:`redirect_response_codes ` 2. Have a *location* header with a valid, fully qualified URL matching the scheme of the original request. 3. The request must have been fully processed by Envoy. 4. The request must not have a body. 5. The scheme pair of the downstream request and the *location* header is allowed by - `allowed downstream and target scheme pair + :ref:`allowed downstream and target scheme pair ` 6. The number of previously handled internal redirect within a given downstream request does not - exceed `max internal redirects + exceed :ref:`max internal redirects ` of the route that the request or redirected request is hitting. -7. All ref:`predicates ` accept +7. All :ref:`predicates ` accept the target route. Any failure will result in redirect being passed downstream instead. @@ -187,14 +187,14 @@ Since a redirected request may be bounced between different routes, any route in 2. or has a `max internal redirects ` smaller or equal to the redirect chain length when the redirect chain hits it -3. or is disallowed by any of the ref:`predicates +3. or is disallowed by any of the :ref:`predicates ` will cause the redirect to be passed downstream. -Two predicates can be used to create a DAG that defines the redirect chain, the ref:`previous routes +Two predicates can be used to create a DAG that defines the redirect chain, the :ref:`previous routes Date: Wed, 22 Apr 2020 18:47:26 -0400 Subject: [PATCH 06/46] Fix link in hcm arch overview. Signed-off-by: pengg --- .../http/http_connection_management.rst | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index ecff545b7883..82360e1d455a 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -156,27 +156,28 @@ response, synthesizing a new request, sending it to the upstream specified by th and returning the redirected response as the response to the original request. Internal redirects are configured via the :ref:`internal redirect policy -` field in route configuration. +` field in route configuration. When redirect handling is on, any 3xx response from upstream, that matches -:ref:`redirect_response_codes ` +:ref:`redirect_response_codes +` is subject to the redirect being handled by Envoy. For a redirect to be handled successfully it must pass the following checks: 1. Have a response code matching one of :ref:`redirect_response_codes - ` + ` 2. Have a *location* header with a valid, fully qualified URL matching the scheme of the original request. 3. The request must have been fully processed by Envoy. 4. The request must not have a body. 5. The scheme pair of the downstream request and the *location* header is allowed by :ref:`allowed downstream and target scheme pair - ` + ` 6. The number of previously handled internal redirect within a given downstream request does not exceed :ref:`max internal redirects - ` + ` of the route that the request or redirected request is hitting. -7. All :ref:`predicates ` accept +7. All :ref:`predicates ` accept the target route. Any failure will result in redirect being passed downstream instead. @@ -185,10 +186,10 @@ Since a redirected request may be bounced between different routes, any route in 1. does not have internal redirect enabled 2. or has a `max internal redirects - ` + ` smaller or equal to the redirect chain length when the redirect chain hits it 3. or is disallowed by any of the :ref:`predicates - ` + ` will cause the redirect to be passed downstream. From 985819a8f0994837f6c51eadf957bf31da59768a Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 19:36:53 -0400 Subject: [PATCH 07/46] Fix doc links. For real. Signed-off-by: pengg --- docs/root/api-v3/config/config.rst | 1 + .../internal_redirect/internal_redirect.rst | 8 +++++++ .../http/http_connection_management.rst | 22 +++++++++---------- 3 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 docs/root/api-v3/config/internal_redirect/internal_redirect.rst diff --git a/docs/root/api-v3/config/config.rst b/docs/root/api-v3/config/config.rst index d7e6e6edd43c..b13b8b0bc92d 100644 --- a/docs/root/api-v3/config/config.rst +++ b/docs/root/api-v3/config/config.rst @@ -16,3 +16,4 @@ Extensions grpc_credential/grpc_credential retry/retry trace/trace + internal_redirect/internal_redirect diff --git a/docs/root/api-v3/config/internal_redirect/internal_redirect.rst b/docs/root/api-v3/config/internal_redirect/internal_redirect.rst new file mode 100644 index 000000000000..b89c53bfbdfb --- /dev/null +++ b/docs/root/api-v3/config/internal_redirect/internal_redirect.rst @@ -0,0 +1,8 @@ +Internal Redirect Predicates +===++++++++++++============= + +.. toctree:: + :glob: + :maxdepth: 2 + + ../../extensions/internal_redirect/** diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index 82360e1d455a..0a02173a569a 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -156,28 +156,28 @@ response, synthesizing a new request, sending it to the upstream specified by th and returning the redirected response as the response to the original request. Internal redirects are configured via the :ref:`internal redirect policy -` field in route configuration. +` field in route configuration. When redirect handling is on, any 3xx response from upstream, that matches :ref:`redirect_response_codes -` +` is subject to the redirect being handled by Envoy. For a redirect to be handled successfully it must pass the following checks: 1. Have a response code matching one of :ref:`redirect_response_codes - ` + ` 2. Have a *location* header with a valid, fully qualified URL matching the scheme of the original request. 3. The request must have been fully processed by Envoy. 4. The request must not have a body. 5. The scheme pair of the downstream request and the *location* header is allowed by :ref:`allowed downstream and target scheme pair - ` + ` 6. The number of previously handled internal redirect within a given downstream request does not exceed :ref:`max internal redirects - ` + ` of the route that the request or redirected request is hitting. -7. All :ref:`predicates ` accept +7. All :ref:`predicates ` accept the target route. Any failure will result in redirect being passed downstream instead. @@ -185,18 +185,18 @@ Any failure will result in redirect being passed downstream instead. Since a redirected request may be bounced between different routes, any route in the chain of redirects that 1. does not have internal redirect enabled -2. or has a `max internal redirects - ` +2. or has a :ref:`max internal redirects + ` smaller or equal to the redirect chain length when the redirect chain hits it 3. or is disallowed by any of the :ref:`predicates - ` + ` will cause the redirect to be passed downstream. Two predicates can be used to create a DAG that defines the redirect chain, the :ref:`previous routes -` predicate, and the :ref:`whitelisted routes -`. Specifically, the *whitelisted routes* predicate defines edges of individual node in the DAG and the *previous routes* predicate defines "visited" state of the edges, so that loop can be avoided if so desired. From 8199c60c5dafecb4fa0214ec51e0b26cb0c32a53 Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 20:00:03 -0400 Subject: [PATCH 08/46] And fix the formats. Signed-off-by: pengg --- api/envoy/config/route/v4alpha/route_components.proto | 9 ++++----- .../envoy/config/route/v3/route_components.proto | 9 ++++----- .../envoy/config/route/v4alpha/route_components.proto | 9 ++++----- .../previous_routes/v3/previous_routes_config.proto | 2 +- .../internal_redirect/whitelisted_routes/config.h | 1 + 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 1d7158c8dd3f..25af53df87c8 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1606,8 +1606,7 @@ message InternalRedirectPolicy { // If not specified, at most one redirect will be followed. google.protobuf.UInt32Value max_internal_redirects = 2; - // Defines what upstream response codes can trigger the internal redirect - // behavior. This is ignored unless + // Defines what upstream response codes are allowed to trigger internal redirect. This is ignored unless // :ref:`internal_redirect_action // ` // is set to @@ -1615,12 +1614,12 @@ message InternalRedirectPolicy { // ` repeated uint32 redirect_response_codes = 3; - // Specifies a list of predicates that is queries when an upstream response is deemed to be able + // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criterias. Any predicate in the list can reject - // the redirect. + // the redirect, causing the response to be proxied to downstream. repeated Predicate predicates = 4; - // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an + // Specifies what combinations of downstream and redirect target scheme are allowed to trigger an // internal redirect. repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; } diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index c6843d36dd17..b3322a1cc123 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -1632,8 +1632,7 @@ message InternalRedirectPolicy { // If not specified, at most one redirect will be followed. google.protobuf.UInt32Value max_internal_redirects = 2; - // Defines what upstream response codes can trigger the internal redirect - // behavior. This is ignored unless + // Defines what upstream response codes are allowed to trigger internal redirect. This is ignored unless // :ref:`internal_redirect_action // ` // is set to @@ -1641,12 +1640,12 @@ message InternalRedirectPolicy { // ` repeated uint32 redirect_response_codes = 3; - // Specifies a list of predicates that is queries when an upstream response is deemed to be able + // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criterias. Any predicate in the list can reject - // the redirect. + // the redirect, causing the response to be proxied to downstream. repeated Predicate predicates = 4; - // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an + // Specifies what combinations of downstream and redirect target scheme are allowed to trigger an // internal redirect. repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; } diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index f48e16f2937f..09c796cefe3c 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -1625,8 +1625,7 @@ message InternalRedirectPolicy { // If not specified, at most one redirect will be followed. google.protobuf.UInt32Value max_internal_redirects = 2; - // Defines what upstream response codes can trigger the internal redirect - // behavior. This is ignored unless + // Defines what upstream response codes are allowed to trigger internal redirect. This is ignored unless // :ref:`internal_redirect_action // ` // is set to @@ -1634,12 +1633,12 @@ message InternalRedirectPolicy { // ` repeated uint32 redirect_response_codes = 3; - // Specifies a list of predicates that is queries when an upstream response is deemed to be able + // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criterias. Any predicate in the list can reject - // the redirect. + // the redirect, causing the response to be proxied to downstream. repeated Predicate predicates = 4; - // Specifies what combinations of dowstream and redirect target scheme is allowed to trigger an + // Specifies what combinations of downstream and redirect target scheme are allowed to trigger an // internal redirect. repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; } diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto index 2b241fdfe56e..6cc5fba871ea 100644 --- a/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto +++ b/generated_api_shadow/envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.proto @@ -13,7 +13,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // [#protodoc-title: Previous routes internal redirect predicate] // An internal redirect predicate that rejects redirect targets that are pointing -// to a route that has been follwed from the current route. +// to a route that has been followed by a previous redirect from the current route. // [#extension: envoy.internal_redirect_predicates.previous_routes] message PreviousRoutesConfig { } diff --git a/source/extensions/internal_redirect/whitelisted_routes/config.h b/source/extensions/internal_redirect/whitelisted_routes/config.h index 5e3952035c7d..6fadc725a86f 100644 --- a/source/extensions/internal_redirect/whitelisted_routes/config.h +++ b/source/extensions/internal_redirect/whitelisted_routes/config.h @@ -6,6 +6,7 @@ #include "common/protobuf/message_validator_impl.h" #include "common/protobuf/utility.h" + #include "extensions/internal_redirect/well_known_names.h" #include "extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h" From fac46f205663a0a8d6b969e2aa2d54069f667e9c Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 20:34:45 -0400 Subject: [PATCH 09/46] Fix typos. Signed-off-by: pengg --- api/envoy/config/route/v3/route_components.proto | 4 ++-- api/envoy/config/route/v4alpha/route_components.proto | 4 ++-- .../envoy/config/route/v3/route_components.proto | 4 ++-- .../envoy/config/route/v4alpha/route_components.proto | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 67366f78173e..7433c37b6e78 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1577,7 +1577,7 @@ message InternalRedirectPolicy { HANDLE_INTERNAL_REDIRECT = 1; } - // HTTP scheme conbinations that are allowed to be handled as internal + // HTTP scheme combinations that are allowed to be handled as internal // redirect. Downstream scheme comes first and redirect target URL scheme // follows. enum DownstreamAndRedirectTargetSchemePair { @@ -1623,7 +1623,7 @@ message InternalRedirectPolicy { repeated uint32 redirect_response_codes = 3; // Specifies a list of predicates that are queried when an upstream response is deemed - // to trigger an internal redirect by all other criterias. Any predicate in the list can reject + // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. repeated Predicate predicates = 4; diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 25af53df87c8..500bf6fffe46 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1566,7 +1566,7 @@ message InternalRedirectPolicy { HANDLE_INTERNAL_REDIRECT = 1; } - // HTTP scheme conbinations that are allowed to be handled as internal + // HTTP scheme combinations that are allowed to be handled as internal // redirect. Downstream scheme comes first and redirect target URL scheme // follows. enum DownstreamAndRedirectTargetSchemePair { @@ -1615,7 +1615,7 @@ message InternalRedirectPolicy { repeated uint32 redirect_response_codes = 3; // Specifies a list of predicates that are queried when an upstream response is deemed - // to trigger an internal redirect by all other criterias. Any predicate in the list can reject + // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. repeated Predicate predicates = 4; diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index b3322a1cc123..cca65c4de114 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -1595,7 +1595,7 @@ message InternalRedirectPolicy { HANDLE_INTERNAL_REDIRECT = 1; } - // HTTP scheme conbinations that are allowed to be handled as internal + // HTTP scheme combinations that are allowed to be handled as internal // redirect. Downstream scheme comes first and redirect target URL scheme // follows. enum DownstreamAndRedirectTargetSchemePair { @@ -1641,7 +1641,7 @@ message InternalRedirectPolicy { repeated uint32 redirect_response_codes = 3; // Specifies a list of predicates that are queried when an upstream response is deemed - // to trigger an internal redirect by all other criterias. Any predicate in the list can reject + // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. repeated Predicate predicates = 4; diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index 09c796cefe3c..0d41b775c89a 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -1585,7 +1585,7 @@ message InternalRedirectPolicy { HANDLE_INTERNAL_REDIRECT = 1; } - // HTTP scheme conbinations that are allowed to be handled as internal + // HTTP scheme combinations that are allowed to be handled as internal // redirect. Downstream scheme comes first and redirect target URL scheme // follows. enum DownstreamAndRedirectTargetSchemePair { @@ -1634,7 +1634,7 @@ message InternalRedirectPolicy { repeated uint32 redirect_response_codes = 3; // Specifies a list of predicates that are queried when an upstream response is deemed - // to trigger an internal redirect by all other criterias. Any predicate in the list can reject + // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. repeated Predicate predicates = 4; From fe9e5c905367c72fce365c8dcef09967b44f0f97 Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 20:59:45 -0400 Subject: [PATCH 10/46] More typo correction. Signed-off-by: pengg --- include/envoy/router/internal_redirect.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index 9afcd7a97420..d4b3db043c1b 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -9,14 +9,14 @@ namespace Envoy { namespace Router { /** - * Used to decide if a internal redirect is allowed to be followed based on the target route. + * Used to decide if an internal redirect is allowed to be followed based on the target route. */ class InternalRedirectPredicate { public: virtual ~InternalRedirectPredicate() = default; /** - * A FilterState is provided so that predicate implementation canuse it to preserve state across + * A FilterState is provided so that predicate implementation can use it to preserve state across * internal redirects. * * @return whether the route specified by target_route_name is allowed to be followed. Any From 179e5a9012a0a76fed97db9814fa63df614d8c3b Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 22 Apr 2020 21:04:56 -0400 Subject: [PATCH 11/46] More typos. Signed-off-by: pengg --- include/envoy/router/internal_redirect.h | 2 +- source/extensions/internal_redirect/well_known_names.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index d4b3db043c1b..68283c345259 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -30,7 +30,7 @@ class InternalRedirectPredicate { using InternalRedirectPredicateSharedPtr = std::shared_ptr; /** - * Factory for InternalRedirectPredicte. + * Factory for InternalRedirectPredicate. */ class InternalRedirectPredicateFactory : public Config::TypedFactory { public: diff --git a/source/extensions/internal_redirect/well_known_names.h b/source/extensions/internal_redirect/well_known_names.h index 009eaf766fe8..443531de3108 100644 --- a/source/extensions/internal_redirect/well_known_names.h +++ b/source/extensions/internal_redirect/well_known_names.h @@ -9,7 +9,7 @@ namespace Extensions { namespace InternalRedirect { /** - * Well-known internal redirect predidate names. + * Well-known internal redirect predicate names. */ class InternalRedirectPredicatesNameValues { public: From 1c7ada2968745054c44d130e60bef82649942cdc Mon Sep 17 00:00:00 2001 From: pengg Date: Thu, 23 Apr 2020 07:12:34 -0400 Subject: [PATCH 12/46] fix clang-tidy findings. Signed-off-by: pengg --- include/envoy/router/internal_redirect.h | 2 +- .../internal_redirect/whitelisted_routes/whitelisted_routes.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index 68283c345259..d3421e1dc84e 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -23,7 +23,7 @@ class InternalRedirectPredicate { * predicate returning false will prevent the redirect from being followed, causing the * response to be proxied to the downstream. */ - virtual bool acceptTargetRoute(StreamInfo::FilterState& filter_State, + virtual bool acceptTargetRoute(StreamInfo::FilterState& filter_state, absl::string_view target_route_name) PURE; }; diff --git a/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h b/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h index 2c5e0b1bec27..d9be53d798a9 100644 --- a/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h +++ b/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h @@ -20,7 +20,7 @@ class WhitelistedRoutesPredicate : public Router::InternalRedirectPredicate { : whitelisted_routes_(config.whitelisted_route_names().begin(), config.whitelisted_route_names().end()) {} - bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name) { + bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name) override { return whitelisted_routes_.contains(route_name); } From 925950ccee7968eee4359efc7feebf628957f0fb Mon Sep 17 00:00:00 2001 From: pengg Date: Thu, 23 Apr 2020 07:25:37 -0400 Subject: [PATCH 13/46] ConfigTest -> PreviousRoutesTest. Signed-off-by: pengg --- .../internal_redirect/previous_routes/config_test.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/extensions/internal_redirect/previous_routes/config_test.cc b/test/extensions/internal_redirect/previous_routes/config_test.cc index 57cc95b6649c..eef27fb668d2 100644 --- a/test/extensions/internal_redirect/previous_routes/config_test.cc +++ b/test/extensions/internal_redirect/previous_routes/config_test.cc @@ -3,7 +3,6 @@ #include "envoy/router/internal_redirect.h" #include "common/stream_info/filter_state_impl.h" - #include "extensions/internal_redirect/previous_routes/config.h" #include "extensions/internal_redirect/well_known_names.h" @@ -17,9 +16,9 @@ namespace Extensions { namespace InternalRedirect { namespace { -class ConfigTest : public testing::Test { +class PreviousRoutesTest : public testing::Test { protected: - ConfigTest() : filter_state_(StreamInfo::FilterState::LifeSpan::FilterChain) { + PreviousRoutesTest() : filter_state_(StreamInfo::FilterState::LifeSpan::FilterChain) { factory_ = Registry::FactoryRegistry::getFactory( InternalRedirectPredicateValues::get().PreviousRoutesPredicate); config_ = factory_->createEmptyConfigProto(); @@ -30,7 +29,7 @@ class ConfigTest : public testing::Test { ProtobufTypes::MessagePtr config_; }; -TEST_F(ConfigTest, TargetIsOnlyTakenOnce) { +TEST_F(PreviousRoutesTest, TargetIsOnlyTakenOnce) { std::string current_route_name = "fake_current_route"; // Create the predicate for the first time. It should remember nothing in the // filter state, so it allows the redirect. @@ -53,7 +52,7 @@ TEST_F(ConfigTest, TargetIsOnlyTakenOnce) { } } -TEST_F(ConfigTest, RoutesAreIndependent) { +TEST_F(PreviousRoutesTest, RoutesAreIndependent) { // Create the predicate on route_0. { auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_0"); From 61e15f78b55420603594c84002f2dc59cf3861dd Mon Sep 17 00:00:00 2001 From: pengg Date: Thu, 23 Apr 2020 08:11:57 -0400 Subject: [PATCH 14/46] Format. Signed-off-by: pengg --- test/extensions/internal_redirect/previous_routes/config_test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/test/extensions/internal_redirect/previous_routes/config_test.cc b/test/extensions/internal_redirect/previous_routes/config_test.cc index eef27fb668d2..8a081ccac5a9 100644 --- a/test/extensions/internal_redirect/previous_routes/config_test.cc +++ b/test/extensions/internal_redirect/previous_routes/config_test.cc @@ -3,6 +3,7 @@ #include "envoy/router/internal_redirect.h" #include "common/stream_info/filter_state_impl.h" + #include "extensions/internal_redirect/previous_routes/config.h" #include "extensions/internal_redirect/well_known_names.h" From a806956c281618cad5e0fbe2c0e24f32a026a193 Mon Sep 17 00:00:00 2001 From: pengg Date: Thu, 23 Apr 2020 09:39:12 -0400 Subject: [PATCH 15/46] Kick CI Signed-off-by: pengg From 5711900dd486e4a9c58a0e116246a5e631c2a4d1 Mon Sep 17 00:00:00 2001 From: pengg Date: Thu, 23 Apr 2020 11:38:07 -0400 Subject: [PATCH 16/46] Kick CI Signed-off-by: pengg From 684ba65f467af6d3bd395c49a0bac750a0467deb Mon Sep 17 00:00:00 2001 From: pengg Date: Thu, 23 Apr 2020 13:45:25 -0400 Subject: [PATCH 17/46] Avoid using newly deprecated fields in the integration test. Signed-off-by: pengg --- test/integration/redirect_integration_test.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index aee360282626..64d187981e50 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -21,17 +21,24 @@ class RedirectIntegrationTest : public HttpProtocolIntegrationTest { auto handle = config_helper_.createVirtualHost("handle.internal.redirect"); handle.mutable_routes(0)->set_name("redirect"); - handle.mutable_routes(0)->mutable_route()->set_internal_redirect_action( - envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT); + handle.mutable_routes(0) + ->mutable_route() + ->mutable_internal_redirect_policy() + ->set_internal_redirect_action( + envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); config_helper_.addVirtualHost(handle); auto handle_max_3_hop = config_helper_.createVirtualHost("handle.internal.redirect.max.three.hop"); handle_max_3_hop.mutable_routes(0)->set_name("max_three_hop"); - handle_max_3_hop.mutable_routes(0)->mutable_route()->set_internal_redirect_action( - envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT); handle_max_3_hop.mutable_routes(0) ->mutable_route() + ->mutable_internal_redirect_policy() + ->set_internal_redirect_action( + envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); + handle_max_3_hop.mutable_routes(0) + ->mutable_route() + ->mutable_internal_redirect_policy() ->mutable_max_internal_redirects() ->set_value(3); config_helper_.addVirtualHost(handle_max_3_hop); From ad05c9b16301261b9a0d66d152e5c28e92b15a15 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 27 Apr 2020 10:16:23 -0400 Subject: [PATCH 18/46] Add comments InternalRedirectAction enum values. Signed-off-by: pengg --- api/envoy/config/route/v3/route_components.proto | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 7433c37b6e78..8f85c238a699 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1573,7 +1573,13 @@ message QueryParameterMatcher { message InternalRedirectPolicy { // Configures :ref:`internal redirect ` behavior. enum InternalRedirectAction { + // Redirect responses are proxied to downstream without triggering Internal Redirect. + // This is the default value. PASS_THROUGH_INTERNAL_REDIRECT = 0; + // Redirect responses (specified via :ref:`redirect_response_codes + // `), + // are followed by Envoy to the new URL. The redirect responses are not visible to + // the downstream unless the redirect following is rejected by other criteria. HANDLE_INTERNAL_REDIRECT = 1; } From c606a1e69074ffea6953a5ceb3be7a38f10a491e Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 27 Apr 2020 10:22:19 -0400 Subject: [PATCH 19/46] Run proto_format on proto comment change. Signed-off-by: pengg --- api/envoy/config/route/v3/route_components.proto | 1 + api/envoy/config/route/v4alpha/route_components.proto | 7 +++++++ .../envoy/config/route/v3/route_components.proto | 7 +++++++ .../envoy/config/route/v4alpha/route_components.proto | 7 +++++++ 4 files changed, 22 insertions(+) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 8f85c238a699..866afbe669c5 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1576,6 +1576,7 @@ message InternalRedirectPolicy { // Redirect responses are proxied to downstream without triggering Internal Redirect. // This is the default value. PASS_THROUGH_INTERNAL_REDIRECT = 0; + // Redirect responses (specified via :ref:`redirect_response_codes // `), // are followed by Envoy to the new URL. The redirect responses are not visible to diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 500bf6fffe46..9133041bf5b8 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1562,7 +1562,14 @@ message InternalRedirectPolicy { // Configures :ref:`internal redirect ` behavior. enum InternalRedirectAction { + // Redirect responses are proxied to downstream without triggering Internal Redirect. + // This is the default value. PASS_THROUGH_INTERNAL_REDIRECT = 0; + + // Redirect responses (specified via :ref:`redirect_response_codes + // `), + // are followed by Envoy to the new URL. The redirect responses are not visible to + // the downstream unless the redirect following is rejected by other criteria. HANDLE_INTERNAL_REDIRECT = 1; } diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index cca65c4de114..307abb358992 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -1591,7 +1591,14 @@ message QueryParameterMatcher { message InternalRedirectPolicy { // Configures :ref:`internal redirect ` behavior. enum InternalRedirectAction { + // Redirect responses are proxied to downstream without triggering Internal Redirect. + // This is the default value. PASS_THROUGH_INTERNAL_REDIRECT = 0; + + // Redirect responses (specified via :ref:`redirect_response_codes + // `), + // are followed by Envoy to the new URL. The redirect responses are not visible to + // the downstream unless the redirect following is rejected by other criteria. HANDLE_INTERNAL_REDIRECT = 1; } diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index 0d41b775c89a..e705942f404f 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -1581,7 +1581,14 @@ message InternalRedirectPolicy { // Configures :ref:`internal redirect ` behavior. enum InternalRedirectAction { + // Redirect responses are proxied to downstream without triggering Internal Redirect. + // This is the default value. PASS_THROUGH_INTERNAL_REDIRECT = 0; + + // Redirect responses (specified via :ref:`redirect_response_codes + // `), + // are followed by Envoy to the new URL. The redirect responses are not visible to + // the downstream unless the redirect following is rejected by other criteria. HANDLE_INTERNAL_REDIRECT = 1; } From feedb0eaee1c60b608c21da57d98dba487bfb952 Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 29 Apr 2020 12:12:15 -0400 Subject: [PATCH 20/46] Address API comments. Signed-off-by: pengg --- api/BUILD | 2 +- .../config/route/v3/route_components.proto | 73 +++++-------------- .../route/v4alpha/route_components.proto | 73 +++++-------------- .../v3/BUILD | 0 .../v3/allowlisted_routes_config.proto | 21 ++++++ .../v3/whitelisted_routes_config.proto | 19 ----- api/versioning/BUILD | 2 +- .../internal_redirect/internal_redirect.rst | 2 +- .../http/http_connection_management.rst | 14 ++-- .../config/route/v3/route_components.proto | 73 +++++-------------- .../route/v4alpha/route_components.proto | 73 +++++-------------- .../v3/BUILD | 0 .../v3/allowlisted_routes_config.proto | 21 ++++++ .../v3/whitelisted_routes_config.proto | 19 ----- include/envoy/router/router.h | 7 +- source/common/router/config_impl.cc | 58 +++------------ source/common/router/config_impl.h | 15 +--- source/common/router/router.cc | 27 ++++--- source/extensions/extensions_build_config.bzl | 2 +- .../BUILD | 10 +-- .../allowlisted_routes/allowlisted_routes.h | 32 ++++++++ .../config.cc | 4 +- .../config.h | 20 ++--- .../internal_redirect/well_known_names.h | 4 +- .../whitelisted_routes/whitelisted_routes.h | 33 --------- test/common/router/config_impl_test.cc | 35 ++++++--- test/common/router/router_test.cc | 37 ++++++++-- test/integration/BUILD | 5 +- test/integration/redirect_integration_test.cc | 48 ++++++------ test/mocks/router/mocks.h | 3 +- 30 files changed, 291 insertions(+), 441 deletions(-) rename api/envoy/extensions/internal_redirect/{whitelisted_routes => allowlisted_routes}/v3/BUILD (100%) create mode 100644 api/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto delete mode 100644 api/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto rename generated_api_shadow/envoy/extensions/internal_redirect/{whitelisted_routes => allowlisted_routes}/v3/BUILD (100%) create mode 100644 generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto delete mode 100644 generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto rename source/extensions/internal_redirect/{whitelisted_routes => allowlisted_routes}/BUILD (75%) create mode 100644 source/extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h rename source/extensions/internal_redirect/{whitelisted_routes => allowlisted_routes}/config.cc (67%) rename source/extensions/internal_redirect/{whitelisted_routes => allowlisted_routes}/config.h (55%) delete mode 100644 source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h diff --git a/api/BUILD b/api/BUILD index 393598fde8c8..fdd260119367 100644 --- a/api/BUILD +++ b/api/BUILD @@ -220,8 +220,8 @@ proto_library( "//envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/v3:pkg", "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", + "//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", - "//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", "//envoy/extensions/transport_sockets/alts/v3:pkg", diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 866afbe669c5..fb2522738618 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -958,6 +958,10 @@ message RouteAction { repeated UpgradeConfig upgrade_configs = 25; + // If present, Envoy will try to follow an upstream redirect response instead of proxying the + // response back to the downstream. An upstream redirect response is defined + // by :ref:`redirect_response_codes + // `. InternalRedirectPolicy internal_redirect_policy = 34; InternalRedirectAction internal_redirect_action = 26 [deprecated = true]; @@ -1569,72 +1573,33 @@ message QueryParameterMatcher { } // HTTP Internal Redirect :ref:`architecture overview `. -// [#next-free-field: 6] message InternalRedirectPolicy { - // Configures :ref:`internal redirect ` behavior. - enum InternalRedirectAction { - // Redirect responses are proxied to downstream without triggering Internal Redirect. - // This is the default value. - PASS_THROUGH_INTERNAL_REDIRECT = 0; - - // Redirect responses (specified via :ref:`redirect_response_codes - // `), - // are followed by Envoy to the new URL. The redirect responses are not visible to - // the downstream unless the redirect following is rejected by other criteria. - HANDLE_INTERNAL_REDIRECT = 1; - } - - // HTTP scheme combinations that are allowed to be handled as internal - // redirect. Downstream scheme comes first and redirect target URL scheme - // follows. - enum DownstreamAndRedirectTargetSchemePair { - HTTP_HTTP = 0; - HTTP_HTTPS = 1; - HTTPS_HTTP = 2; - HTTPS_HTTPS = 3; - } - message Predicate { - string name = 1 [(validate.rules).string = {min_bytes: 1}]; - - google.protobuf.Any typed_config = 2; + google.protobuf.Any typed_config = 1; } - InternalRedirectAction internal_redirect_action = 1; - - // An internal redirect is handled, iff the number of previous internal redirects that a - // downstream request has encountered is lower than this value, and - // :ref:`internal_redirect_action - // ` - // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT - // ` + // An internal redirect is not handled, unless the number of previous internal redirects that a + // downstream request has encountered is lower than this value. // In the case where a downstream request is bounced among multiple routes by internal redirect, - // the first route that hits this threshold, or has - // :ref:`internal_redirect_action - // ` - // set to - // :ref:`PASS_THROUGH_INTERNAL_REDIRECT - // ` + // the first route that hits this threshold, or does not set :ref:`internal_redirect_policy + // ` // will pass the redirect back to downstream. // // If not specified, at most one redirect will be followed. - google.protobuf.UInt32Value max_internal_redirects = 2; + google.protobuf.UInt32Value max_internal_redirects = 1; - // Defines what upstream response codes are allowed to trigger internal redirect. This is ignored unless - // :ref:`internal_redirect_action - // ` - // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT - // ` - repeated uint32 redirect_response_codes = 3; + // Defines what upstream response codes are allowed to trigger internal redirect. If unspecified, + // only 302 will be treated as internal redirect. + // Only 301, 302, 303, 307 and 308 are valid values. + repeated uint32 redirect_response_codes = 2 + [(validate.rules).repeated = {items {uint32 {lte: 308 gte: 301}}}]; // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated Predicate predicates = 4; + repeated Predicate predicates = 3; - // Specifies what combinations of downstream and redirect target scheme are allowed to trigger an - // internal redirect. - repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; + // Allow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to not follow such redirects. + bool allow_cross_scheme_redirect = 4; } diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 9133041bf5b8..a6265383c8dc 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -959,6 +959,10 @@ message RouteAction { repeated UpgradeConfig upgrade_configs = 25; + // If present, Envoy will try to follow an upstream redirect response instead of proxying the + // response back to the downstream. An upstream redirect response is defined + // by :ref:`redirect_response_codes + // `. InternalRedirectPolicy internal_redirect_policy = 34; // Indicates that the route has a hedge policy. Note that if this is set, @@ -1555,78 +1559,39 @@ message QueryParameterMatcher { } // HTTP Internal Redirect :ref:`architecture overview `. -// [#next-free-field: 6] message InternalRedirectPolicy { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.InternalRedirectPolicy"; - // Configures :ref:`internal redirect ` behavior. - enum InternalRedirectAction { - // Redirect responses are proxied to downstream without triggering Internal Redirect. - // This is the default value. - PASS_THROUGH_INTERNAL_REDIRECT = 0; - - // Redirect responses (specified via :ref:`redirect_response_codes - // `), - // are followed by Envoy to the new URL. The redirect responses are not visible to - // the downstream unless the redirect following is rejected by other criteria. - HANDLE_INTERNAL_REDIRECT = 1; - } - - // HTTP scheme combinations that are allowed to be handled as internal - // redirect. Downstream scheme comes first and redirect target URL scheme - // follows. - enum DownstreamAndRedirectTargetSchemePair { - HTTP_HTTP = 0; - HTTP_HTTPS = 1; - HTTPS_HTTP = 2; - HTTPS_HTTPS = 3; - } - message Predicate { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.InternalRedirectPolicy.Predicate"; - string name = 1 [(validate.rules).string = {min_bytes: 1}]; - - google.protobuf.Any typed_config = 2; + google.protobuf.Any typed_config = 1; } - InternalRedirectAction internal_redirect_action = 1; - - // An internal redirect is handled, iff the number of previous internal redirects that a - // downstream request has encountered is lower than this value, and - // :ref:`internal_redirect_action - // ` - // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT - // ` + // An internal redirect is not handled, unless the number of previous internal redirects that a + // downstream request has encountered is lower than this value. // In the case where a downstream request is bounced among multiple routes by internal redirect, - // the first route that hits this threshold, or has - // :ref:`internal_redirect_action - // ` - // set to - // :ref:`PASS_THROUGH_INTERNAL_REDIRECT - // ` + // the first route that hits this threshold, or does not set :ref:`internal_redirect_policy + // ` // will pass the redirect back to downstream. // // If not specified, at most one redirect will be followed. - google.protobuf.UInt32Value max_internal_redirects = 2; + google.protobuf.UInt32Value max_internal_redirects = 1; - // Defines what upstream response codes are allowed to trigger internal redirect. This is ignored unless - // :ref:`internal_redirect_action - // ` - // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT - // ` - repeated uint32 redirect_response_codes = 3; + // Defines what upstream response codes are allowed to trigger internal redirect. If unspecified, + // only 302 will be treated as internal redirect. + // Only 301, 302, 303, 307 and 308 are valid values. + repeated uint32 redirect_response_codes = 2 + [(validate.rules).repeated = {items {uint32 {lte: 308 gte: 301}}}]; // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated Predicate predicates = 4; + repeated Predicate predicates = 3; - // Specifies what combinations of downstream and redirect target scheme are allowed to trigger an - // internal redirect. - repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; + // Allow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to not follow such redirects. + bool allow_cross_scheme_redirect = 4; } diff --git a/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD b/api/envoy/extensions/internal_redirect/allowlisted_routes/v3/BUILD similarity index 100% rename from api/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD rename to api/envoy/extensions/internal_redirect/allowlisted_routes/v3/BUILD diff --git a/api/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto b/api/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto new file mode 100644 index 000000000000..fd1eeb2b41d2 --- /dev/null +++ b/api/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package envoy.extensions.internal_redirect.allowlisted_routes.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.allowlisted_routes.v3"; +option java_outer_classname = "AllowlistedRoutesConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Allowlisted routes internal redirect predicate] + +// An internal redirect predicate that accepts only explicitly allowed target routes. +// [#extension: envoy.internal_redirect_predicates.allowlisted_routes] +message AllowlistedRoutesConfig { + // The list of routes that's allowed as redirect target by this predicate, + // identified by the route's :ref:`name `. + repeated string allowed_route_names = 1; +} diff --git a/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto b/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto deleted file mode 100644 index cfe9bdbe8bc5..000000000000 --- a/api/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; - -package envoy.extensions.internal_redirect.whitelisted_routes.v3; - -import "udpa/annotations/status.proto"; -import "udpa/annotations/versioning.proto"; - -option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.whitelisted_routes.v3"; -option java_outer_classname = "WhitelistedRoutesConfigProto"; -option java_multiple_files = true; -option (udpa.annotations.file_status).package_version_status = ACTIVE; - -// [#protodoc-title: Whitelisted routes internal redirect predicate] - -// An internal redirect predicate that accepts only whitelisted target routes. -// [#extension: envoy.internal_redirect_predicates.whitelisted_routes] -message WhitelistedRoutesConfig { - repeated string whitelisted_route_names = 1; -} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index a421833696fd..3336de491aa6 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -102,8 +102,8 @@ proto_library( "//envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/v3:pkg", "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", + "//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", - "//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", "//envoy/extensions/transport_sockets/alts/v3:pkg", diff --git a/docs/root/api-v3/config/internal_redirect/internal_redirect.rst b/docs/root/api-v3/config/internal_redirect/internal_redirect.rst index b89c53bfbdfb..5452e8accee7 100644 --- a/docs/root/api-v3/config/internal_redirect/internal_redirect.rst +++ b/docs/root/api-v3/config/internal_redirect/internal_redirect.rst @@ -1,5 +1,5 @@ Internal Redirect Predicates -===++++++++++++============= +============================ .. toctree:: :glob: diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index 0a02173a569a..b37b43fc444c 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -165,14 +165,14 @@ is subject to the redirect being handled by Envoy. For a redirect to be handled successfully it must pass the following checks: 1. Have a response code matching one of :ref:`redirect_response_codes - ` + `. 2. Have a *location* header with a valid, fully qualified URL matching the scheme of the original request. 3. The request must have been fully processed by Envoy. 4. The request must not have a body. -5. The scheme pair of the downstream request and the *location* header is allowed by - :ref:`allowed downstream and target scheme pair - ` +5. The scheme of the downstream request and the *location* header are the same or + :ref:`allow_cross_scheme_redirect + ` is true. 6. The number of previously handled internal redirect within a given downstream request does not exceed :ref:`max internal redirects ` @@ -195,9 +195,9 @@ will cause the redirect to be passed downstream. Two predicates can be used to create a DAG that defines the redirect chain, the :ref:`previous routes ` predicate, and -the :ref:`whitelisted routes -`. -Specifically, the *whitelisted routes* predicate defines edges of individual node in the DAG +the :ref:`allowlisted_routes +`. +Specifically, the *allowlisted routes* predicate defines edges of individual node in the DAG and the *previous routes* predicate defines "visited" state of the edges, so that loop can be avoided if so desired. diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 307abb358992..8e190a0539ab 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -961,6 +961,10 @@ message RouteAction { string cluster = 1 [(validate.rules).string = {min_bytes: 1}]; + // If present, Envoy will try to follow an upstream redirect response instead of proxying the + // response back to the downstream. An upstream redirect response is defined + // by :ref:`redirect_response_codes + // `. string cluster_header = 2 [(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME strict: false}]; @@ -1587,72 +1591,33 @@ message QueryParameterMatcher { } // HTTP Internal Redirect :ref:`architecture overview `. -// [#next-free-field: 6] message InternalRedirectPolicy { - // Configures :ref:`internal redirect ` behavior. - enum InternalRedirectAction { - // Redirect responses are proxied to downstream without triggering Internal Redirect. - // This is the default value. - PASS_THROUGH_INTERNAL_REDIRECT = 0; - - // Redirect responses (specified via :ref:`redirect_response_codes - // `), - // are followed by Envoy to the new URL. The redirect responses are not visible to - // the downstream unless the redirect following is rejected by other criteria. - HANDLE_INTERNAL_REDIRECT = 1; - } - - // HTTP scheme combinations that are allowed to be handled as internal - // redirect. Downstream scheme comes first and redirect target URL scheme - // follows. - enum DownstreamAndRedirectTargetSchemePair { - HTTP_HTTP = 0; - HTTP_HTTPS = 1; - HTTPS_HTTP = 2; - HTTPS_HTTPS = 3; - } - message Predicate { - string name = 1 [(validate.rules).string = {min_bytes: 1}]; - - google.protobuf.Any typed_config = 2; + google.protobuf.Any typed_config = 1; } - InternalRedirectAction internal_redirect_action = 1; - - // An internal redirect is handled, iff the number of previous internal redirects that a - // downstream request has encountered is lower than this value, and - // :ref:`internal_redirect_action - // ` - // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT - // ` + // An internal redirect is not handled, unless the number of previous internal redirects that a + // downstream request has encountered is lower than this value. // In the case where a downstream request is bounced among multiple routes by internal redirect, - // the first route that hits this threshold, or has - // :ref:`internal_redirect_action - // ` - // set to - // :ref:`PASS_THROUGH_INTERNAL_REDIRECT - // ` + // the first route that hits this threshold, or does not set :ref:`internal_redirect_policy + // ` // will pass the redirect back to downstream. // // If not specified, at most one redirect will be followed. - google.protobuf.UInt32Value max_internal_redirects = 2; + google.protobuf.UInt32Value max_internal_redirects = 1; - // Defines what upstream response codes are allowed to trigger internal redirect. This is ignored unless - // :ref:`internal_redirect_action - // ` - // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT - // ` - repeated uint32 redirect_response_codes = 3; + // Defines what upstream response codes are allowed to trigger internal redirect. If unspecified, + // only 302 will be treated as internal redirect. + // Only 301, 302, 303, 307 and 308 are valid values. + repeated uint32 redirect_response_codes = 2 + [(validate.rules).repeated = {items {uint32 {lte: 308 gte: 301}}}]; // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated Predicate predicates = 4; + repeated Predicate predicates = 3; - // Specifies what combinations of downstream and redirect target scheme are allowed to trigger an - // internal redirect. - repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; + // Allow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to not follow such redirects. + bool allow_cross_scheme_redirect = 4; } diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index e705942f404f..adc8fddf1a0f 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -959,6 +959,10 @@ message RouteAction { repeated UpgradeConfig upgrade_configs = 25; + // If present, Envoy will try to follow an upstream redirect response instead of proxying the + // response back to the downstream. An upstream redirect response is defined + // by :ref:`redirect_response_codes + // `. InternalRedirectPolicy internal_redirect_policy = 34; InternalRedirectAction hidden_envoy_deprecated_internal_redirect_action = 26 [deprecated = true]; @@ -1574,78 +1578,39 @@ message QueryParameterMatcher { } // HTTP Internal Redirect :ref:`architecture overview `. -// [#next-free-field: 6] message InternalRedirectPolicy { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.InternalRedirectPolicy"; - // Configures :ref:`internal redirect ` behavior. - enum InternalRedirectAction { - // Redirect responses are proxied to downstream without triggering Internal Redirect. - // This is the default value. - PASS_THROUGH_INTERNAL_REDIRECT = 0; - - // Redirect responses (specified via :ref:`redirect_response_codes - // `), - // are followed by Envoy to the new URL. The redirect responses are not visible to - // the downstream unless the redirect following is rejected by other criteria. - HANDLE_INTERNAL_REDIRECT = 1; - } - - // HTTP scheme combinations that are allowed to be handled as internal - // redirect. Downstream scheme comes first and redirect target URL scheme - // follows. - enum DownstreamAndRedirectTargetSchemePair { - HTTP_HTTP = 0; - HTTP_HTTPS = 1; - HTTPS_HTTP = 2; - HTTPS_HTTPS = 3; - } - message Predicate { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.InternalRedirectPolicy.Predicate"; - string name = 1 [(validate.rules).string = {min_bytes: 1}]; - - google.protobuf.Any typed_config = 2; + google.protobuf.Any typed_config = 1; } - InternalRedirectAction internal_redirect_action = 1; - - // An internal redirect is handled, iff the number of previous internal redirects that a - // downstream request has encountered is lower than this value, and - // :ref:`internal_redirect_action - // ` - // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT - // ` + // An internal redirect is not handled, unless the number of previous internal redirects that a + // downstream request has encountered is lower than this value. // In the case where a downstream request is bounced among multiple routes by internal redirect, - // the first route that hits this threshold, or has - // :ref:`internal_redirect_action - // ` - // set to - // :ref:`PASS_THROUGH_INTERNAL_REDIRECT - // ` + // the first route that hits this threshold, or does not set :ref:`internal_redirect_policy + // ` // will pass the redirect back to downstream. // // If not specified, at most one redirect will be followed. - google.protobuf.UInt32Value max_internal_redirects = 2; + google.protobuf.UInt32Value max_internal_redirects = 1; - // Defines what upstream response codes are allowed to trigger internal redirect. This is ignored unless - // :ref:`internal_redirect_action - // ` - // is set to - // :ref:`HANDLE_INTERNAL_REDIRECT - // ` - repeated uint32 redirect_response_codes = 3; + // Defines what upstream response codes are allowed to trigger internal redirect. If unspecified, + // only 302 will be treated as internal redirect. + // Only 301, 302, 303, 307 and 308 are valid values. + repeated uint32 redirect_response_codes = 2 + [(validate.rules).repeated = {items {uint32 {lte: 308 gte: 301}}}]; // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated Predicate predicates = 4; + repeated Predicate predicates = 3; - // Specifies what combinations of downstream and redirect target scheme are allowed to trigger an - // internal redirect. - repeated DownstreamAndRedirectTargetSchemePair allowed_downstream_and_target_scheme_pairs = 5; + // Allow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to not follow such redirects. + bool allow_cross_scheme_redirect = 4; } diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD b/generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/BUILD similarity index 100% rename from generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/BUILD rename to generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/BUILD diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto new file mode 100644 index 000000000000..fd1eeb2b41d2 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package envoy.extensions.internal_redirect.allowlisted_routes.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.allowlisted_routes.v3"; +option java_outer_classname = "AllowlistedRoutesConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: Allowlisted routes internal redirect predicate] + +// An internal redirect predicate that accepts only explicitly allowed target routes. +// [#extension: envoy.internal_redirect_predicates.allowlisted_routes] +message AllowlistedRoutesConfig { + // The list of routes that's allowed as redirect target by this predicate, + // identified by the route's :ref:`name `. + repeated string allowed_route_names = 1; +} diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto deleted file mode 100644 index cfe9bdbe8bc5..000000000000 --- a/generated_api_shadow/envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.proto +++ /dev/null @@ -1,19 +0,0 @@ -syntax = "proto3"; - -package envoy.extensions.internal_redirect.whitelisted_routes.v3; - -import "udpa/annotations/status.proto"; -import "udpa/annotations/versioning.proto"; - -option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.whitelisted_routes.v3"; -option java_outer_classname = "WhitelistedRoutesConfigProto"; -option java_multiple_files = true; -option (udpa.annotations.file_status).package_version_status = ACTIVE; - -// [#protodoc-title: Whitelisted routes internal redirect predicate] - -// An internal redirect predicate that accepts only whitelisted target routes. -// [#extension: envoy.internal_redirect_predicates.whitelisted_routes] -message WhitelistedRoutesConfig { - repeated string whitelisted_route_names = 1; -} diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index 7818dca92c9c..a73c7c16eef8 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -267,11 +267,10 @@ class InternalRedirectPolicy { virtual uint32_t maxInternalRedirects() const PURE; /** - * @return if the pair of HTTP scheme from the downstream request and the target url is allowed - * for internal redirect. + * @return if it is allowed to follow the redirect with a different scheme in + * the target URI than the downstream request. */ - virtual bool isDownstreamAndRedirectTargetSchemePairAllowed(bool downstream_is_https, - bool target_is_https) const PURE; + virtual bool isCrossSchemeRedirectAllowed() const PURE; }; /** diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 6d857c38e5ef..4112d889f0a4 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -147,15 +147,18 @@ Upstream::RetryPrioritySharedPtr RetryPolicyImpl::retryPriority() const { InternalRedirectPolicyImpl::InternalRedirectPolicyImpl( const envoy::config::route::v3::InternalRedirectPolicy& policy_config, ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) - : enabled_(internalRedirectEnabled(policy_config)), current_route_name_(current_route_name), + : enabled_(true), current_route_name_(current_route_name), redirect_response_codes_(buildRedirectResponseCodes(policy_config)), max_internal_redirects_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(policy_config, max_internal_redirects, 1)), - allowed_scheme_pairs_(buildAllowedSchemePairs(policy_config)) { + allow_cross_scheme_redirect_(policy_config.allow_cross_scheme_redirect()) { for (const auto& predicate : policy_config.predicates()) { - auto& factory = - Envoy::Config::Utility::getAndCheckFactory(predicate); - auto config = factory.createEmptyConfigProto(); + const std::string type{ + TypeUtil::typeUrlToDescriptorFullName(predicate.typed_config().type_url())}; + auto* factory = + Registry::FactoryRegistry::getFactoryByType(type); + + auto config = factory->createEmptyConfigProto(); Envoy::Config::Utility::translateOpaqueConfig(predicate.typed_config(), {}, validator, *config); predicate_factories_.emplace_back(factory, std::move(config)); } @@ -164,29 +167,12 @@ InternalRedirectPolicyImpl::InternalRedirectPolicyImpl( std::vector InternalRedirectPolicyImpl::predicates() const { std::vector predicates; for (const auto& predicate_factory : predicate_factories_) { - predicates.emplace_back(predicate_factory.first.createInternalRedirectPredicate( + predicates.emplace_back(predicate_factory.first->createInternalRedirectPredicate( *predicate_factory.second, current_route_name_)); } return predicates; } -bool InternalRedirectPolicyImpl::isDownstreamAndRedirectTargetSchemePairAllowed( - bool downstream_is_https, bool target_is_https) const { - return allowed_scheme_pairs_.contains(compactSchemePair(downstream_is_https, target_is_https)); -} - -bool InternalRedirectPolicyImpl::internalRedirectEnabled( - const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const { - switch (policy_config.internal_redirect_action()) { - case envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT: - return true; - case envoy::config::route::v3::InternalRedirectPolicy::PASS_THROUGH_INTERNAL_REDIRECT: - return false; - default: - return false; - } -} - absl::flat_hash_set InternalRedirectPolicyImpl::buildRedirectResponseCodes( const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const { if (policy_config.redirect_response_codes_size() == 0) { @@ -200,23 +186,6 @@ absl::flat_hash_set InternalRedirectPolicyImpl::buildRedirectRespons return ret; } -absl::flat_hash_set InternalRedirectPolicyImpl::buildAllowedSchemePairs( - const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const { - if (policy_config.allowed_downstream_and_target_scheme_pairs_size() == 0) { - return absl::flat_hash_set{compactSchemePair(true, true), - compactSchemePair(true, false), - compactSchemePair(false, false)}; - } - return absl::flat_hash_set( - policy_config.allowed_downstream_and_target_scheme_pairs().begin(), - policy_config.allowed_downstream_and_target_scheme_pairs().end()); -} - -uint8_t InternalRedirectPolicyImpl::compactSchemePair(bool downstream_is_https, - bool target_is_https) const { - return (downstream_is_https ? 0x2 : 0x0) | (target_is_https ? 0x1 : 0x0); -} - CorsPolicyImpl::CorsPolicyImpl(const envoy::config::route::v3::CorsPolicy& config, Runtime::Loader& loader) : config_(config), loader_(loader), allow_methods_(config.allow_methods()), @@ -773,16 +742,11 @@ InternalRedirectPolicyImpl RouteEntryImplBase::buildInternalRedirectPolicy( envoy::config::route::v3::InternalRedirectPolicy policy_config; switch (route_config.internal_redirect_action()) { case envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT: - policy_config.set_internal_redirect_action( - envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); break; case envoy::config::route::v3::RouteAction::PASS_THROUGH_INTERNAL_REDIRECT: - policy_config.set_internal_redirect_action( - envoy::config::route::v3::InternalRedirectPolicy::PASS_THROUGH_INTERNAL_REDIRECT); - break; + return InternalRedirectPolicyImpl(); default: - policy_config.set_internal_redirect_action( - envoy::config::route::v3::InternalRedirectPolicy::PASS_THROUGH_INTERNAL_REDIRECT); + return InternalRedirectPolicyImpl(); } if (route_config.has_max_internal_redirects()) { *policy_config.mutable_max_internal_redirects() = route_config.max_internal_redirects(); diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index d1ed06f9669f..7c6a367d4719 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -402,28 +402,19 @@ class InternalRedirectPolicyImpl : public InternalRedirectPolicy { uint32_t maxInternalRedirects() const override { return max_internal_redirects_; } - bool isDownstreamAndRedirectTargetSchemePairAllowed(bool downstream_is_https, - bool target_is_https) const override; + bool isCrossSchemeRedirectAllowed() const override { return allow_cross_scheme_redirect_; } private: - bool internalRedirectEnabled( - const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const; - absl::flat_hash_set buildRedirectResponseCodes( const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const; - absl::flat_hash_set buildAllowedSchemePairs( - const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const; - - uint8_t compactSchemePair(bool downstream_is_https, bool target_is_https) const; - const bool enabled_{false}; const std::string current_route_name_; const absl::flat_hash_set redirect_response_codes_; const uint32_t max_internal_redirects_{1}; - const absl::flat_hash_set allowed_scheme_pairs_; + const bool allow_cross_scheme_redirect_{false}; - std::vector> + std::vector> predicate_factories_; }; diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 4de404510bc9..8e31f4f9c17e 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -46,17 +46,17 @@ constexpr char NumInternalRedirectsFilterStateName[] = "num_internal_redirects"; uint32_t getLength(const Buffer::Instance* instance) { return instance ? instance->length() : 0; } -bool schemeIsHttps(const Http::RequestHeaderMap& downstream_headers, - const Network::Connection& connection) { - if (!connection.ssl()) { - return false; - } +bool schemeIsHttp(const Http::RequestHeaderMap& downstream_headers, + const Network::Connection& connection) { if (downstream_headers.ForwardedProto() && downstream_headers.ForwardedProto()->value().getStringView() == Http::Headers::get().SchemeValues.Http) { - return false; + return true; } - return true; + if (!connection.ssl()) { + return true; + } + return false; } bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream_headers, @@ -77,11 +77,10 @@ bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream return false; } - // Don't allow serving TLS responses over plaintext. - bool downstream_is_https = schemeIsHttps(downstream_headers, connection); - bool target_is_https = absolute_url.scheme() == Http::Headers::get().SchemeValues.Https; - if (!policy.isDownstreamAndRedirectTargetSchemePairAllowed(downstream_is_https, - target_is_https)) { + // Don't allow serving TLS responses over plaintext unless allowed by policy. + bool scheme_is_http = schemeIsHttp(downstream_headers, connection); + bool target_is_http = absolute_url.scheme() == Http::Headers::get().SchemeValues.Http; + if (!policy.isCrossSchemeRedirectAllowed() && scheme_is_http != target_is_http) { return false; } @@ -103,8 +102,8 @@ bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream // Preserve the original request URL for the second pass. downstream_headers.setEnvoyOriginalUrl( - absl::StrCat(downstream_is_https ? Http::Headers::get().SchemeValues.Https - : Http::Headers::get().SchemeValues.Http, + absl::StrCat(scheme_is_http ? Http::Headers::get().SchemeValues.Http + : Http::Headers::get().SchemeValues.Https, "://", downstream_headers.Host()->value().getStringView(), downstream_headers.Path()->value().getStringView())); diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 97cf7c037c97..ae8cb1f5f86a 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -174,6 +174,6 @@ EXTENSIONS = { # Internal redirect predicates # + "envoy.internal_redirect_predicates.allowlisted_routes": "//source/extensions/internal_redirect/allowlisted_routes:config", "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", - "envoy.internal_redirect_predicates.whitelisted_routes": "//source/extensions/internal_redirect/whitelisted_routes:config", } diff --git a/source/extensions/internal_redirect/whitelisted_routes/BUILD b/source/extensions/internal_redirect/allowlisted_routes/BUILD similarity index 75% rename from source/extensions/internal_redirect/whitelisted_routes/BUILD rename to source/extensions/internal_redirect/allowlisted_routes/BUILD index 6ce7f251edae..5db35c8802b8 100644 --- a/source/extensions/internal_redirect/whitelisted_routes/BUILD +++ b/source/extensions/internal_redirect/allowlisted_routes/BUILD @@ -10,12 +10,12 @@ load( envoy_package() envoy_cc_library( - name = "whitelisted_routes_lib", - hdrs = ["whitelisted_routes.h"], + name = "allowlisted_routes_lib", + hdrs = ["allowlisted_routes.h"], deps = [ "//include/envoy/router:internal_redirect_interface", "//include/envoy/stream_info:filter_state_interface", - "@envoy_api//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg_cc_proto", ], ) @@ -25,10 +25,10 @@ envoy_cc_extension( hdrs = ["config.h"], security_posture = "robust_to_untrusted_downstream_and_upstream", deps = [ - ":whitelisted_routes_lib", + ":allowlisted_routes_lib", "//include/envoy/registry", "//include/envoy/router:internal_redirect_interface", "//source/extensions/internal_redirect:well_known_names", - "@envoy_api//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h b/source/extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h new file mode 100644 index 000000000000..c4f7881350ef --- /dev/null +++ b/source/extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h @@ -0,0 +1,32 @@ +#pragma once + +#include "envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.pb.h" +#include "envoy/router/internal_redirect.h" +#include "envoy/stream_info/filter_state.h" + +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +class AllowlistedRoutesPredicate : public Router::InternalRedirectPredicate { +public: + AllowlistedRoutesPredicate( + absl::string_view, + const envoy::extensions::internal_redirect::allowlisted_routes::v3::AllowlistedRoutesConfig& + config) + : allowed_routes_(config.allowed_route_names().begin(), config.allowed_route_names().end()) {} + + bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name) override { + return allowed_routes_.contains(route_name); + } + +private: + const absl::flat_hash_set allowed_routes_; +}; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/whitelisted_routes/config.cc b/source/extensions/internal_redirect/allowlisted_routes/config.cc similarity index 67% rename from source/extensions/internal_redirect/whitelisted_routes/config.cc rename to source/extensions/internal_redirect/allowlisted_routes/config.cc index 5906f48d3e89..726e0da862b8 100644 --- a/source/extensions/internal_redirect/whitelisted_routes/config.cc +++ b/source/extensions/internal_redirect/allowlisted_routes/config.cc @@ -1,4 +1,4 @@ -#include "extensions/internal_redirect/whitelisted_routes/config.h" +#include "extensions/internal_redirect/allowlisted_routes/config.h" #include "envoy/registry/registry.h" #include "envoy/router/internal_redirect.h" @@ -7,7 +7,7 @@ namespace Envoy { namespace Extensions { namespace InternalRedirect { -REGISTER_FACTORY(WhitelistedRoutesPredicateFactory, Router::InternalRedirectPredicateFactory); +REGISTER_FACTORY(AllowlistedRoutesPredicateFactory, Router::InternalRedirectPredicateFactory); } // namespace InternalRedirect } // namespace Extensions diff --git a/source/extensions/internal_redirect/whitelisted_routes/config.h b/source/extensions/internal_redirect/allowlisted_routes/config.h similarity index 55% rename from source/extensions/internal_redirect/whitelisted_routes/config.h rename to source/extensions/internal_redirect/allowlisted_routes/config.h index 6fadc725a86f..3e11d7d19275 100644 --- a/source/extensions/internal_redirect/whitelisted_routes/config.h +++ b/source/extensions/internal_redirect/allowlisted_routes/config.h @@ -1,39 +1,39 @@ #pragma once -#include "envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.pb.h" -#include "envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.pb.validate.h" +#include "envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.pb.validate.h" #include "envoy/router/internal_redirect.h" #include "common/protobuf/message_validator_impl.h" #include "common/protobuf/utility.h" #include "extensions/internal_redirect/well_known_names.h" -#include "extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h" +#include "extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h" namespace Envoy { namespace Extensions { namespace InternalRedirect { -class WhitelistedRoutesPredicateFactory : public Router::InternalRedirectPredicateFactory { +class AllowlistedRoutesPredicateFactory : public Router::InternalRedirectPredicateFactory { public: Router::InternalRedirectPredicateSharedPtr createInternalRedirectPredicate(const Protobuf::Message& config, absl::string_view current_route_name) override { - auto whitelisted_routes_config = + auto allowlisted_routes_config = MessageUtil::downcastAndValidate( + allowlisted_routes::v3::AllowlistedRoutesConfig&>( config, ProtobufMessage::getStrictValidationVisitor()); - return std::make_shared(current_route_name, - whitelisted_routes_config); + return std::make_shared(current_route_name, + allowlisted_routes_config); } std::string name() const override { - return InternalRedirectPredicateValues::get().WhitelistedRoutesPredicate; + return InternalRedirectPredicateValues::get().AllowlistedRoutesPredicate; } ProtobufTypes::MessagePtr createEmptyConfigProto() override { return std::make_unique< - envoy::extensions::internal_redirect::whitelisted_routes::v3::WhitelistedRoutesConfig>(); + envoy::extensions::internal_redirect::allowlisted_routes::v3::AllowlistedRoutesConfig>(); } }; diff --git a/source/extensions/internal_redirect/well_known_names.h b/source/extensions/internal_redirect/well_known_names.h index 443531de3108..a89acb589bbe 100644 --- a/source/extensions/internal_redirect/well_known_names.h +++ b/source/extensions/internal_redirect/well_known_names.h @@ -14,8 +14,8 @@ namespace InternalRedirect { class InternalRedirectPredicatesNameValues { public: const std::string PreviousRoutesPredicate = "envoy.internal_redirect_predicates.previous_routes"; - const std::string WhitelistedRoutesPredicate = - "envoy.internal_redirect_predicates.whitelisted_routes"; + const std::string AllowlistedRoutesPredicate = + "envoy.internal_redirect_predicates.allowlisted_routes"; }; using InternalRedirectPredicateValues = ConstSingleton; diff --git a/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h b/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h deleted file mode 100644 index d9be53d798a9..000000000000 --- a/source/extensions/internal_redirect/whitelisted_routes/whitelisted_routes.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.pb.h" -#include "envoy/router/internal_redirect.h" -#include "envoy/stream_info/filter_state.h" - -#include "absl/container/flat_hash_set.h" -#include "absl/strings/string_view.h" - -namespace Envoy { -namespace Extensions { -namespace InternalRedirect { - -class WhitelistedRoutesPredicate : public Router::InternalRedirectPredicate { -public: - WhitelistedRoutesPredicate( - absl::string_view, - const envoy::extensions::internal_redirect::whitelisted_routes::v3::WhitelistedRoutesConfig& - config) - : whitelisted_routes_(config.whitelisted_route_names().begin(), - config.whitelisted_route_names().end()) {} - - bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name) override { - return whitelisted_routes_.contains(route_name); - } - -private: - const absl::flat_hash_set whitelisted_routes_; -}; - -} // namespace InternalRedirect -} // namespace Extensions -} // namespace Envoy diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index a23cdd93811a..6e07b6514b46 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6749,6 +6749,30 @@ name: RetriableStatusCodes EXPECT_NE(predicates1, predicates2); } +TEST_F(RouteConfigurationV2, InternalRedirctIsDisabledWhenNotSpecifiedInRouteAction) { + const std::string InternalRedirectEnabled = R"EOF( +name: InternalRedirectEnabled +virtual_hosts: + - name: regex + domains: [idle.lyft.com] + routes: + - match: + safe_regex: + google_re2: {} + regex: "/regex" + route: + cluster: some-cluster + )EOF"; + + TestConfigImpl config(parseRouteConfigurationFromV2Yaml(InternalRedirectEnabled), + factory_context_, true); + Http::TestRequestHeaderMapImpl headers = + genRedirectHeaders("idle.lyft.com", "/regex", true, false); + const auto& internal_redirect_policy = + config.route(headers, 0)->routeEntry()->internalRedirectPolicy(); + EXPECT_FALSE(internal_redirect_policy.enabled()); +} + TEST_F(RouteConfigurationV2, DefaultInternalRedirctPolicyIsSensible) { const std::string InternalRedirectEnabled = R"EOF( name: InternalRedirectEnabled @@ -6762,9 +6786,7 @@ name: InternalRedirectEnabled regex: "/regex" route: cluster: some-cluster - internal_redirect_policy: - internal_redirect_action: HANDLE_INTERNAL_REDIRECT - + internal_redirect_policy: {} )EOF"; TestConfigImpl config(parseRouteConfigurationFromV2Yaml(InternalRedirectEnabled), @@ -6778,12 +6800,7 @@ name: InternalRedirectEnabled EXPECT_FALSE(internal_redirect_policy.shouldRedirectForCode(static_cast(200))); EXPECT_EQ(1, internal_redirect_policy.maxInternalRedirects()); EXPECT_TRUE(internal_redirect_policy.predicates().empty()); - EXPECT_TRUE(internal_redirect_policy.isDownstreamAndRedirectTargetSchemePairAllowed(true, true)); - EXPECT_TRUE(internal_redirect_policy.isDownstreamAndRedirectTargetSchemePairAllowed(true, false)); - EXPECT_TRUE( - internal_redirect_policy.isDownstreamAndRedirectTargetSchemePairAllowed(false, false)); - EXPECT_FALSE( - internal_redirect_policy.isDownstreamAndRedirectTargetSchemePairAllowed(false, true)); + EXPECT_FALSE(internal_redirect_policy.isCrossSchemeRedirectAllowed()); } class PerFilterConfigsTest : public testing::Test, public ConfigImplTestBase { diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index b3876553e745..731c1657cd30 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -296,8 +296,8 @@ class RouterTestBase : public testing::Test { ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, maxInternalRedirects()) .WillByDefault(Return(max_internal_redirects)); ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, - isDownstreamAndRedirectTargetSchemePairAllowed(_, _)) - .WillByDefault(Return(true)); + isCrossSchemeRedirectAllowed()) + .WillByDefault(Return(false)); ON_CALL(callbacks_, connection()).WillByDefault(Return(&connection_)); } @@ -4379,16 +4379,13 @@ TEST_F(RouterTest, InternalRedirectRejectedWithBody) { .value()); } -TEST_F(RouterTest, InternalRedirectRejectedByDownstreamAndTargetSchemePair) { +TEST_F(RouterTest, InternalRedirectRejectedByCrossSchemeRedirect) { enableRedirects(); sendRequest(); redirect_headers_->setLocation("https://www.foo.com"); - EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, - isDownstreamAndRedirectTargetSchemePairAllowed(false, true)) - .WillOnce(Return(false)); EXPECT_CALL(callbacks_, recreateStream()).Times(0); response_decoder_->decodeHeaders(std::move(redirect_headers_), true); @@ -4397,13 +4394,39 @@ TEST_F(RouterTest, InternalRedirectRejectedByDownstreamAndTargetSchemePair) { .value()); } -TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { +TEST_F(RouterTest, CrossSchemeRedirectAllowedByPolicy) { enableRedirects(); sendRequest(); redirect_headers_->setLocation("https://www.foo.com"); + EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, + isCrossSchemeRedirectAllowed()) + .WillOnce(Return(true)); + EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(callbacks_, recreateStream()).Times(1).WillOnce(Return(true)); + response_decoder_->decodeHeaders(std::move(redirect_headers_), false); + EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ + .counter("upstream_internal_redirect_succeeded_total") + .value()); + + // In production, the HCM recreateStream would have called this. + router_.onDestroy(); + EXPECT_EQ(1, callbacks_.streamInfo() + .filterState() + ->getDataMutable("num_internal_redirects") + .value()); +} + +TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { + enableRedirects(); + + sendRequest(); + + redirect_headers_->setLocation("http://www.foo.com"); + auto mock_predicate = std::make_shared(); EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); diff --git a/test/integration/BUILD b/test/integration/BUILD index 768d944bc2f8..5de4cbc50eda 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -612,12 +612,13 @@ envoy_cc_test( deps = [ ":http_protocol_integration_lib", "//source/common/http:header_map_lib", + "//source/extensions/internal_redirect/allowlisted_routes:config", "//source/extensions/internal_redirect/previous_routes:config", - "//source/extensions/internal_redirect/whitelisted_routes:config", "//test/test_common:utility_lib", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/internal_redirect/whitelisted_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/previous_routes/v3:pkg_cc_proto", ], ) diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index 64d187981e50..6c8481ac79f5 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -1,6 +1,7 @@ #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" -#include "envoy/extensions/internal_redirect/whitelisted_routes/v3/whitelisted_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.pb.h" #include "test/integration/http_protocol_integration.h" @@ -23,9 +24,7 @@ class RedirectIntegrationTest : public HttpProtocolIntegrationTest { handle.mutable_routes(0)->set_name("redirect"); handle.mutable_routes(0) ->mutable_route() - ->mutable_internal_redirect_policy() - ->set_internal_redirect_action( - envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); + ->mutable_internal_redirect_policy(); config_helper_.addVirtualHost(handle); auto handle_max_3_hop = @@ -33,9 +32,7 @@ class RedirectIntegrationTest : public HttpProtocolIntegrationTest { handle_max_3_hop.mutable_routes(0)->set_name("max_three_hop"); handle_max_3_hop.mutable_routes(0) ->mutable_route() - ->mutable_internal_redirect_policy() - ->set_internal_redirect_action( - envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); + ->mutable_internal_redirect_policy(); handle_max_3_hop.mutable_routes(0) ->mutable_route() ->mutable_internal_redirect_policy() @@ -236,11 +233,11 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByPreviousRoutesPredica auto* internal_redirect_policy = handle_prevent_repeated_target.mutable_routes(0) ->mutable_route() ->mutable_internal_redirect_policy(); - internal_redirect_policy->set_internal_redirect_action( - envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); - internal_redirect_policy->add_predicates()->set_name( - "envoy.internal_redirect_predicates.previous_routes"); internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); + envoy::extensions::internal_redirect::previous_routes::v3::PreviousRoutesConfig + previous_routes_config; + internal_redirect_policy->add_predicates()->mutable_typed_config()->PackFrom( + previous_routes_config); config_helper_.addVirtualHost(handle_prevent_repeated_target); // Validate that header sanitization is only called once. @@ -280,25 +277,22 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByPreviousRoutesPredica ->value()); } -TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByWhitelistedRoutesPredicate) { - auto handle_whitelisted_redirect_route = - config_helper_.createVirtualHost("handle.internal.redirect.only.whitelisted.target"); - auto* internal_redirect_policy = handle_whitelisted_redirect_route.mutable_routes(0) +TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowlistedRoutesPredicate) { + auto handle_allowlisted_redirect_route = + config_helper_.createVirtualHost("handle.internal.redirect.only.allowlisted.target"); + auto* internal_redirect_policy = handle_allowlisted_redirect_route.mutable_routes(0) ->mutable_route() ->mutable_internal_redirect_policy(); - internal_redirect_policy->set_internal_redirect_action( - envoy::config::route::v3::InternalRedirectPolicy::HANDLE_INTERNAL_REDIRECT); - auto* whitelisted_routes_predicate = internal_redirect_policy->add_predicates(); - whitelisted_routes_predicate->set_name("envoy.internal_redirect_predicates.whitelisted_routes"); - envoy::extensions::internal_redirect::whitelisted_routes::v3::WhitelistedRoutesConfig - whitelisted_routes_config; - *whitelisted_routes_config.add_whitelisted_route_names() = "max_three_hop"; - whitelisted_routes_predicate->mutable_typed_config()->PackFrom(whitelisted_routes_config); + auto* allowlisted_routes_predicate = internal_redirect_policy->add_predicates(); + envoy::extensions::internal_redirect::allowlisted_routes::v3::AllowlistedRoutesConfig + allowlisted_routes_config; + *allowlisted_routes_config.add_allowed_route_names() = "max_three_hop"; + allowlisted_routes_predicate->mutable_typed_config()->PackFrom(allowlisted_routes_config); internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); - config_helper_.addVirtualHost(handle_whitelisted_redirect_route); + config_helper_.addVirtualHost(handle_allowlisted_redirect_route); // Validate that header sanitization is only called once. config_helper_.addConfigModifier( @@ -309,7 +303,7 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByWhitelistedRoutesPred codec_client_ = makeHttpConnection(lookupPort("http")); - default_request_headers_.setHost("handle.internal.redirect.only.whitelisted.target"); + default_request_headers_.setHost("handle.internal.redirect.only.allowlisted.target"); IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); @@ -321,11 +315,11 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByWhitelistedRoutesPred auto second_request = waitForNextStream(); // Redirect back to the original route. redirect_response_.setLocation( - "http://handle.internal.redirect.only.whitelisted.target/another/path"); + "http://handle.internal.redirect.only.allowlisted.target/another/path"); second_request->encodeHeaders(redirect_response_, true); auto third_request = waitForNextStream(); - // Redirect to the non-whitelisted route. This should fail. + // Redirect to the non-allowlisted route. This should fail. redirect_response_.setLocation("http://handle.internal.redirect/yet/another/path"); third_request->encodeHeaders(redirect_response_, true); diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index b835901263ee..00f153c9fb2f 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -138,8 +138,7 @@ class MockInternalRedirectPolicy : public InternalRedirectPolicy { MOCK_METHOD(bool, shouldRedirectForCode, (const Http::Code& response_code), (const)); MOCK_METHOD(std::vector, predicates, (), (const)); MOCK_METHOD(uint32_t, maxInternalRedirects, (), (const)); - MOCK_METHOD(bool, isDownstreamAndRedirectTargetSchemePairAllowed, - (bool downstream_is_https, bool target_is_https), (const)); + MOCK_METHOD(bool, isCrossSchemeRedirectAllowed, (), (const)); }; class MockInternalRedirectPredicate : public InternalRedirectPredicate { From b5a1f54c51a795c9b5b1b166f215608a4c0720d4 Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 29 Apr 2020 15:06:00 -0400 Subject: [PATCH 21/46] Fix format. Signed-off-by: pengg --- .../internal_redirect/allowlisted_routes/config.h | 2 +- test/integration/redirect_integration_test.cc | 8 ++------ tools/spelling/spelling_dictionary.txt | 2 ++ 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/source/extensions/internal_redirect/allowlisted_routes/config.h b/source/extensions/internal_redirect/allowlisted_routes/config.h index 3e11d7d19275..91c0881c93eb 100644 --- a/source/extensions/internal_redirect/allowlisted_routes/config.h +++ b/source/extensions/internal_redirect/allowlisted_routes/config.h @@ -7,8 +7,8 @@ #include "common/protobuf/message_validator_impl.h" #include "common/protobuf/utility.h" -#include "extensions/internal_redirect/well_known_names.h" #include "extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h" +#include "extensions/internal_redirect/well_known_names.h" namespace Envoy { namespace Extensions { diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index 6c8481ac79f5..0eb41a4e92ec 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -22,17 +22,13 @@ class RedirectIntegrationTest : public HttpProtocolIntegrationTest { auto handle = config_helper_.createVirtualHost("handle.internal.redirect"); handle.mutable_routes(0)->set_name("redirect"); - handle.mutable_routes(0) - ->mutable_route() - ->mutable_internal_redirect_policy(); + handle.mutable_routes(0)->mutable_route()->mutable_internal_redirect_policy(); config_helper_.addVirtualHost(handle); auto handle_max_3_hop = config_helper_.createVirtualHost("handle.internal.redirect.max.three.hop"); handle_max_3_hop.mutable_routes(0)->set_name("max_three_hop"); - handle_max_3_hop.mutable_routes(0) - ->mutable_route() - ->mutable_internal_redirect_policy(); + handle_max_3_hop.mutable_routes(0)->mutable_route()->mutable_internal_redirect_policy(); handle_max_3_hop.mutable_routes(0) ->mutable_route() ->mutable_internal_redirect_policy() diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 2bc47abeb63f..604563fc61d9 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -17,6 +17,7 @@ ASCII ASSERTs AST AWS +Allowlisted BACKTRACE BSON BPF @@ -359,6 +360,7 @@ alloc alloca allocator allowlist +allowlisted alls alphanumerics amongst From 23521a638b56fa1a813ad4c4474f3de80214b55c Mon Sep 17 00:00:00 2001 From: pengg Date: Thu, 30 Apr 2020 14:33:40 -0400 Subject: [PATCH 22/46] Address API comments. Signed-off-by: pengg --- api/envoy/config/route/v3/route_components.proto | 11 +++-------- .../config/route/v4alpha/route_components.proto | 14 +++----------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index fb2522738618..1a61d531ed3c 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1574,10 +1574,6 @@ message QueryParameterMatcher { // HTTP Internal Redirect :ref:`architecture overview `. message InternalRedirectPolicy { - message Predicate { - google.protobuf.Any typed_config = 1; - } - // An internal redirect is not handled, unless the number of previous internal redirects that a // downstream request has encountered is lower than this value. // In the case where a downstream request is bounced among multiple routes by internal redirect, @@ -1590,14 +1586,13 @@ message InternalRedirectPolicy { // Defines what upstream response codes are allowed to trigger internal redirect. If unspecified, // only 302 will be treated as internal redirect. - // Only 301, 302, 303, 307 and 308 are valid values. - repeated uint32 redirect_response_codes = 2 - [(validate.rules).repeated = {items {uint32 {lte: 308 gte: 301}}}]; + // Only 301, 302, 303, 307 and 308 are valid values. Any other codes will be ignored. + repeated uint32 redirect_response_codes = 2 [(validate.rules).repeated = {max_items: 5}]; // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated Predicate predicates = 3; + repeated google.protobuf.Any predicates = 3; // Allow internal redirect to follow a target URI with a different scheme than the value of // x-forwarded-proto. Default behavior is to not follow such redirects. diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index a6265383c8dc..6883a9d8a586 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1563,13 +1563,6 @@ message InternalRedirectPolicy { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.InternalRedirectPolicy"; - message Predicate { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.route.v3.InternalRedirectPolicy.Predicate"; - - google.protobuf.Any typed_config = 1; - } - // An internal redirect is not handled, unless the number of previous internal redirects that a // downstream request has encountered is lower than this value. // In the case where a downstream request is bounced among multiple routes by internal redirect, @@ -1582,14 +1575,13 @@ message InternalRedirectPolicy { // Defines what upstream response codes are allowed to trigger internal redirect. If unspecified, // only 302 will be treated as internal redirect. - // Only 301, 302, 303, 307 and 308 are valid values. - repeated uint32 redirect_response_codes = 2 - [(validate.rules).repeated = {items {uint32 {lte: 308 gte: 301}}}]; + // Only 301, 302, 303, 307 and 308 are valid values. Any other codes will be ignored. + repeated uint32 redirect_response_codes = 2 [(validate.rules).repeated = {max_items: 5}]; // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated Predicate predicates = 3; + repeated google.protobuf.Any predicates = 3; // Allow internal redirect to follow a target URI with a different scheme than the value of // x-forwarded-proto. Default behavior is to not follow such redirects. From dabc625c0d721f0fee1069b58fe5559606c05e69 Mon Sep 17 00:00:00 2001 From: pengg Date: Thu, 30 Apr 2020 16:53:35 -0400 Subject: [PATCH 23/46] Address code comments. allow_cross_scheme_redirect will be address separately in a different commit. Signed-off-by: pengg --- api/BUILD | 2 +- .../v3/BUILD | 0 .../v3/allow_listed_routes_config.proto} | 17 +++++----- api/versioning/BUILD | 2 +- .../http/http_connection_management.rst | 6 ++-- .../config/route/v3/route_components.proto | 11 ++----- .../route/v4alpha/route_components.proto | 14 ++------- .../v3/BUILD | 0 .../v3/allow_listed_routes_config.proto} | 17 +++++----- include/envoy/router/internal_redirect.h | 2 +- include/envoy/router/router.h | 2 +- source/common/router/BUILD | 1 + source/common/router/config_impl.cc | 17 +++++----- source/common/router/config_impl.h | 7 +++-- source/common/router/router.cc | 31 +++++++++++++------ source/extensions/extensions_build_config.bzl | 2 +- .../BUILD | 10 +++--- .../allow_listed_routes.h} | 8 ++--- .../config.cc | 4 +-- .../config.h | 20 ++++++------ .../previous_routes/previous_routes.cc | 4 +-- .../internal_redirect/well_known_names.h | 4 +-- test/common/router/config_impl_test.cc | 2 +- test/common/router/router_test.cc | 11 +++++-- test/integration/BUILD | 4 +-- test/integration/redirect_integration_test.cc | 31 +++++++++---------- test/mocks/router/mocks.h | 2 +- 27 files changed, 125 insertions(+), 106 deletions(-) rename api/envoy/extensions/internal_redirect/{allowlisted_routes => allow_listed_routes}/v3/BUILD (100%) rename api/envoy/extensions/internal_redirect/{allowlisted_routes/v3/allowlisted_routes_config.proto => allow_listed_routes/v3/allow_listed_routes_config.proto} (51%) rename generated_api_shadow/envoy/extensions/internal_redirect/{allowlisted_routes => allow_listed_routes}/v3/BUILD (100%) rename generated_api_shadow/envoy/extensions/internal_redirect/{allowlisted_routes/v3/allowlisted_routes_config.proto => allow_listed_routes/v3/allow_listed_routes_config.proto} (51%) rename source/extensions/internal_redirect/{allowlisted_routes => allow_listed_routes}/BUILD (69%) rename source/extensions/internal_redirect/{allowlisted_routes/allowlisted_routes.h => allow_listed_routes/allow_listed_routes.h} (70%) rename source/extensions/internal_redirect/{allowlisted_routes => allow_listed_routes}/config.cc (66%) rename source/extensions/internal_redirect/{allowlisted_routes => allow_listed_routes}/config.h (53%) diff --git a/api/BUILD b/api/BUILD index fdd260119367..8322ac6ae650 100644 --- a/api/BUILD +++ b/api/BUILD @@ -220,7 +220,7 @@ proto_library( "//envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/v3:pkg", "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", - "//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg", + "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", diff --git a/api/envoy/extensions/internal_redirect/allowlisted_routes/v3/BUILD b/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/BUILD similarity index 100% rename from api/envoy/extensions/internal_redirect/allowlisted_routes/v3/BUILD rename to api/envoy/extensions/internal_redirect/allow_listed_routes/v3/BUILD diff --git a/api/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto b/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto similarity index 51% rename from api/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto rename to api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto index fd1eeb2b41d2..026d727ddfaf 100644 --- a/api/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto +++ b/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto @@ -1,21 +1,24 @@ syntax = "proto3"; -package envoy.extensions.internal_redirect.allowlisted_routes.v3; +package envoy.extensions.internal_redirect.allow_listed_routes.v3; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.allowlisted_routes.v3"; -option java_outer_classname = "AllowlistedRoutesConfigProto"; +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.allow_listed_routes.v3"; +option java_outer_classname = "AllowListedRoutesConfigProto"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; -// [#protodoc-title: Allowlisted routes internal redirect predicate] +// [#protodoc-title: AllowListed routes internal redirect predicate] // An internal redirect predicate that accepts only explicitly allowed target routes. -// [#extension: envoy.internal_redirect_predicates.allowlisted_routes] -message AllowlistedRoutesConfig { +// [#extension: envoy.internal_redirect_predicates.allow_listed_routes] +message AllowListedRoutesConfig { // The list of routes that's allowed as redirect target by this predicate, // identified by the route's :ref:`name `. - repeated string allowed_route_names = 1; + // Empty route names are not allowed. + repeated string allowed_route_names = 1 + [(validate.rules).repeated = {items {string {min_len: 1}}}]; } diff --git a/api/versioning/BUILD b/api/versioning/BUILD index 3336de491aa6..8542fe0c5f1f 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -102,7 +102,7 @@ proto_library( "//envoy/extensions/filters/network/thrift_proxy/filters/ratelimit/v3:pkg", "//envoy/extensions/filters/network/thrift_proxy/v3:pkg", "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", - "//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg", + "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index b37b43fc444c..de9f7d14326d 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -165,9 +165,9 @@ is subject to the redirect being handled by Envoy. For a redirect to be handled successfully it must pass the following checks: 1. Have a response code matching one of :ref:`redirect_response_codes - `. -2. Have a *location* header with a valid, fully qualified URL matching the scheme of the original - request. + `, which is + either 302 (by default), or a set of 3xx codes (301, 302, 303, 307, 308). +2. Have a *location* header with a valid, fully qualified URL. 3. The request must have been fully processed by Envoy. 4. The request must not have a body. 5. The scheme of the downstream request and the *location* header are the same or diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 8e190a0539ab..eced5f50128b 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -1592,10 +1592,6 @@ message QueryParameterMatcher { // HTTP Internal Redirect :ref:`architecture overview `. message InternalRedirectPolicy { - message Predicate { - google.protobuf.Any typed_config = 1; - } - // An internal redirect is not handled, unless the number of previous internal redirects that a // downstream request has encountered is lower than this value. // In the case where a downstream request is bounced among multiple routes by internal redirect, @@ -1608,14 +1604,13 @@ message InternalRedirectPolicy { // Defines what upstream response codes are allowed to trigger internal redirect. If unspecified, // only 302 will be treated as internal redirect. - // Only 301, 302, 303, 307 and 308 are valid values. - repeated uint32 redirect_response_codes = 2 - [(validate.rules).repeated = {items {uint32 {lte: 308 gte: 301}}}]; + // Only 301, 302, 303, 307 and 308 are valid values. Any other codes will be ignored. + repeated uint32 redirect_response_codes = 2 [(validate.rules).repeated = {max_items: 5}]; // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated Predicate predicates = 3; + repeated google.protobuf.Any predicates = 3; // Allow internal redirect to follow a target URI with a different scheme than the value of // x-forwarded-proto. Default behavior is to not follow such redirects. diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index adc8fddf1a0f..6c379fb08cb0 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -1582,13 +1582,6 @@ message InternalRedirectPolicy { option (udpa.annotations.versioning).previous_message_type = "envoy.config.route.v3.InternalRedirectPolicy"; - message Predicate { - option (udpa.annotations.versioning).previous_message_type = - "envoy.config.route.v3.InternalRedirectPolicy.Predicate"; - - google.protobuf.Any typed_config = 1; - } - // An internal redirect is not handled, unless the number of previous internal redirects that a // downstream request has encountered is lower than this value. // In the case where a downstream request is bounced among multiple routes by internal redirect, @@ -1601,14 +1594,13 @@ message InternalRedirectPolicy { // Defines what upstream response codes are allowed to trigger internal redirect. If unspecified, // only 302 will be treated as internal redirect. - // Only 301, 302, 303, 307 and 308 are valid values. - repeated uint32 redirect_response_codes = 2 - [(validate.rules).repeated = {items {uint32 {lte: 308 gte: 301}}}]; + // Only 301, 302, 303, 307 and 308 are valid values. Any other codes will be ignored. + repeated uint32 redirect_response_codes = 2 [(validate.rules).repeated = {max_items: 5}]; // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated Predicate predicates = 3; + repeated google.protobuf.Any predicates = 3; // Allow internal redirect to follow a target URI with a different scheme than the value of // x-forwarded-proto. Default behavior is to not follow such redirects. diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/BUILD b/generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/BUILD similarity index 100% rename from generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/BUILD rename to generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/BUILD diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto similarity index 51% rename from generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto rename to generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto index fd1eeb2b41d2..026d727ddfaf 100644 --- a/generated_api_shadow/envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.proto +++ b/generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto @@ -1,21 +1,24 @@ syntax = "proto3"; -package envoy.extensions.internal_redirect.allowlisted_routes.v3; +package envoy.extensions.internal_redirect.allow_listed_routes.v3; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.allowlisted_routes.v3"; -option java_outer_classname = "AllowlistedRoutesConfigProto"; +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.allow_listed_routes.v3"; +option java_outer_classname = "AllowListedRoutesConfigProto"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; -// [#protodoc-title: Allowlisted routes internal redirect predicate] +// [#protodoc-title: AllowListed routes internal redirect predicate] // An internal redirect predicate that accepts only explicitly allowed target routes. -// [#extension: envoy.internal_redirect_predicates.allowlisted_routes] -message AllowlistedRoutesConfig { +// [#extension: envoy.internal_redirect_predicates.allow_listed_routes] +message AllowListedRoutesConfig { // The list of routes that's allowed as redirect target by this predicate, // identified by the route's :ref:`name `. - repeated string allowed_route_names = 1; + // Empty route names are not allowed. + repeated string allowed_route_names = 1 + [(validate.rules).repeated = {items {string {min_len: 1}}}]; } diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index d3421e1dc84e..b25adb714fbd 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -21,7 +21,7 @@ class InternalRedirectPredicate { * * @return whether the route specified by target_route_name is allowed to be followed. Any * predicate returning false will prevent the redirect from being followed, causing the - * response to be proxied to the downstream. + * response to be proxied downstream. */ virtual bool acceptTargetRoute(StreamInfo::FilterState& filter_state, absl::string_view target_route_name) PURE; diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index a73c7c16eef8..80d62babcfb3 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -251,7 +251,7 @@ class InternalRedirectPolicy { /** * @return whether the given response_code should trigger an internal redirect on this route. */ - virtual bool shouldRedirectForCode(const Http::Code& response_code) const PURE; + virtual bool shouldRedirectForResponseCode(const Http::Code& response_code) const PURE; /** * Creates the target route predicates. This should really be called only once for each upstream diff --git a/source/common/router/BUILD b/source/common/router/BUILD index 2786beb3bccc..0128f51db920 100644 --- a/source/common/router/BUILD +++ b/source/common/router/BUILD @@ -289,6 +289,7 @@ envoy_cc_library( "//source/common/access_log:access_log_lib", "//source/common/buffer:watermark_buffer_lib", "//source/common/common:assert_lib", + "//source/common/common:cleanup_lib", "//source/common/common:empty_string", "//source/common/common:enum_to_int", "//source/common/common:hash_lib", diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 4112d889f0a4..b469ffaecbc3 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -147,19 +147,18 @@ Upstream::RetryPrioritySharedPtr RetryPolicyImpl::retryPriority() const { InternalRedirectPolicyImpl::InternalRedirectPolicyImpl( const envoy::config::route::v3::InternalRedirectPolicy& policy_config, ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) - : enabled_(true), current_route_name_(current_route_name), + : current_route_name_(current_route_name), redirect_response_codes_(buildRedirectResponseCodes(policy_config)), max_internal_redirects_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(policy_config, max_internal_redirects, 1)), - allow_cross_scheme_redirect_(policy_config.allow_cross_scheme_redirect()) { + enabled_(true), allow_cross_scheme_redirect_(policy_config.allow_cross_scheme_redirect()) { for (const auto& predicate : policy_config.predicates()) { - const std::string type{ - TypeUtil::typeUrlToDescriptorFullName(predicate.typed_config().type_url())}; + const std::string type{TypeUtil::typeUrlToDescriptorFullName(predicate.type_url())}; auto* factory = Registry::FactoryRegistry::getFactoryByType(type); auto config = factory->createEmptyConfigProto(); - Envoy::Config::Utility::translateOpaqueConfig(predicate.typed_config(), {}, validator, *config); + Envoy::Config::Utility::translateOpaqueConfig(predicate, {}, validator, *config); predicate_factories_.emplace_back(factory, std::move(config)); } } @@ -181,7 +180,11 @@ absl::flat_hash_set InternalRedirectPolicyImpl::buildRedirectRespons absl::flat_hash_set ret; std::for_each(policy_config.redirect_response_codes().begin(), policy_config.redirect_response_codes().end(), [&ret](uint32_t response_code) { - ret.insert(static_cast(response_code)); + const absl::flat_hash_set valid_redirect_response_code = {301, 302, 303, + 307, 308}; + if (valid_redirect_response_code.contains(response_code)) { + ret.insert(static_cast(response_code)); + } }); return ret; } @@ -744,7 +747,7 @@ InternalRedirectPolicyImpl RouteEntryImplBase::buildInternalRedirectPolicy( case envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT: break; case envoy::config::route::v3::RouteAction::PASS_THROUGH_INTERNAL_REDIRECT: - return InternalRedirectPolicyImpl(); + FALLTHRU; default: return InternalRedirectPolicyImpl(); } diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index 7c6a367d4719..8bf7b8e9d94b 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -387,14 +387,17 @@ class RouteTracingImpl : public RouteTracing { */ class InternalRedirectPolicyImpl : public InternalRedirectPolicy { public: + // Constructor that enables internal redirect with policy_config controlling the configurable + // behaviors. explicit InternalRedirectPolicyImpl( const envoy::config::route::v3::InternalRedirectPolicy& policy_config, ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name); + // Default constructor that disables internal redirect. InternalRedirectPolicyImpl() = default; bool enabled() const override { return enabled_; } - bool shouldRedirectForCode(const Http::Code& response_code) const override { + bool shouldRedirectForResponseCode(const Http::Code& response_code) const override { return redirect_response_codes_.contains(response_code); } @@ -408,10 +411,10 @@ class InternalRedirectPolicyImpl : public InternalRedirectPolicy { absl::flat_hash_set buildRedirectResponseCodes( const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const; - const bool enabled_{false}; const std::string current_route_name_; const absl::flat_hash_set redirect_response_codes_; const uint32_t max_internal_redirects_{1}; + const bool enabled_{false}; const bool allow_cross_scheme_redirect_{false}; std::vector> diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 8e31f4f9c17e..cb054501b0d1 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -15,6 +15,7 @@ #include "envoy/upstream/upstream.h" #include "common/common/assert.h" +#include "common/common/cleanup.h" #include "common/common/empty_string.h" #include "common/common/enum_to_int.h" #include "common/common/scope_tracker.h" @@ -98,14 +99,18 @@ bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream if (num_internal_redirect.value() >= policy.maxInternalRedirects()) { return false; } - num_internal_redirect.increment(); - - // Preserve the original request URL for the second pass. - downstream_headers.setEnvoyOriginalUrl( - absl::StrCat(scheme_is_http ? Http::Headers::get().SchemeValues.Http - : Http::Headers::get().SchemeValues.Https, - "://", downstream_headers.Host()->value().getStringView(), - downstream_headers.Path()->value().getStringView())); + std::string original_host(downstream_headers.Host()->value().getStringView()); + std::string original_path(downstream_headers.Path()->value().getStringView()); + bool scheme_is_set = (downstream_headers.Scheme() != nullptr); + Cleanup restore_original_headers( + [&downstream_headers, original_host, original_path, scheme_is_set, scheme_is_http]() { + downstream_headers.setHost(original_host); + downstream_headers.setPath(original_path); + if (scheme_is_set) { + downstream_headers.setScheme(scheme_is_http ? Http::Headers::get().SchemeValues.Http + : Http::Headers::get().SchemeValues.Https); + } + }); // Replace the original host, scheme and path. downstream_headers.setScheme(absolute_url.scheme()); @@ -125,6 +130,14 @@ bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream return false; } } + + num_internal_redirect.increment(); + restore_original_headers.cancel(); + // Preserve the original request URL for the second pass. + downstream_headers.setEnvoyOriginalUrl(absl::StrCat(scheme_is_http + ? Http::Headers::get().SchemeValues.Http + : Http::Headers::get().SchemeValues.Https, + "://", original_host, original_path)); return true; } @@ -1258,7 +1271,7 @@ void Filter::onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPt } if (route_entry_->internalRedirectPolicy().enabled() && - route_entry_->internalRedirectPolicy().shouldRedirectForCode( + route_entry_->internalRedirectPolicy().shouldRedirectForResponseCode( static_cast(response_code)) && setupRedirect(*headers, upstream_request)) { return; diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index ae8cb1f5f86a..2fd05422f0ef 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -174,6 +174,6 @@ EXTENSIONS = { # Internal redirect predicates # - "envoy.internal_redirect_predicates.allowlisted_routes": "//source/extensions/internal_redirect/allowlisted_routes:config", + "envoy.internal_redirect_predicates.allow_listed_routes": "//source/extensions/internal_redirect/allow_listed_routes:config", "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", } diff --git a/source/extensions/internal_redirect/allowlisted_routes/BUILD b/source/extensions/internal_redirect/allow_listed_routes/BUILD similarity index 69% rename from source/extensions/internal_redirect/allowlisted_routes/BUILD rename to source/extensions/internal_redirect/allow_listed_routes/BUILD index 5db35c8802b8..88ff9d714d78 100644 --- a/source/extensions/internal_redirect/allowlisted_routes/BUILD +++ b/source/extensions/internal_redirect/allow_listed_routes/BUILD @@ -10,12 +10,12 @@ load( envoy_package() envoy_cc_library( - name = "allowlisted_routes_lib", - hdrs = ["allowlisted_routes.h"], + name = "allow_listed_routes_lib", + hdrs = ["allow_listed_routes.h"], deps = [ "//include/envoy/router:internal_redirect_interface", "//include/envoy/stream_info:filter_state_interface", - "@envoy_api//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg_cc_proto", ], ) @@ -25,10 +25,10 @@ envoy_cc_extension( hdrs = ["config.h"], security_posture = "robust_to_untrusted_downstream_and_upstream", deps = [ - ":allowlisted_routes_lib", + ":allow_listed_routes_lib", "//include/envoy/registry", "//include/envoy/router:internal_redirect_interface", "//source/extensions/internal_redirect:well_known_names", - "@envoy_api//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h similarity index 70% rename from source/extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h rename to source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h index c4f7881350ef..d7b099f97058 100644 --- a/source/extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h +++ b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h @@ -1,6 +1,6 @@ #pragma once -#include "envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.pb.h" #include "envoy/router/internal_redirect.h" #include "envoy/stream_info/filter_state.h" @@ -11,11 +11,11 @@ namespace Envoy { namespace Extensions { namespace InternalRedirect { -class AllowlistedRoutesPredicate : public Router::InternalRedirectPredicate { +class AllowListedRoutesPredicate : public Router::InternalRedirectPredicate { public: - AllowlistedRoutesPredicate( + AllowListedRoutesPredicate( absl::string_view, - const envoy::extensions::internal_redirect::allowlisted_routes::v3::AllowlistedRoutesConfig& + const envoy::extensions::internal_redirect::allow_listed_routes::v3::AllowListedRoutesConfig& config) : allowed_routes_(config.allowed_route_names().begin(), config.allowed_route_names().end()) {} diff --git a/source/extensions/internal_redirect/allowlisted_routes/config.cc b/source/extensions/internal_redirect/allow_listed_routes/config.cc similarity index 66% rename from source/extensions/internal_redirect/allowlisted_routes/config.cc rename to source/extensions/internal_redirect/allow_listed_routes/config.cc index 726e0da862b8..55c2d5af81ce 100644 --- a/source/extensions/internal_redirect/allowlisted_routes/config.cc +++ b/source/extensions/internal_redirect/allow_listed_routes/config.cc @@ -1,4 +1,4 @@ -#include "extensions/internal_redirect/allowlisted_routes/config.h" +#include "extensions/internal_redirect/allow_listed_routes/config.h" #include "envoy/registry/registry.h" #include "envoy/router/internal_redirect.h" @@ -7,7 +7,7 @@ namespace Envoy { namespace Extensions { namespace InternalRedirect { -REGISTER_FACTORY(AllowlistedRoutesPredicateFactory, Router::InternalRedirectPredicateFactory); +REGISTER_FACTORY(AllowListedRoutesPredicateFactory, Router::InternalRedirectPredicateFactory); } // namespace InternalRedirect } // namespace Extensions diff --git a/source/extensions/internal_redirect/allowlisted_routes/config.h b/source/extensions/internal_redirect/allow_listed_routes/config.h similarity index 53% rename from source/extensions/internal_redirect/allowlisted_routes/config.h rename to source/extensions/internal_redirect/allow_listed_routes/config.h index 91c0881c93eb..1ec9e2e62d4a 100644 --- a/source/extensions/internal_redirect/allowlisted_routes/config.h +++ b/source/extensions/internal_redirect/allow_listed_routes/config.h @@ -1,39 +1,39 @@ #pragma once -#include "envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.pb.h" -#include "envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.pb.validate.h" +#include "envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.pb.validate.h" #include "envoy/router/internal_redirect.h" #include "common/protobuf/message_validator_impl.h" #include "common/protobuf/utility.h" -#include "extensions/internal_redirect/allowlisted_routes/allowlisted_routes.h" +#include "extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h" #include "extensions/internal_redirect/well_known_names.h" namespace Envoy { namespace Extensions { namespace InternalRedirect { -class AllowlistedRoutesPredicateFactory : public Router::InternalRedirectPredicateFactory { +class AllowListedRoutesPredicateFactory : public Router::InternalRedirectPredicateFactory { public: Router::InternalRedirectPredicateSharedPtr createInternalRedirectPredicate(const Protobuf::Message& config, absl::string_view current_route_name) override { - auto allowlisted_routes_config = + auto allow_listed_routes_config = MessageUtil::downcastAndValidate( + allow_listed_routes::v3::AllowListedRoutesConfig&>( config, ProtobufMessage::getStrictValidationVisitor()); - return std::make_shared(current_route_name, - allowlisted_routes_config); + return std::make_shared(current_route_name, + allow_listed_routes_config); } std::string name() const override { - return InternalRedirectPredicateValues::get().AllowlistedRoutesPredicate; + return InternalRedirectPredicateValues::get().AllowListedRoutesPredicate; } ProtobufTypes::MessagePtr createEmptyConfigProto() override { return std::make_unique< - envoy::extensions::internal_redirect::allowlisted_routes::v3::AllowlistedRoutesConfig>(); + envoy::extensions::internal_redirect::allow_listed_routes::v3::AllowListedRoutesConfig>(); } }; diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.cc b/source/extensions/internal_redirect/previous_routes/previous_routes.cc index cac79ce3c853..bcbf3f1165cb 100644 --- a/source/extensions/internal_redirect/previous_routes/previous_routes.cc +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.cc @@ -23,7 +23,7 @@ class PreviousRoutesPredicateState : public StreamInfo::FilterState::Object { PreviousRoutesPredicateState(const PreviousRoutesPredicateState&) = delete; PreviousRoutesPredicateState& operator=(const PreviousRoutesPredicateState&) = delete; - bool insertRouteIfUnexist(absl::string_view route) { + bool insertRouteIfNotPresent(absl::string_view route) { return previous_routes_.insert(std::string(route)).second; } @@ -44,7 +44,7 @@ bool PreviousRoutesPredicate::acceptTargetRoute(StreamInfo::FilterState& filter_ } auto& predicate_state = filter_state.getDataMutable(filter_state_name); - return predicate_state.insertRouteIfUnexist(route_name); + return predicate_state.insertRouteIfNotPresent(route_name); } } // namespace InternalRedirect diff --git a/source/extensions/internal_redirect/well_known_names.h b/source/extensions/internal_redirect/well_known_names.h index a89acb589bbe..1a530a398561 100644 --- a/source/extensions/internal_redirect/well_known_names.h +++ b/source/extensions/internal_redirect/well_known_names.h @@ -14,8 +14,8 @@ namespace InternalRedirect { class InternalRedirectPredicatesNameValues { public: const std::string PreviousRoutesPredicate = "envoy.internal_redirect_predicates.previous_routes"; - const std::string AllowlistedRoutesPredicate = - "envoy.internal_redirect_predicates.allowlisted_routes"; + const std::string AllowListedRoutesPredicate = + "envoy.internal_redirect_predicates.allow_listed_routes"; }; using InternalRedirectPredicateValues = ConstSingleton; diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 6e07b6514b46..17e647d39436 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6796,7 +6796,7 @@ name: InternalRedirectEnabled const auto& internal_redirect_policy = config.route(headers, 0)->routeEntry()->internalRedirectPolicy(); EXPECT_TRUE(internal_redirect_policy.enabled()); - EXPECT_TRUE(internal_redirect_policy.shouldRedirectForCode(static_cast(302))); + EXPECT_TRUE(internal_redirect_policy.shouldRedirectForResponseCode(static_cast(302))); EXPECT_FALSE(internal_redirect_policy.shouldRedirectForCode(static_cast(200))); EXPECT_EQ(1, internal_redirect_policy.maxInternalRedirects()); EXPECT_TRUE(internal_redirect_policy.predicates().empty()); diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 731c1657cd30..3b931d844303 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -291,7 +291,8 @@ class RouterTestBase : public testing::Test { void enableRedirects(uint32_t max_internal_redirects = 1) { ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, enabled()) .WillByDefault(Return(true)); - ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, shouldRedirectForCode(_)) + ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, + shouldRedirectForResponseCode(_)) .WillByDefault(Return(true)); ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, maxInternalRedirects()) .WillByDefault(Return(max_internal_redirects)); @@ -4425,7 +4426,7 @@ TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { sendRequest(); - redirect_headers_->setLocation("http://www.foo.com"); + redirect_headers_->setLocation("http://www.foo.com/some/path"); auto mock_predicate = std::make_shared(); @@ -4440,6 +4441,12 @@ TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_failed_total") .value()); + + // Make sure the original host/path is preserved. + EXPECT_EQ("host", default_request_headers_.Host()->value().getStringView()); + EXPECT_EQ("/", default_request_headers_.Path()->value().getStringView()); + // Make sure x-envoy-original-url is not set for unsuccessful redirect. + EXPECT_EQ(nullptr, default_request_headers_.EnvoyOriginalUrl()); } TEST_F(RouterTest, HttpInternalRedirectSucceeded) { diff --git a/test/integration/BUILD b/test/integration/BUILD index 5de4cbc50eda..52423b78401e 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -612,12 +612,12 @@ envoy_cc_test( deps = [ ":http_protocol_integration_lib", "//source/common/http:header_map_lib", - "//source/extensions/internal_redirect/allowlisted_routes:config", + "//source/extensions/internal_redirect/allow_listed_routes:config", "//source/extensions/internal_redirect/previous_routes:config", "//test/test_common:utility_lib", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/internal_redirect/allowlisted_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/internal_redirect/previous_routes/v3:pkg_cc_proto", ], ) diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index 0eb41a4e92ec..f28d456ed245 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -1,6 +1,6 @@ #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" -#include "envoy/extensions/internal_redirect/allowlisted_routes/v3/allowlisted_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.pb.h" #include "envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.pb.h" #include "test/integration/http_protocol_integration.h" @@ -232,8 +232,7 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByPreviousRoutesPredica internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); envoy::extensions::internal_redirect::previous_routes::v3::PreviousRoutesConfig previous_routes_config; - internal_redirect_policy->add_predicates()->mutable_typed_config()->PackFrom( - previous_routes_config); + internal_redirect_policy->add_predicates()->PackFrom(previous_routes_config); config_helper_.addVirtualHost(handle_prevent_repeated_target); // Validate that header sanitization is only called once. @@ -273,22 +272,22 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByPreviousRoutesPredica ->value()); } -TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowlistedRoutesPredicate) { - auto handle_allowlisted_redirect_route = - config_helper_.createVirtualHost("handle.internal.redirect.only.allowlisted.target"); - auto* internal_redirect_policy = handle_allowlisted_redirect_route.mutable_routes(0) +TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowListedRoutesPredicate) { + auto handle_allow_listed_redirect_route = + config_helper_.createVirtualHost("handle.internal.redirect.only.allow.listed.target"); + auto* internal_redirect_policy = handle_allow_listed_redirect_route.mutable_routes(0) ->mutable_route() ->mutable_internal_redirect_policy(); - auto* allowlisted_routes_predicate = internal_redirect_policy->add_predicates(); - envoy::extensions::internal_redirect::allowlisted_routes::v3::AllowlistedRoutesConfig - allowlisted_routes_config; - *allowlisted_routes_config.add_allowed_route_names() = "max_three_hop"; - allowlisted_routes_predicate->mutable_typed_config()->PackFrom(allowlisted_routes_config); + auto* allow_listed_routes_predicate = internal_redirect_policy->add_predicates(); + envoy::extensions::internal_redirect::allow_listed_routes::v3::AllowListedRoutesConfig + allow_listed_routes_config; + *allow_listed_routes_config.add_allowed_route_names() = "max_three_hop"; + allow_listed_routes_predicate->PackFrom(allow_listed_routes_config); internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); - config_helper_.addVirtualHost(handle_allowlisted_redirect_route); + config_helper_.addVirtualHost(handle_allow_listed_redirect_route); // Validate that header sanitization is only called once. config_helper_.addConfigModifier( @@ -299,7 +298,7 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowlistedRoutesPred codec_client_ = makeHttpConnection(lookupPort("http")); - default_request_headers_.setHost("handle.internal.redirect.only.allowlisted.target"); + default_request_headers_.setHost("handle.internal.redirect.only.allow.listed.target"); IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); @@ -311,11 +310,11 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowlistedRoutesPred auto second_request = waitForNextStream(); // Redirect back to the original route. redirect_response_.setLocation( - "http://handle.internal.redirect.only.allowlisted.target/another/path"); + "http://handle.internal.redirect.only.allow.listed.target/another/path"); second_request->encodeHeaders(redirect_response_, true); auto third_request = waitForNextStream(); - // Redirect to the non-allowlisted route. This should fail. + // Redirect to the non-allow-listed route. This should fail. redirect_response_.setLocation("http://handle.internal.redirect/yet/another/path"); third_request->encodeHeaders(redirect_response_, true); diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index 00f153c9fb2f..1d7e63f8bca0 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -135,7 +135,7 @@ class MockInternalRedirectPolicy : public InternalRedirectPolicy { public: MockInternalRedirectPolicy(); MOCK_METHOD(bool, enabled, (), (const)); - MOCK_METHOD(bool, shouldRedirectForCode, (const Http::Code& response_code), (const)); + MOCK_METHOD(bool, shouldRedirectForResponseCode, (const Http::Code& response_code), (const)); MOCK_METHOD(std::vector, predicates, (), (const)); MOCK_METHOD(uint32_t, maxInternalRedirects, (), (const)); MOCK_METHOD(bool, isCrossSchemeRedirectAllowed, (), (const)); From d6d1dced98ce207625e01aa41f2392a4fcbac1e1 Mon Sep 17 00:00:00 2001 From: pengg Date: Fri, 1 May 2020 08:47:34 -0400 Subject: [PATCH 24/46] Change to allow cross scheme redirect by default. Signed-off-by: pengg --- .../config/route/v3/route_components.proto | 6 +- .../route/v4alpha/route_components.proto | 6 +- .../config/route/v3/route_components.proto | 6 +- .../route/v4alpha/route_components.proto | 6 +- source/common/router/config_impl.cc | 3 +- source/common/router/config_impl.h | 4 +- test/common/router/config_impl_test.cc | 2 +- test/common/router/router_test.cc | 57 ++++++++++--------- 8 files changed, 46 insertions(+), 44 deletions(-) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 1a61d531ed3c..d06f9047e41c 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1594,7 +1594,7 @@ message InternalRedirectPolicy { // the redirect, causing the response to be proxied to downstream. repeated google.protobuf.Any predicates = 3; - // Allow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to not follow such redirects. - bool allow_cross_scheme_redirect = 4; + // Disallow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to follow such redirects. + bool disallow_cross_scheme_redirect = 4; } diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 6883a9d8a586..f957340ffc1d 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1583,7 +1583,7 @@ message InternalRedirectPolicy { // the redirect, causing the response to be proxied to downstream. repeated google.protobuf.Any predicates = 3; - // Allow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to not follow such redirects. - bool allow_cross_scheme_redirect = 4; + // Disallow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to follow such redirects. + bool disallow_cross_scheme_redirect = 4; } diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index eced5f50128b..658f0c123268 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -1612,7 +1612,7 @@ message InternalRedirectPolicy { // the redirect, causing the response to be proxied to downstream. repeated google.protobuf.Any predicates = 3; - // Allow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to not follow such redirects. - bool allow_cross_scheme_redirect = 4; + // Disallow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to follow such redirects. + bool disallow_cross_scheme_redirect = 4; } diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index 6c379fb08cb0..d63b271ad8c2 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -1602,7 +1602,7 @@ message InternalRedirectPolicy { // the redirect, causing the response to be proxied to downstream. repeated google.protobuf.Any predicates = 3; - // Allow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to not follow such redirects. - bool allow_cross_scheme_redirect = 4; + // Disallow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to follow such redirects. + bool disallow_cross_scheme_redirect = 4; } diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index b469ffaecbc3..d6c93a9d45f2 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -151,7 +151,8 @@ InternalRedirectPolicyImpl::InternalRedirectPolicyImpl( redirect_response_codes_(buildRedirectResponseCodes(policy_config)), max_internal_redirects_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(policy_config, max_internal_redirects, 1)), - enabled_(true), allow_cross_scheme_redirect_(policy_config.allow_cross_scheme_redirect()) { + enabled_(true), + disallow_cross_scheme_redirect_(policy_config.disallow_cross_scheme_redirect()) { for (const auto& predicate : policy_config.predicates()) { const std::string type{TypeUtil::typeUrlToDescriptorFullName(predicate.type_url())}; auto* factory = diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index 8bf7b8e9d94b..2067208ce65e 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -405,7 +405,7 @@ class InternalRedirectPolicyImpl : public InternalRedirectPolicy { uint32_t maxInternalRedirects() const override { return max_internal_redirects_; } - bool isCrossSchemeRedirectAllowed() const override { return allow_cross_scheme_redirect_; } + bool isCrossSchemeRedirectAllowed() const override { return !disallow_cross_scheme_redirect_; } private: absl::flat_hash_set buildRedirectResponseCodes( @@ -415,7 +415,7 @@ class InternalRedirectPolicyImpl : public InternalRedirectPolicy { const absl::flat_hash_set redirect_response_codes_; const uint32_t max_internal_redirects_{1}; const bool enabled_{false}; - const bool allow_cross_scheme_redirect_{false}; + const bool disallow_cross_scheme_redirect_{false}; std::vector> predicate_factories_; diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 17e647d39436..0daf2552c7bf 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6800,7 +6800,7 @@ name: InternalRedirectEnabled EXPECT_FALSE(internal_redirect_policy.shouldRedirectForCode(static_cast(200))); EXPECT_EQ(1, internal_redirect_policy.maxInternalRedirects()); EXPECT_TRUE(internal_redirect_policy.predicates().empty()); - EXPECT_FALSE(internal_redirect_policy.isCrossSchemeRedirectAllowed()); + EXPECT_TRUE(internal_redirect_policy.isCrossSchemeRedirectAllowed()); } class PerFilterConfigsTest : public testing::Test, public ConfigImplTestBase { diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 3b931d844303..c294bb5f0870 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -298,7 +298,7 @@ class RouterTestBase : public testing::Test { .WillByDefault(Return(max_internal_redirects)); ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, isCrossSchemeRedirectAllowed()) - .WillByDefault(Return(false)); + .WillByDefault(Return(true)); ON_CALL(callbacks_, connection()).WillByDefault(Return(&connection_)); } @@ -4380,13 +4380,17 @@ TEST_F(RouterTest, InternalRedirectRejectedWithBody) { .value()); } -TEST_F(RouterTest, InternalRedirectRejectedByCrossSchemeRedirect) { +TEST_F(RouterTest, CrossSchemeRedirectRejectedByPolicy) { enableRedirects(); sendRequest(); redirect_headers_->setLocation("https://www.foo.com"); + EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, + isCrossSchemeRedirectAllowed()) + .WillOnce(Return(false)); EXPECT_CALL(callbacks_, recreateStream()).Times(0); response_decoder_->decodeHeaders(std::move(redirect_headers_), true); @@ -4395,32 +4399,6 @@ TEST_F(RouterTest, InternalRedirectRejectedByCrossSchemeRedirect) { .value()); } -TEST_F(RouterTest, CrossSchemeRedirectAllowedByPolicy) { - enableRedirects(); - - sendRequest(); - - redirect_headers_->setLocation("https://www.foo.com"); - - EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); - EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, - isCrossSchemeRedirectAllowed()) - .WillOnce(Return(true)); - EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); - EXPECT_CALL(callbacks_, recreateStream()).Times(1).WillOnce(Return(true)); - response_decoder_->decodeHeaders(std::move(redirect_headers_), false); - EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ - .counter("upstream_internal_redirect_succeeded_total") - .value()); - - // In production, the HCM recreateStream would have called this. - router_.onDestroy(); - EXPECT_EQ(1, callbacks_.streamInfo() - .filterState() - ->getDataMutable("num_internal_redirects") - .value()); -} - TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { enableRedirects(); @@ -4456,6 +4434,9 @@ TEST_F(RouterTest, HttpInternalRedirectSucceeded) { sendRequest(); EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, + isCrossSchemeRedirectAllowed()) + .WillOnce(Return(false)); EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); EXPECT_CALL(callbacks_, recreateStream()).Times(1).WillOnce(Return(true)); response_decoder_->decodeHeaders(std::move(redirect_headers_), false); @@ -4492,6 +4473,26 @@ TEST_F(RouterTest, HttpsInternalRedirectSucceeded) { router_.onDestroy(); } +TEST_F(RouterTest, CrossSchemeRedirectAllowedByPolicy) { + auto ssl_connection = std::make_shared(); + enableRedirects(); + + sendRequest(); + + redirect_headers_->setLocation("http://www.foo.com"); + EXPECT_CALL(connection_, ssl()).Times(1).WillOnce(Return(ssl_connection)); + EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); + EXPECT_CALL(callbacks_, recreateStream()).Times(1).WillOnce(Return(true)); + response_decoder_->decodeHeaders(std::move(redirect_headers_), false); + EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ + .counter("upstream_internal_redirect_succeeded_total") + .value()); + + // In production, the HCM recreateStream would have called this. + router_.onDestroy(); +} + TEST_F(RouterTest, Shadow) { ShadowPolicyPtr policy = std::make_unique("foo", "bar"); callbacks_.route_->route_entry_.shadow_policies_.push_back(std::move(policy)); From 753e046938df6e90934564c3c7fac61c63477789 Mon Sep 17 00:00:00 2001 From: pengg Date: Fri, 1 May 2020 09:27:54 -0400 Subject: [PATCH 25/46] Fix doc ref links. Signed-off-by: pengg --- .../http/http_connection_management.rst | 12 ++++++------ source/extensions/extensions_build_config.bzl | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index de9f7d14326d..a6a4c31dbd2e 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -170,9 +170,9 @@ For a redirect to be handled successfully it must pass the following checks: 2. Have a *location* header with a valid, fully qualified URL. 3. The request must have been fully processed by Envoy. 4. The request must not have a body. -5. The scheme of the downstream request and the *location* header are the same or - :ref:`allow_cross_scheme_redirect - ` is true. +5. :ref:`disallow_cross_scheme_redirect + ` is false (default), + or the scheme of the downstream request and the *location* header are the same. 6. The number of previously handled internal redirect within a given downstream request does not exceed :ref:`max internal redirects ` @@ -195,9 +195,9 @@ will cause the redirect to be passed downstream. Two predicates can be used to create a DAG that defines the redirect chain, the :ref:`previous routes ` predicate, and -the :ref:`allowlisted_routes -`. -Specifically, the *allowlisted routes* predicate defines edges of individual node in the DAG +the :ref:`allow_listed_routes +`. +Specifically, the *allow listed routes* predicate defines edges of individual node in the DAG and the *previous routes* predicate defines "visited" state of the edges, so that loop can be avoided if so desired. diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 2fd05422f0ef..d9021ba46c18 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -175,5 +175,5 @@ EXTENSIONS = { # Internal redirect predicates # "envoy.internal_redirect_predicates.allow_listed_routes": "//source/extensions/internal_redirect/allow_listed_routes:config", - "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", + "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", } From ceb0fa95a3584bcb38c4325ca725cea331e1de72 Mon Sep 17 00:00:00 2001 From: pengg Date: Fri, 1 May 2020 10:40:10 -0400 Subject: [PATCH 26/46] Fix router:config_impl_test Signed-off-by: pengg --- test/common/router/config_impl_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 0daf2552c7bf..29e82cdbbb44 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6797,7 +6797,8 @@ name: InternalRedirectEnabled config.route(headers, 0)->routeEntry()->internalRedirectPolicy(); EXPECT_TRUE(internal_redirect_policy.enabled()); EXPECT_TRUE(internal_redirect_policy.shouldRedirectForResponseCode(static_cast(302))); - EXPECT_FALSE(internal_redirect_policy.shouldRedirectForCode(static_cast(200))); + EXPECT_FALSE( + internal_redirect_policy.shouldRedirectForResponseCode(static_cast(200))); EXPECT_EQ(1, internal_redirect_policy.maxInternalRedirects()); EXPECT_TRUE(internal_redirect_policy.predicates().empty()); EXPECT_TRUE(internal_redirect_policy.isCrossSchemeRedirectAllowed()); From f7beb49ac17ef2dc3385dada91693f4b29d69d57 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 16:07:11 -0400 Subject: [PATCH 27/46] Test that invalid response codes are dropped by InternalRedirectPolicy. Add trace logging to InternalRedirectPredicate. Signed-off-by: pengg --- include/envoy/router/BUILD | 1 + include/envoy/router/internal_redirect.h | 34 +++++++-- source/extensions/extensions_build_config.bzl | 1 + .../allow_listed_routes/BUILD | 1 + .../allow_listed_routes/allow_listed_routes.h | 10 ++- .../internal_redirect/previous_routes/BUILD | 1 + .../previous_routes/previous_routes.cc | 4 +- .../previous_routes/previous_routes.h | 10 ++- test/common/router/config_impl_test.cc | 69 +++++++++++++++++++ 9 files changed, 121 insertions(+), 10 deletions(-) diff --git a/include/envoy/router/BUILD b/include/envoy/router/BUILD index 211ef62a71ec..44aee699e338 100644 --- a/include/envoy/router/BUILD +++ b/include/envoy/router/BUILD @@ -116,5 +116,6 @@ envoy_cc_library( deps = [ "//include/envoy/config:typed_config_interface", "//include/envoy/stream_info:filter_state_interface", + "//source/common/common:minimal_logger_lib", ], ) diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index b25adb714fbd..b9c164d340f1 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -3,6 +3,8 @@ #include "envoy/config/typed_config.h" #include "envoy/stream_info/filter_state.h" +#include "common/common/logger.h" + #include "absl/strings/string_view.h" namespace Envoy { @@ -11,20 +13,44 @@ namespace Router { /** * Used to decide if an internal redirect is allowed to be followed based on the target route. */ -class InternalRedirectPredicate { +class InternalRedirectPredicate : Logger::Loggable { public: virtual ~InternalRedirectPredicate() = default; /** * A FilterState is provided so that predicate implementation can use it to preserve state across - * internal redirects. + * internal redirects. This wraps acceptTargetRouteImpl to provide basic trace logging. + * + * @return whether the route specified by target_route_name is allowed to be followed. Any + * predicate returning false will prevent the redirect from being followed, causing the + * response to be proxied downstream. + */ + bool acceptTargetRoute(StreamInfo::FilterState& filter_state, + absl::string_view target_route_name) { + if (!acceptTargetRouteImpl(filter_state, target_route_name)) { + ENVOY_LOG(trace, "rejecting redirect targeting {}, by {} predicate", target_route_name, + name()); + return false; + } + return true; + } + + /** + * @return the name of the current predicate. + */ + virtual absl::string_view name() const PURE; + +protected: + /** + * Called by acceptTargetRoute. Predicate implementations should override this to provide the + * actual funcitonality. * * @return whether the route specified by target_route_name is allowed to be followed. Any * predicate returning false will prevent the redirect from being followed, causing the * response to be proxied downstream. */ - virtual bool acceptTargetRoute(StreamInfo::FilterState& filter_state, - absl::string_view target_route_name) PURE; + virtual bool acceptTargetRouteImpl(StreamInfo::FilterState& filter_state, + absl::string_view target_route_name) PURE; }; using InternalRedirectPredicateSharedPtr = std::shared_ptr; diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 7948a8b439f1..605972e78bfb 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -173,6 +173,7 @@ EXTENSIONS = { "envoy.filters.http.cache.simple_http_cache": "//source/extensions/filters/http/cache/simple_http_cache:simple_http_cache_lib", + # # Internal redirect predicates # "envoy.internal_redirect_predicates.allow_listed_routes": "//source/extensions/internal_redirect/allow_listed_routes:config", diff --git a/source/extensions/internal_redirect/allow_listed_routes/BUILD b/source/extensions/internal_redirect/allow_listed_routes/BUILD index 88ff9d714d78..02cf2789dc79 100644 --- a/source/extensions/internal_redirect/allow_listed_routes/BUILD +++ b/source/extensions/internal_redirect/allow_listed_routes/BUILD @@ -15,6 +15,7 @@ envoy_cc_library( deps = [ "//include/envoy/router:internal_redirect_interface", "//include/envoy/stream_info:filter_state_interface", + "//source/extensions/internal_redirect:well_known_names", "@envoy_api//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h index d7b099f97058..6f71fdc8d44f 100644 --- a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h +++ b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h @@ -4,6 +4,8 @@ #include "envoy/router/internal_redirect.h" #include "envoy/stream_info/filter_state.h" +#include "extensions/internal_redirect/well_known_names.h" + #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" @@ -19,11 +21,15 @@ class AllowListedRoutesPredicate : public Router::InternalRedirectPredicate { config) : allowed_routes_(config.allowed_route_names().begin(), config.allowed_route_names().end()) {} - bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name) override { - return allowed_routes_.contains(route_name); + absl::string_view name() const override { + return InternalRedirectPredicateValues::get().AllowListedRoutesPredicate; } private: + bool acceptTargetRouteImpl(StreamInfo::FilterState&, absl::string_view route_name) override { + return allowed_routes_.contains(route_name); + } + const absl::flat_hash_set allowed_routes_; }; diff --git a/source/extensions/internal_redirect/previous_routes/BUILD b/source/extensions/internal_redirect/previous_routes/BUILD index a3b5005ad5eb..d022a4c6719c 100644 --- a/source/extensions/internal_redirect/previous_routes/BUILD +++ b/source/extensions/internal_redirect/previous_routes/BUILD @@ -16,6 +16,7 @@ envoy_cc_library( deps = [ "//include/envoy/router:internal_redirect_interface", "//include/envoy/stream_info:filter_state_interface", + "//source/extensions/internal_redirect:well_known_names", ], ) diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.cc b/source/extensions/internal_redirect/previous_routes/previous_routes.cc index bcbf3f1165cb..385980c79835 100644 --- a/source/extensions/internal_redirect/previous_routes/previous_routes.cc +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.cc @@ -33,8 +33,8 @@ class PreviousRoutesPredicateState : public StreamInfo::FilterState::Object { } // namespace -bool PreviousRoutesPredicate::acceptTargetRoute(StreamInfo::FilterState& filter_state, - absl::string_view route_name) { +bool PreviousRoutesPredicate::acceptTargetRouteImpl(StreamInfo::FilterState& filter_state, + absl::string_view route_name) { auto filter_state_name = absl::StrCat(PreviousRoutesPredicateStateNamePrefix, ".", current_route_name_); if (!filter_state.hasData(filter_state_name)) { diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.h b/source/extensions/internal_redirect/previous_routes/previous_routes.h index 293c3be6d5e6..7bfeaa4b545f 100644 --- a/source/extensions/internal_redirect/previous_routes/previous_routes.h +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.h @@ -3,6 +3,8 @@ #include "envoy/router/internal_redirect.h" #include "envoy/stream_info/filter_state.h" +#include "extensions/internal_redirect/well_known_names.h" + #include "absl/strings/string_view.h" namespace Envoy { @@ -14,10 +16,14 @@ class PreviousRoutesPredicate : public Router::InternalRedirectPredicate { explicit PreviousRoutesPredicate(absl::string_view current_route_name) : current_route_name_(current_route_name) {} - bool acceptTargetRoute(StreamInfo::FilterState& filter_state, - absl::string_view route_name) override; + absl::string_view name() const override { + return InternalRedirectPredicateValues::get().PreviousRoutesPredicate; + } private: + bool acceptTargetRouteImpl(StreamInfo::FilterState& filter_state, + absl::string_view route_name) override; + const std::string current_route_name_; }; diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 43d554957e4d..aec31948f904 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6909,6 +6909,75 @@ name: InternalRedirectEnabled EXPECT_TRUE(internal_redirect_policy.isCrossSchemeRedirectAllowed()); } +TEST_F(RouteConfigurationV2, InternalRedirctPolicyDropsInvalidRedirectCode) { + const std::string InternalRedirectEnabled = R"EOF( +name: InternalRedirectEnabled +virtual_hosts: + - name: regex + domains: [idle.lyft.com] + routes: + - match: + safe_regex: + google_re2: {} + regex: "/regex" + route: + cluster: some-cluster + internal_redirect_policy: + redirect_response_codes: [301, 302, 303, 304] + )EOF"; + + TestConfigImpl config(parseRouteConfigurationFromV2Yaml(InternalRedirectEnabled), + factory_context_, true); + Http::TestRequestHeaderMapImpl headers = + genRedirectHeaders("idle.lyft.com", "/regex", true, false); + const auto& internal_redirect_policy = + config.route(headers, 0)->routeEntry()->internalRedirectPolicy(); + EXPECT_TRUE(internal_redirect_policy.enabled()); + EXPECT_TRUE(internal_redirect_policy.shouldRedirectForResponseCode(static_cast(301))); + EXPECT_TRUE(internal_redirect_policy.shouldRedirectForResponseCode(static_cast(302))); + EXPECT_TRUE(internal_redirect_policy.shouldRedirectForResponseCode(static_cast(303))); + EXPECT_FALSE( + internal_redirect_policy.shouldRedirectForResponseCode(static_cast(304))); + EXPECT_FALSE( + internal_redirect_policy.shouldRedirectForResponseCode(static_cast(305))); + EXPECT_FALSE( + internal_redirect_policy.shouldRedirectForResponseCode(static_cast(306))); + EXPECT_FALSE( + internal_redirect_policy.shouldRedirectForResponseCode(static_cast(307))); +} + +TEST_F(RouteConfigurationV2, InternalRedirctPolicyDropsInvalidRedirectCodeCauseEmptySet) { + const std::string InternalRedirectEnabled = R"EOF( +name: InternalRedirectEnabled +virtual_hosts: + - name: regex + domains: [idle.lyft.com] + routes: + - match: + safe_regex: + google_re2: {} + regex: "/regex" + route: + cluster: some-cluster + internal_redirect_policy: + redirect_response_codes: [200, 304] + )EOF"; + + TestConfigImpl config(parseRouteConfigurationFromV2Yaml(InternalRedirectEnabled), + factory_context_, true); + Http::TestRequestHeaderMapImpl headers = + genRedirectHeaders("idle.lyft.com", "/regex", true, false); + const auto& internal_redirect_policy = + config.route(headers, 0)->routeEntry()->internalRedirectPolicy(); + EXPECT_TRUE(internal_redirect_policy.enabled()); + EXPECT_FALSE( + internal_redirect_policy.shouldRedirectForResponseCode(static_cast(302))); + EXPECT_FALSE( + internal_redirect_policy.shouldRedirectForResponseCode(static_cast(304))); + EXPECT_FALSE( + internal_redirect_policy.shouldRedirectForResponseCode(static_cast(200))); +} + class PerFilterConfigsTest : public testing::Test, public ConfigImplTestBase { public: PerFilterConfigsTest() From bc525c44a62f79650a8ccc9c608e07071e6a08c1 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 16:11:53 -0400 Subject: [PATCH 28/46] Fix typo. Signed-off-by: pengg --- include/envoy/router/internal_redirect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index b9c164d340f1..600c96e6795a 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -43,7 +43,7 @@ class InternalRedirectPredicate : Logger::Loggable { protected: /** * Called by acceptTargetRoute. Predicate implementations should override this to provide the - * actual funcitonality. + * actual functionality. * * @return whether the route specified by target_route_name is allowed to be followed. Any * predicate returning false will prevent the redirect from being followed, causing the From 4e06b6d9892ca08e754fd2a886276bbce918c4ac Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 16:23:06 -0400 Subject: [PATCH 29/46] regenerate v4 protos. Signed-off-by: pengg --- .../route/v4alpha/route_components.proto | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index 75b9e6a9f180..55ee65e331d5 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -4,8 +4,8 @@ package envoy.config.route.v4alpha; import "envoy/config/core/v4alpha/base.proto"; import "envoy/config/core/v4alpha/proxy_protocol.proto"; -import "envoy/type/matcher/v3/regex.proto"; -import "envoy/type/matcher/v3/string.proto"; +import "envoy/type/matcher/v4alpha/regex.proto"; +import "envoy/type/matcher/v4alpha/string.proto"; import "envoy/type/tracing/v3/custom_tag.proto"; import "envoy/type/v3/percent.proto"; import "envoy/type/v3/range.proto"; @@ -143,7 +143,7 @@ message VirtualHost { // will see the attempt count as perceived by the second Envoy. Defaults to false. // This header is unaffected by the // :ref:`suppress_envoy_headers - // ` flag. + // ` flag. // // [#next-major-version: rename to include_attempt_count_in_request.] bool include_request_attempt_count = 14; @@ -155,7 +155,7 @@ message VirtualHost { // will see the attempt count as perceived by the Envoy closest upstream from itself. Defaults to false. // This header is unaffected by the // :ref:`suppress_envoy_headers - // ` flag. + // ` flag. bool include_attempt_count_in_response = 19; // Indicates the retry policy for all routes in this virtual host. Note that setting a @@ -428,7 +428,7 @@ message RouteMatch { // path_specifier entirely and just rely on a set of header matchers which can already match // on :path, etc. The issue with that is it is unclear how to generically deal with query string // stripping. This needs more thought.] - type.matcher.v3.RegexMatcher safe_regex = 10 [(validate.rules).message = {required: true}]; + type.matcher.v4alpha.RegexMatcher safe_regex = 10 [(validate.rules).message = {required: true}]; // [#not-implemented-hide:] // If this is used as the matcher, the matcher will only match CONNECT requests. @@ -499,7 +499,7 @@ message CorsPolicy { // Specifies string patterns that match allowed origins. An origin is allowed if any of the // string matchers match. - repeated type.matcher.v3.StringMatcher allow_origin_string_match = 11; + repeated type.matcher.v4alpha.StringMatcher allow_origin_string_match = 11; // Specifies the content for the *access-control-allow-methods* header. string allow_methods = 2; @@ -856,7 +856,7 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - type.matcher.v3.RegexMatchAndSubstitute regex_rewrite = 32; + type.matcher.v4alpha.RegexMatchAndSubstitute regex_rewrite = 32; oneof host_rewrite_specifier { // Indicates that during forwarding, the host header will be swapped with @@ -1542,7 +1542,7 @@ message HeaderMatcher { // If specified, this regex string is a regular expression rule which implies the entire request // header value must match the regex. The rule will not match if only a subsequence of the // request header value matches the regex. - type.matcher.v3.RegexMatcher safe_regex_match = 11; + type.matcher.v4alpha.RegexMatcher safe_regex_match = 11; // If specified, header match will be performed based on range. // The rule will match if the request header value is within this range. @@ -1604,7 +1604,8 @@ message QueryParameterMatcher { oneof query_parameter_match_specifier { // Specifies whether a query parameter value should match against a string. - type.matcher.v3.StringMatcher string_match = 5 [(validate.rules).message = {required: true}]; + type.matcher.v4alpha.StringMatcher string_match = 5 + [(validate.rules).message = {required: true}]; // Specifies whether a query parameter should be present. bool present_match = 6; From 8c31bf949f3da2a4257541ce0128f9342eab9141 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 17:20:06 -0400 Subject: [PATCH 30/46] Add stats for internal redirect. Signed-off-by: pengg --- include/envoy/router/internal_redirect.h | 26 +-- source/common/router/router.cc | 173 +++++++++--------- source/common/router/router.h | 9 + .../allow_listed_routes/allow_listed_routes.h | 9 +- .../previous_routes/previous_routes.cc | 4 +- .../previous_routes/previous_routes.h | 6 +- test/common/router/router_test.cc | 7 + test/mocks/router/mocks.h | 1 + 8 files changed, 119 insertions(+), 116 deletions(-) diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index 600c96e6795a..167a4bad3478 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -12,6 +12,7 @@ namespace Router { /** * Used to decide if an internal redirect is allowed to be followed based on the target route. + * Subclassing Logger::Loggable so that implementations can log details. */ class InternalRedirectPredicate : Logger::Loggable { public: @@ -19,38 +20,19 @@ class InternalRedirectPredicate : Logger::Loggable { /** * A FilterState is provided so that predicate implementation can use it to preserve state across - * internal redirects. This wraps acceptTargetRouteImpl to provide basic trace logging. + * internal redirects. * * @return whether the route specified by target_route_name is allowed to be followed. Any * predicate returning false will prevent the redirect from being followed, causing the * response to be proxied downstream. */ - bool acceptTargetRoute(StreamInfo::FilterState& filter_state, - absl::string_view target_route_name) { - if (!acceptTargetRouteImpl(filter_state, target_route_name)) { - ENVOY_LOG(trace, "rejecting redirect targeting {}, by {} predicate", target_route_name, - name()); - return false; - } - return true; - } + virtual bool acceptTargetRoute(StreamInfo::FilterState& filter_state, + absl::string_view target_route_name) PURE; /** * @return the name of the current predicate. */ virtual absl::string_view name() const PURE; - -protected: - /** - * Called by acceptTargetRoute. Predicate implementations should override this to provide the - * actual functionality. - * - * @return whether the route specified by target_route_name is allowed to be followed. Any - * predicate returning false will prevent the redirect from being followed, causing the - * response to be proxied downstream. - */ - virtual bool acceptTargetRouteImpl(StreamInfo::FilterState& filter_state, - absl::string_view target_route_name) PURE; }; using InternalRedirectPredicateSharedPtr = std::shared_ptr; diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 3affb7aea5f3..397913ba00b3 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -60,87 +60,6 @@ bool schemeIsHttp(const Http::RequestHeaderMap& downstream_headers, return false; } -bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream_headers, - Http::StreamDecoderFilterCallbacks* callbacks, - const InternalRedirectPolicy& policy, - const Http::HeaderEntry& internal_redirect, - const Network::Connection& connection) { - // Make sure the redirect response contains a URL to redirect to. - if (internal_redirect.value().getStringView().length() == 0) { - return false; - } - if (!downstream_headers.Path()) { - return false; - } - - Http::Utility::Url absolute_url; - if (!absolute_url.initialize(internal_redirect.value().getStringView(), false)) { - return false; - } - - // Don't allow serving TLS responses over plaintext unless allowed by policy. - bool scheme_is_http = schemeIsHttp(downstream_headers, connection); - bool target_is_http = absolute_url.scheme() == Http::Headers::get().SchemeValues.Http; - if (!policy.isCrossSchemeRedirectAllowed() && scheme_is_http != target_is_http) { - return false; - } - - const StreamInfo::FilterStateSharedPtr& filter_state = callbacks->streamInfo().filterState(); - // Make sure that performing the redirect won't result in exceeding the configured number of - // redirects allowed for this route. - if (!filter_state->hasData(NumInternalRedirectsFilterStateName)) { - filter_state->setData( - NumInternalRedirectsFilterStateName, std::make_shared(0), - StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Request); - } - StreamInfo::UInt32Accessor& num_internal_redirect = - filter_state->getDataMutable(NumInternalRedirectsFilterStateName); - - if (num_internal_redirect.value() >= policy.maxInternalRedirects()) { - return false; - } - std::string original_host(downstream_headers.Host()->value().getStringView()); - std::string original_path(downstream_headers.Path()->value().getStringView()); - bool scheme_is_set = (downstream_headers.Scheme() != nullptr); - Cleanup restore_original_headers( - [&downstream_headers, original_host, original_path, scheme_is_set, scheme_is_http]() { - downstream_headers.setHost(original_host); - downstream_headers.setPath(original_path); - if (scheme_is_set) { - downstream_headers.setScheme(scheme_is_http ? Http::Headers::get().SchemeValues.Http - : Http::Headers::get().SchemeValues.Https); - } - }); - - // Replace the original host, scheme and path. - downstream_headers.setScheme(absolute_url.scheme()); - downstream_headers.setHost(absolute_url.hostAndPort()); - downstream_headers.setPath(absolute_url.pathAndQueryParams()); - - callbacks->clearRouteCache(); - auto route = callbacks->route(); - // Don't allow a redirect to a non existing route. - if (!route) { - return false; - } - - auto& route_name = route->routeEntry()->routeName(); - for (auto& predicate : policy.predicates()) { - if (!predicate->acceptTargetRoute(*filter_state, route_name)) { - return false; - } - } - - num_internal_redirect.increment(); - restore_original_headers.cancel(); - // Preserve the original request URL for the second pass. - downstream_headers.setEnvoyOriginalUrl(absl::StrCat(scheme_is_http - ? Http::Headers::get().SchemeValues.Http - : Http::Headers::get().SchemeValues.Https, - "://", original_host, original_path)); - return true; -} - constexpr uint64_t TimeoutPrecisionFactor = 100; Http::ConnectionPool::Instance* @@ -1521,9 +1440,7 @@ bool Filter::setupRedirect(const Http::ResponseHeaderMap& headers, if (downstream_end_stream_ && !callbacks_->decodingBuffer() && // Redirects with body not yet supported. location != nullptr && - convertRequestHeadersForInternalRedirect(*downstream_headers_, callbacks_, - route_entry_->internalRedirectPolicy(), *location, - *callbacks_->connection()) && + convertRequestHeadersForInternalRedirect(*downstream_headers_, *location) && callbacks_->recreateStream()) { cluster_->stats().upstream_internal_redirect_succeeded_total_.inc(); return true; @@ -1536,6 +1453,94 @@ bool Filter::setupRedirect(const Http::ResponseHeaderMap& headers, return false; } +bool Filter::convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream_headers, + const Http::HeaderEntry& internal_redirect) { + // Make sure the redirect response contains a URL to redirect to. + if (internal_redirect.value().getStringView().length() == 0) { + config_.stats_.passthrough_internal_redirect_no_location_.inc(); + return false; + } + if (!downstream_headers.Path()) { + config_.stats_.passthrough_internal_redirect_no_path_.inc(); + return false; + } + + Http::Utility::Url absolute_url; + if (!absolute_url.initialize(internal_redirect.value().getStringView(), false)) { + config_.stats_.passthrough_internal_redirect_bad_location_.inc(); + return false; + } + + auto& policy = route_entry_->internalRedirectPolicy(); + // Don't allow serving TLS responses over plaintext unless allowed by policy. + bool scheme_is_http = schemeIsHttp(downstream_headers, *callbacks_->connection()); + bool target_is_http = absolute_url.scheme() == Http::Headers::get().SchemeValues.Http; + if (!policy.isCrossSchemeRedirectAllowed() && scheme_is_http != target_is_http) { + config_.stats_.passthrough_internal_redirect_unsafe_scheme_.inc(); + return false; + } + + const StreamInfo::FilterStateSharedPtr& filter_state = callbacks_->streamInfo().filterState(); + // Make sure that performing the redirect won't result in exceeding the configured number of + // redirects allowed for this route. + if (!filter_state->hasData(NumInternalRedirectsFilterStateName)) { + filter_state->setData( + NumInternalRedirectsFilterStateName, std::make_shared(0), + StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Request); + } + StreamInfo::UInt32Accessor& num_internal_redirect = + filter_state->getDataMutable(NumInternalRedirectsFilterStateName); + + if (num_internal_redirect.value() >= policy.maxInternalRedirects()) { + config_.stats_.passthrough_internal_redirect_too_many_redirects_.inc(); + return false; + } + std::string original_host(downstream_headers.Host()->value().getStringView()); + std::string original_path(downstream_headers.Path()->value().getStringView()); + bool scheme_is_set = (downstream_headers.Scheme() != nullptr); + Cleanup restore_original_headers( + [&downstream_headers, original_host, original_path, scheme_is_set, scheme_is_http]() { + downstream_headers.setHost(original_host); + downstream_headers.setPath(original_path); + if (scheme_is_set) { + downstream_headers.setScheme(scheme_is_http ? Http::Headers::get().SchemeValues.Http + : Http::Headers::get().SchemeValues.Https); + } + }); + + // Replace the original host, scheme and path. + downstream_headers.setScheme(absolute_url.scheme()); + downstream_headers.setHost(absolute_url.hostAndPort()); + downstream_headers.setPath(absolute_url.pathAndQueryParams()); + + callbacks_->clearRouteCache(); + auto route = callbacks_->route(); + // Don't allow a redirect to a non existing route. + if (!route) { + config_.stats_.passthrough_internal_redirect_no_route_.inc(); + return false; + } + + auto& route_name = route->routeEntry()->routeName(); + for (auto& predicate : policy.predicates()) { + if (!predicate->acceptTargetRoute(*filter_state, route_name)) { + config_.stats_.passthrough_internal_redirect_predicate_.inc(); + ENVOY_STREAM_LOG(trace, "rejecting redirect targeting {}, by {} predicate", *callbacks_, + route_name, predicate->name()); + return false; + } + } + + num_internal_redirect.increment(); + restore_original_headers.cancel(); + // Preserve the original request URL for the second pass. + downstream_headers.setEnvoyOriginalUrl(absl::StrCat(scheme_is_http + ? Http::Headers::get().SchemeValues.Http + : Http::Headers::get().SchemeValues.Https, + "://", original_host, original_path)); + return true; +} + void Filter::doRetry() { ENVOY_STREAM_LOG(debug, "performing retry", *callbacks_); diff --git a/source/common/router/router.h b/source/common/router/router.h index 058e82bdc540..7323a6d8fa09 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -41,6 +41,13 @@ namespace Router { */ // clang-format off #define ALL_ROUTER_STATS(COUNTER) \ + COUNTER(passthrough_internal_redirect_no_location) \ + COUNTER(passthrough_internal_redirect_no_path) \ + COUNTER(passthrough_internal_redirect_bad_location) \ + COUNTER(passthrough_internal_redirect_unsafe_scheme) \ + COUNTER(passthrough_internal_redirect_too_many_redirects) \ + COUNTER(passthrough_internal_redirect_no_route) \ + COUNTER(passthrough_internal_redirect_predicate) \ COUNTER(no_route) \ COUNTER(no_cluster) \ COUNTER(rq_redirect) \ @@ -500,6 +507,8 @@ class Filter : Logger::Loggable, void resetOtherUpstreams(UpstreamRequest& upstream_request); void sendNoHealthyUpstreamResponse(); bool setupRedirect(const Http::ResponseHeaderMap& headers, UpstreamRequest& upstream_request); + bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream_headers, + const Http::HeaderEntry& internal_redirect); void updateOutlierDetection(Upstream::Outlier::Result result, UpstreamRequest& upstream_request, absl::optional code); void doRetry(); diff --git a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h index 6f71fdc8d44f..5b0f2fd3678c 100644 --- a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h +++ b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h @@ -21,13 +21,12 @@ class AllowListedRoutesPredicate : public Router::InternalRedirectPredicate { config) : allowed_routes_(config.allowed_route_names().begin(), config.allowed_route_names().end()) {} - absl::string_view name() const override { - return InternalRedirectPredicateValues::get().AllowListedRoutesPredicate; + bool acceptTargetRoutel(StreamInfo::FilterState&, absl::string_view route_name) override { + return allowed_routes_.contains(route_name); } -private: - bool acceptTargetRouteImpl(StreamInfo::FilterState&, absl::string_view route_name) override { - return allowed_routes_.contains(route_name); + absl::string_view name() const override { + return InternalRedirectPredicateValues::get().AllowListedRoutesPredicate; } const absl::flat_hash_set allowed_routes_; diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.cc b/source/extensions/internal_redirect/previous_routes/previous_routes.cc index 385980c79835..bcbf3f1165cb 100644 --- a/source/extensions/internal_redirect/previous_routes/previous_routes.cc +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.cc @@ -33,8 +33,8 @@ class PreviousRoutesPredicateState : public StreamInfo::FilterState::Object { } // namespace -bool PreviousRoutesPredicate::acceptTargetRouteImpl(StreamInfo::FilterState& filter_state, - absl::string_view route_name) { +bool PreviousRoutesPredicate::acceptTargetRoute(StreamInfo::FilterState& filter_state, + absl::string_view route_name) { auto filter_state_name = absl::StrCat(PreviousRoutesPredicateStateNamePrefix, ".", current_route_name_); if (!filter_state.hasData(filter_state_name)) { diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.h b/source/extensions/internal_redirect/previous_routes/previous_routes.h index 7bfeaa4b545f..cd96ff04419b 100644 --- a/source/extensions/internal_redirect/previous_routes/previous_routes.h +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.h @@ -16,14 +16,14 @@ class PreviousRoutesPredicate : public Router::InternalRedirectPredicate { explicit PreviousRoutesPredicate(absl::string_view current_route_name) : current_route_name_(current_route_name) {} + bool acceptTargetRoute(StreamInfo::FilterState& filter_state, + absl::string_view route_name) override; + absl::string_view name() const override { return InternalRedirectPredicateValues::get().PreviousRoutesPredicate; } private: - bool acceptTargetRouteImpl(StreamInfo::FilterState& filter_state, - absl::string_view route_name) override; - const std::string current_route_name_; }; diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index dba955c16eb0..f86fbf300e60 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -4448,6 +4448,8 @@ TEST_F(RouterTest, InternalRedirectRejectedWhenReachingMaxInternalRedirect) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_failed_total") .value()); + EXPECT_EQ(1UL, + stats_store_.counter("test.passthrough_internal_redirect_too_many_redirects").value()); } TEST_F(RouterTest, InternalRedirectRejectedWithEmptyLocation) { @@ -4465,6 +4467,7 @@ TEST_F(RouterTest, InternalRedirectRejectedWithEmptyLocation) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_failed_total") .value()); + EXPECT_EQ(1UL, stats_store_.counter("test.passthrough_internal_redirect_no_location").value()); } TEST_F(RouterTest, InternalRedirectRejectedWithInvalidLocation) { @@ -4482,6 +4485,7 @@ TEST_F(RouterTest, InternalRedirectRejectedWithInvalidLocation) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_failed_total") .value()); + EXPECT_EQ(1UL, stats_store_.counter("test.passthrough_internal_redirect_bad_location").value()); } TEST_F(RouterTest, InternalRedirectRejectedWithoutCompleteRequest) { @@ -4551,6 +4555,7 @@ TEST_F(RouterTest, CrossSchemeRedirectRejectedByPolicy) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_failed_total") .value()); + EXPECT_EQ(1UL, stats_store_.counter("test.passthrough_internal_redirect_unsafe_scheme").value()); } TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { @@ -4567,12 +4572,14 @@ TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, predicates()) .WillOnce(Return(std::vector({mock_predicate}))); EXPECT_CALL(*mock_predicate, acceptTargetRoute(_, _)).WillOnce(Return(false)); + ON_CALL(*mock_predicate, name()).WillByDefault(Return("mock_predicate")); EXPECT_CALL(callbacks_, recreateStream()).Times(0); response_decoder_->decodeHeaders(std::move(redirect_headers_), true); EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_failed_total") .value()); + EXPECT_EQ(1UL, stats_store_.counter("test.passthrough_internal_redirect_predicate").value()); // Make sure the original host/path is preserved. EXPECT_EQ("host", default_request_headers_.Host()->value().getStringView()); diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index ab82519c60d8..ab7dcfc664a0 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -144,6 +144,7 @@ class MockInternalRedirectPolicy : public InternalRedirectPolicy { class MockInternalRedirectPredicate : public InternalRedirectPredicate { public: MOCK_METHOD(bool, acceptTargetRoute, (StreamInfo::FilterState&, absl::string_view)); + MOCK_METHOD(absl::string_view, name, (), (const)); }; class MockRetryState : public RetryState { From db7a8feabeaebcabc638b82dc6a00d8cc2e12209 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 17:33:58 -0400 Subject: [PATCH 31/46] Fix accidentally broken doc format in http_connection_management.rst Signed-off-by: pengg --- .../http/http_connection_management.rst | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index 889f92a1e3b8..2747907a16a4 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -52,29 +52,29 @@ Normally during retries, host selection follows the same process as the original can be used to modify this behavior, and they fall into two categories: * :ref:`Host Predicates `: -These predicates can be used to "reject" a host, which will cause host selection to be reattempted. -Any number of these predicates can be specified, and the host will be rejected if any of the predicates reject the host. + These predicates can be used to "reject" a host, which will cause host selection to be reattempted. + Any number of these predicates can be specified, and the host will be rejected if any of the predicates reject the host. -Envoy supports the following built-in host predicates + Envoy supports the following built-in host predicates -* *envoy.retry_host_predicates.previous_hosts*: This will keep track of previously attempted hosts, and rejects -hosts that have already been attempted. + * *envoy.retry_host_predicates.previous_hosts*: This will keep track of previously attempted hosts, and rejects + hosts that have already been attempted. -* *envoy.retry_host_predicates.omit_canary_hosts*: This will reject any host that is a marked as canary host. -Hosts are marked by setting ``canary: true`` for the ``envoy.lb`` filter in the endpoint's filter metadata. -See :ref:`LbEndpoint ` for more details. + * *envoy.retry_host_predicates.omit_canary_hosts*: This will reject any host that is a marked as canary host. + Hosts are marked by setting ``canary: true`` for the ``envoy.lb`` filter in the endpoint's filter metadata. + See :ref:`LbEndpoint ` for more details. -* *envoy.retry_host_predicates.omit_host_metadata*: This will reject any host based on predefined metadata match criteria. -See the configuration example below for more details. + * *envoy.retry_host_predicates.omit_host_metadata*: This will reject any host based on predefined metadata match criteria. + See the configuration example below for more details. * :ref:`Priority Predicates`: These predicates can -be used to adjust the priority load used when selecting a priority for a retry attempt. Only one such -predicate may be specified. + be used to adjust the priority load used when selecting a priority for a retry attempt. Only one such + predicate may be specified. -Envoy supports the following built-in priority predicates + Envoy supports the following built-in priority predicates -* *envoy.retry_priorities.previous_priorities*: This will keep track of previously attempted priorities, -and adjust the priority load such that other priorities will be targeted in subsequent retry attempts. + * *envoy.retry_priorities.previous_priorities*: This will keep track of previously attempted priorities, + and adjust the priority load such that other priorities will be targeted in subsequent retry attempts. Host selection will continue until either the configured predicates accept the host or a configurable :ref:`max attempts ` has been reached. @@ -90,10 +90,10 @@ For example, to configure retries to prefer hosts that haven't been attempted al .. code-block:: yaml -retry_policy: -retry_host_predicate: -- name: envoy.retry_host_predicates.previous_hosts -host_selection_retry_max_attempts: 3 + retry_policy: + retry_host_predicate: + - name: envoy.retry_host_predicates.previous_hosts + host_selection_retry_max_attempts: 3 This will reject hosts previously attempted, retrying host selection a maximum of 3 times. The bound on attempts is necessary in order to deal with scenarios in which finding an acceptable host is either @@ -104,15 +104,15 @@ To reject a host based on its metadata, ``envoy.retry_host_predicates.omit_host_ .. code-block:: yaml -retry_policy: -retry_host_predicate: -- name: envoy.retry_host_predicates.omit_host_metadata -typed_config: -"@type": type.googleapis.com/envoy.extensions.retry.host.omit_host_metadata.v3.OmitHostMetadataConfig -metadata_match: - filter_metadata: - envoy.lb: - key: value + retry_policy: + retry_host_predicate: + - name: envoy.retry_host_predicates.omit_host_metadata + typed_config: + "@type": type.googleapis.com/envoy.extensions.retry.host.omit_host_metadata.v3.OmitHostMetadataConfig + metadata_match: + filter_metadata: + envoy.lb: + key: value This will reject any host with matching (key, value) in its metadata. @@ -121,12 +121,12 @@ To configure retries to attempt other priorities during retries, the built-in .. code-block:: yaml -retry_policy: -retry_priority: -name: envoy.retry_priorities.previous_priorities -typed_config: -"@type": type.googleapis.com/envoy.extensions.retry.priority.previous_priorities.v3.PreviousPrioritiesConfig -update_frequency: 2 + retry_policy: + retry_priority: + name: envoy.retry_priorities.previous_priorities + typed_config: + "@type": type.googleapis.com/envoy.extensions.retry.priority.previous_priorities.v3.PreviousPrioritiesConfig + update_frequency: 2 This will target priorities in subsequent retry attempts that haven't been already used. The ``update_frequency`` parameter decides how often the priority load should be recalculated. @@ -136,15 +136,15 @@ previously attempted priorities. .. code-block:: yaml -retry_policy: -retry_host_predicate: -- name: envoy.retry_host_predicates.previous_hosts -host_selection_retry_max_attempts: 3 -retry_priority: -name: envoy.retry_priorities.previous_priorities -typed_config: -"@type": type.googleapis.com/envoy.extensions.retry.priority.previous_priorities.v3.PreviousPrioritiesConfig -update_frequency: 2 + retry_policy: + retry_host_predicate: + - name: envoy.retry_host_predicates.previous_hosts + host_selection_retry_max_attempts: 3 + retry_priority: + name: envoy.retry_priorities.previous_priorities + typed_config: + "@type": type.googleapis.com/envoy.extensions.retry.priority.previous_priorities.v3.PreviousPrioritiesConfig + update_frequency: 2 .. _arch_overview_internal_redirects: From cac69d615cf4a8662a87fdbcebc96e97565f9b85 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 17:37:30 -0400 Subject: [PATCH 32/46] Fix comment. Signed-off-by: pengg --- .../intro/arch_overview/http/http_connection_management.rst | 2 +- include/envoy/router/router.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index 2747907a16a4..8d76d142c4a0 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -112,7 +112,7 @@ To reject a host based on its metadata, ``envoy.retry_host_predicates.omit_host_ metadata_match: filter_metadata: envoy.lb: - key: value + key: value This will reject any host with matching (key, value) in its metadata. diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index 4686b1be9354..728991855a84 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -719,8 +719,8 @@ class RouteEntry : public ResponseEntry { /** * @return const InternalRedirectPolicy& the internal redirect policy for the route. All routes - * have a internal redirect policy even if it is not enabled, which means redirects from - * the upstream are not followed. + * have a internal redirect policy even if it is not enabled, which means redirects are + * simply proxied as normal responses. */ virtual const InternalRedirectPolicy& internalRedirectPolicy() const PURE; From a24ee609cdd89616bd45d03f3e5adea562ea03a7 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 17:52:41 -0400 Subject: [PATCH 33/46] Fix typo. Add counter test in redirection integration test. Signed-off-by: pengg --- .../allow_listed_routes/allow_listed_routes.h | 2 +- test/integration/redirect_integration_test.cc | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h index 5b0f2fd3678c..df3c1202b1c1 100644 --- a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h +++ b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h @@ -21,7 +21,7 @@ class AllowListedRoutesPredicate : public Router::InternalRedirectPredicate { config) : allowed_routes_(config.allowed_route_names().begin(), config.allowed_route_names().end()) {} - bool acceptTargetRoutel(StreamInfo::FilterState&, absl::string_view route_name) override { + bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name) override { return allowed_routes_.contains(route_name); } diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index f28d456ed245..6369c95394fe 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -179,6 +179,9 @@ TEST_P(RedirectIntegrationTest, InternalRedirectWithThreeHopLimit) { EXPECT_EQ( 1, test_server_->counter("cluster.cluster_0.upstream_internal_redirect_failed_total")->value()); + EXPECT_EQ( + 1, test_server_->counter("http.config_test.passthrough_internal_redirect_too_many_redirects") + ->value()); } TEST_P(RedirectIntegrationTest, InternalRedirectToDestinationWithBody) { @@ -270,6 +273,9 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByPreviousRoutesPredica response->headers().Location()->value().getStringView()); EXPECT_EQ(2, test_server_->counter("cluster.cluster_0.upstream_internal_redirect_succeeded_total") ->value()); + EXPECT_EQ( + 1, + test_server_->counter("http.config_test.passthrough_internal_redirect_predicate")->value()); } TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowListedRoutesPredicate) { @@ -325,6 +331,9 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowListedRoutesPred response->headers().Location()->value().getStringView()); EXPECT_EQ(2, test_server_->counter("cluster.cluster_0.upstream_internal_redirect_succeeded_total") ->value()); + EXPECT_EQ( + 1, + test_server_->counter("http.config_test.passthrough_internal_redirect_predicate")->value()); } TEST_P(RedirectIntegrationTest, InvalidRedirect) { From 1144d827109f64270788bee33d0c8c378a23f9d4 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 18:25:57 -0400 Subject: [PATCH 34/46] Update dictionary. Signed-off-by: pengg --- tools/spelling/spelling_dictionary.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 6a5004b95e6e..22bca74c0444 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -171,6 +171,7 @@ LHS LLVM LPT LRS +Loggable MB MD MERCHANTABILITY From 9e0892642934460fbe92f218b7eef40a7adbb088 Mon Sep 17 00:00:00 2001 From: pengg Date: Mon, 4 May 2020 19:53:36 -0400 Subject: [PATCH 35/46] NiceMock. Signed-off-by: pengg --- test/common/router/router_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index f86fbf300e60..bc91316442fd 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -4565,7 +4565,7 @@ TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { redirect_headers_->setLocation("http://www.foo.com/some/path"); - auto mock_predicate = std::make_shared(); + auto mock_predicate = std::make_shared>(); EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); From c1114257abaa5033640e73ee1941eef75dbd6888 Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 5 May 2020 09:45:26 -0400 Subject: [PATCH 36/46] Bump stats_integration_test bounds. Signed-off-by: pengg --- source/common/router/router.cc | 12 ++++++------ source/common/router/router.h | 2 -- test/integration/stats_integration_test.cc | 9 ++++++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 397913ba00b3..46090c5d4939 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -1455,16 +1455,16 @@ bool Filter::setupRedirect(const Http::ResponseHeaderMap& headers, bool Filter::convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream_headers, const Http::HeaderEntry& internal_redirect) { - // Make sure the redirect response contains a URL to redirect to. - if (internal_redirect.value().getStringView().length() == 0) { - config_.stats_.passthrough_internal_redirect_no_location_.inc(); - return false; - } if (!downstream_headers.Path()) { - config_.stats_.passthrough_internal_redirect_no_path_.inc(); + ENVOY_STREAM_LOG(trace, "no path in downstream_headers", *callbacks_); return false; } + // Make sure the redirect response contains a URL to redirect to. + if (internal_redirect.value().getStringView().length() == 0) { + config_.stats_.passthrough_internal_redirect_bad_location_.inc(); + return false; + } Http::Utility::Url absolute_url; if (!absolute_url.initialize(internal_redirect.value().getStringView(), false)) { config_.stats_.passthrough_internal_redirect_bad_location_.inc(); diff --git a/source/common/router/router.h b/source/common/router/router.h index 7323a6d8fa09..3bbb54379fab 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -41,8 +41,6 @@ namespace Router { */ // clang-format off #define ALL_ROUTER_STATS(COUNTER) \ - COUNTER(passthrough_internal_redirect_no_location) \ - COUNTER(passthrough_internal_redirect_no_path) \ COUNTER(passthrough_internal_redirect_bad_location) \ COUNTER(passthrough_internal_redirect_unsafe_scheme) \ COUNTER(passthrough_internal_redirect_too_many_redirects) \ diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index ea7fa20fd23b..12f67e0e647b 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -272,6 +272,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithFakeSymbolTable) { // 2020/04/02 10624 43356 44000 Use 100 clusters rather than 1000 to avoid timeouts // 2020/04/07 10661 43349 44000 fix clang tidy on master // 2020/04/23 10531 44169 44600 http: max stream duration upstream support. + // 2020/05/05 10908 44263 44600 router: add InternalRedirectPolicy and predicate // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -285,7 +286,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithFakeSymbolTable) { // If you encounter a failure here, please see // https://github.com/envoyproxy/envoy/blob/master/source/docs/stats.md#stats-memory-tests // for details on how to fix. - EXPECT_MEMORY_EQ(m_per_cluster, 44169); + EXPECT_MEMORY_EQ(m_per_cluster, 44263); EXPECT_MEMORY_LE(m_per_cluster, 44600); } @@ -331,6 +332,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // 2020/04/02 10624 35564 36000 Use 100 clusters rather than 1000 to avoid timeouts // 2020/04/07 10661 35557 36000 fix clang tidy on master // 2020/04/23 10531 36281 36800 http: max stream duration upstream support. + // 2020/05/05 10908 36376 36800 router: add InternalRedirectPolicy and predicate // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -344,7 +346,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // If you encounter a failure here, please see // https://github.com/envoyproxy/envoy/blob/master/source/docs/stats.md#stats-memory-tests // for details on how to fix. - EXPECT_MEMORY_EQ(m_per_cluster, 36281); + EXPECT_MEMORY_EQ(m_per_cluster, 36376); EXPECT_MEMORY_LE(m_per_cluster, 36800); } @@ -377,6 +379,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeHostSizeWithStats) { // 2020/02/13 10042 1363 1655 Metadata object are shared across different clusters // and hosts. // 2020/04/02 10624 1380 1655 Use 100 clusters rather than 1000 to avoid timeouts + // 2020/05/05 10908 1412 1655 router: add InternalRedirectPolicy and predicate // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -386,7 +389,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeHostSizeWithStats) { // If you encounter a failure here, please see // https://github.com/envoyproxy/envoy/blob/master/source/docs/stats.md#stats-memory-tests // for details on how to fix. - EXPECT_MEMORY_EQ(m_per_host, 1380); + EXPECT_MEMORY_EQ(m_per_host, 1412); EXPECT_MEMORY_LE(m_per_host, 1655); } From 5be8361a095d9863fd3fecb2f3bf33af5d81965e Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 5 May 2020 10:31:15 -0400 Subject: [PATCH 37/46] Adjust stats_integration expectation according to CI. Signed-off-by: pengg --- test/integration/stats_integration_test.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/integration/stats_integration_test.cc b/test/integration/stats_integration_test.cc index 12f67e0e647b..9221d3d38381 100644 --- a/test/integration/stats_integration_test.cc +++ b/test/integration/stats_integration_test.cc @@ -272,7 +272,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithFakeSymbolTable) { // 2020/04/02 10624 43356 44000 Use 100 clusters rather than 1000 to avoid timeouts // 2020/04/07 10661 43349 44000 fix clang tidy on master // 2020/04/23 10531 44169 44600 http: max stream duration upstream support. - // 2020/05/05 10908 44263 44600 router: add InternalRedirectPolicy and predicate + // 2020/05/05 10908 44233 44600 router: add InternalRedirectPolicy and predicate // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -286,7 +286,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithFakeSymbolTable) { // If you encounter a failure here, please see // https://github.com/envoyproxy/envoy/blob/master/source/docs/stats.md#stats-memory-tests // for details on how to fix. - EXPECT_MEMORY_EQ(m_per_cluster, 44263); + EXPECT_MEMORY_EQ(m_per_cluster, 44233); EXPECT_MEMORY_LE(m_per_cluster, 44600); } @@ -332,7 +332,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // 2020/04/02 10624 35564 36000 Use 100 clusters rather than 1000 to avoid timeouts // 2020/04/07 10661 35557 36000 fix clang tidy on master // 2020/04/23 10531 36281 36800 http: max stream duration upstream support. - // 2020/05/05 10908 36376 36800 router: add InternalRedirectPolicy and predicate + // 2020/05/05 10908 36345 36800 router: add InternalRedirectPolicy and predicate // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -346,7 +346,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeClusterSizeWithRealSymbolTable) { // If you encounter a failure here, please see // https://github.com/envoyproxy/envoy/blob/master/source/docs/stats.md#stats-memory-tests // for details on how to fix. - EXPECT_MEMORY_EQ(m_per_cluster, 36376); + EXPECT_MEMORY_EQ(m_per_cluster, 36345); EXPECT_MEMORY_LE(m_per_cluster, 36800); } @@ -379,7 +379,6 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeHostSizeWithStats) { // 2020/02/13 10042 1363 1655 Metadata object are shared across different clusters // and hosts. // 2020/04/02 10624 1380 1655 Use 100 clusters rather than 1000 to avoid timeouts - // 2020/05/05 10908 1412 1655 router: add InternalRedirectPolicy and predicate // Note: when adjusting this value: EXPECT_MEMORY_EQ is active only in CI // 'release' builds, where we control the platform and tool-chain. So you @@ -389,7 +388,7 @@ TEST_P(ClusterMemoryTestRunner, MemoryLargeHostSizeWithStats) { // If you encounter a failure here, please see // https://github.com/envoyproxy/envoy/blob/master/source/docs/stats.md#stats-memory-tests // for details on how to fix. - EXPECT_MEMORY_EQ(m_per_host, 1412); + EXPECT_MEMORY_EQ(m_per_host, 1380); EXPECT_MEMORY_LE(m_per_host, 1655); } From 5c1e199a49b9c3176328b48deb6e8931fcabd73a Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 5 May 2020 11:04:33 -0400 Subject: [PATCH 38/46] Fix router_test. Signed-off-by: pengg --- test/common/router/router_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index bc91316442fd..a182113e7c01 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -4467,7 +4467,7 @@ TEST_F(RouterTest, InternalRedirectRejectedWithEmptyLocation) { EXPECT_EQ(1U, cm_.thread_local_cluster_.cluster_.info_->stats_store_ .counter("upstream_internal_redirect_failed_total") .value()); - EXPECT_EQ(1UL, stats_store_.counter("test.passthrough_internal_redirect_no_location").value()); + EXPECT_EQ(1UL, stats_store_.counter("test.passthrough_internal_redirect_bad_location").value()); } TEST_F(RouterTest, InternalRedirectRejectedWithInvalidLocation) { From 8461c5fefb7f41c4950968fbaadf59f92882c6f8 Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 12 May 2020 16:09:02 -0400 Subject: [PATCH 39/46] Move predicate config to use TypedExtensionConfig. Signed-off-by: pengg --- .../config/route/v3/route_components.proto | 3 +- .../route/v4alpha/route_components.proto | 3 +- .../config/route/v3/route_components.proto | 557 +++++++++--------- .../route/v4alpha/route_components.proto | 3 +- source/common/router/config_impl.cc | 5 +- test/integration/redirect_integration_test.cc | 7 +- 6 files changed, 293 insertions(+), 285 deletions(-) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 5119c81eac85..c016500bb773 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.config.route.v3; import "envoy/config/core/v3/base.proto"; +import "envoy/config/core/v3/extension.proto"; import "envoy/config/core/v3/proxy_protocol.proto"; import "envoy/type/matcher/v3/regex.proto"; import "envoy/type/matcher/v3/string.proto"; @@ -1621,7 +1622,7 @@ message InternalRedirectPolicy { // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated google.protobuf.Any predicates = 3; + repeated core.v3.TypedExtensionConfig predicates = 3; // Disallow internal redirect to follow a target URI with a different scheme than the value of // x-forwarded-proto. Default behavior is to follow such redirects. diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 6f3f47241c56..c4876929170d 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.config.route.v4alpha; import "envoy/config/core/v4alpha/base.proto"; +import "envoy/config/core/v4alpha/extension.proto"; import "envoy/config/core/v4alpha/proxy_protocol.proto"; import "envoy/type/matcher/v4alpha/regex.proto"; import "envoy/type/matcher/v4alpha/string.proto"; @@ -1616,7 +1617,7 @@ message InternalRedirectPolicy { // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated google.protobuf.Any predicates = 3; + repeated core.v4alpha.TypedExtensionConfig predicates = 3; // Disallow internal redirect to follow a target URI with a different scheme than the value of // x-forwarded-proto. Default behavior is to follow such redirects. diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index 4ddac72fef09..a9506b6262b5 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.config.route.v3; import "envoy/config/core/v3/base.proto"; +import "envoy/config/core/v3/extension.proto"; import "envoy/config/core/v3/proxy_protocol.proto"; import "envoy/type/matcher/v3/regex.proto"; import "envoy/type/matcher/v3/string.proto"; @@ -208,40 +209,40 @@ message Route { // Route matching parameters. RouteMatch match = 1 [(validate.rules).message = {required: true}]; - // Route request to some upstream cluster. - core.v3.Metadata metadata = 4; + oneof action { + option (validate.required) = true; - // Return a redirect. - Decorator decorator = 5; + // Route request to some upstream cluster. + RouteAction route = 2; - // Return an arbitrary HTTP response directly, without proxying. - map typed_per_filter_config = 13; + // Return a redirect. + RedirectAction redirect = 3; - // [#not-implemented-hide:] - // If true, a filter will define the action (e.g., it could dynamically generate the - // RouteAction). - repeated core.v3.HeaderValueOption request_headers_to_add = 9 - [(validate.rules).repeated = {max_items: 1000}]; + // Return an arbitrary HTTP response directly, without proxying. + DirectResponseAction direct_response = 7; + + // [#not-implemented-hide:] + // If true, a filter will define the action (e.g., it could dynamically generate the + // RouteAction). + FilterAction filter_action = 17; + } // The Metadata field can be used to provide additional information // about the route. It can be used for configuration, stats, and logging. // The metadata should go under the filter namespace that will need it. // For instance, if the metadata is intended for the Router filter, // the filter name should be specified as *envoy.filters.http.router*. - repeated string request_headers_to_remove = 12 [(validate.rules).repeated = { - items {string {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME strict: false}} - }]; + core.v3.Metadata metadata = 4; // Decorator for the matched route. - repeated core.v3.HeaderValueOption response_headers_to_add = 10 - [(validate.rules).repeated = {max_items: 1000}]; + Decorator decorator = 5; // The typed_per_filter_config field can be used to provide route-specific // configurations for filters. The key should match the filter name, such as // *envoy.filters.http.buffer* for the HTTP buffer filter. Use of this field is filter // specific; see the :ref:`HTTP filter documentation ` for // if and how it is utilized. - repeated string response_headers_to_remove = 11; + map typed_per_filter_config = 13; // Specifies a set of headers that will be added to requests matching this // route. Headers specified at this level are applied before headers from the @@ -249,11 +250,14 @@ message Route { // :ref:`envoy_api_msg_config.route.v3.RouteConfiguration`. For more information, including details on // header value syntax, see the documentation on :ref:`custom request headers // `. - Tracing tracing = 15; + repeated core.v3.HeaderValueOption request_headers_to_add = 9 + [(validate.rules).repeated = {max_items: 1000}]; // Specifies a list of HTTP headers that should be removed from each request // matching this route. - google.protobuf.UInt32Value per_request_buffer_limit_bytes = 16; + repeated string request_headers_to_remove = 12 [(validate.rules).repeated = { + items {string {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME strict: false}} + }]; // Specifies a set of headers that will be added to responses to requests // matching this route. Headers specified at this level are applied before @@ -261,27 +265,24 @@ message Route { // :ref:`envoy_api_msg_config.route.v3.RouteConfiguration`. For more information, including // details on header value syntax, see the documentation on // :ref:`custom request headers `. - map hidden_envoy_deprecated_per_filter_config = 8 - [deprecated = true]; - - oneof action { - option (validate.required) = true; + repeated core.v3.HeaderValueOption response_headers_to_add = 10 + [(validate.rules).repeated = {max_items: 1000}]; - // Specifies a list of HTTP headers that should be removed from each response - // to requests matching this route. - RouteAction route = 2; + // Specifies a list of HTTP headers that should be removed from each response + // to requests matching this route. + repeated string response_headers_to_remove = 11; - // Presence of the object defines whether the connection manager's tracing configuration - // is overridden by this route specific instance. - RedirectAction redirect = 3; + // Presence of the object defines whether the connection manager's tracing configuration + // is overridden by this route specific instance. + Tracing tracing = 15; - // The maximum bytes which will be buffered for retries and shadowing. - // If set, the bytes actually buffered will be the minimum value of this and the - // listener per_connection_buffer_limit_bytes. - DirectResponseAction direct_response = 7; + // The maximum bytes which will be buffered for retries and shadowing. + // If set, the bytes actually buffered will be the minimum value of this and the + // listener per_connection_buffer_limit_bytes. + google.protobuf.UInt32Value per_request_buffer_limit_bytes = 16; - FilterAction filter_action = 17; - } + map hidden_envoy_deprecated_per_filter_config = 8 + [deprecated = true]; } // Compared to the :ref:`cluster ` field that specifies a @@ -403,41 +404,51 @@ message RouteMatch { reserved 5; - // If specified, the route is a prefix rule meaning that the prefix must - // match the beginning of the *:path* header. - google.protobuf.BoolValue case_sensitive = 4; + oneof path_specifier { + option (validate.required) = true; - // If specified, the route is an exact path rule meaning that the path must - // exactly match the *:path* header once the query string is removed. - core.v3.RuntimeFractionalPercent runtime_fraction = 9; + // If specified, the route is a prefix rule meaning that the prefix must + // match the beginning of the *:path* header. + string prefix = 1; - // If specified, the route is a regular expression rule meaning that the - // regex must match the *:path* header once the query string is removed. The entire path - // (without the query string) must match the regex. The rule will not match if only a - // subsequence of the *:path* header matches the regex. - // - // [#next-major-version: In the v3 API we should redo how path specification works such - // that we utilize StringMatcher, and additionally have consistent options around whether we - // strip query strings, do a case sensitive match, etc. In the interim it will be too disruptive - // to deprecate the existing options. We should even consider whether we want to do away with - // path_specifier entirely and just rely on a set of header matchers which can already match - // on :path, etc. The issue with that is it is unclear how to generically deal with query string - // stripping. This needs more thought.] - repeated HeaderMatcher headers = 6; + // If specified, the route is an exact path rule meaning that the path must + // exactly match the *:path* header once the query string is removed. + string path = 2; - // [#not-implemented-hide:] - // If this is used as the matcher, the matcher will only match CONNECT requests. - // Note that this will not match HTTP/2 upgrade-style CONNECT requests - // (WebSocket and the like) as they are normalized in Envoy as HTTP/1.1 style - // upgrades. - // This is the only way to match CONNECT requests for HTTP/1.1. For HTTP/2, - // where CONNECT requests may have a path, the path matchers will work if - // there is a path present. - repeated QueryParameterMatcher query_parameters = 7; + // If specified, the route is a regular expression rule meaning that the + // regex must match the *:path* header once the query string is removed. The entire path + // (without the query string) must match the regex. The rule will not match if only a + // subsequence of the *:path* header matches the regex. + // + // [#next-major-version: In the v3 API we should redo how path specification works such + // that we utilize StringMatcher, and additionally have consistent options around whether we + // strip query strings, do a case sensitive match, etc. In the interim it will be too disruptive + // to deprecate the existing options. We should even consider whether we want to do away with + // path_specifier entirely and just rely on a set of header matchers which can already match + // on :path, etc. The issue with that is it is unclear how to generically deal with query string + // stripping. This needs more thought.] + type.matcher.v3.RegexMatcher safe_regex = 10 [(validate.rules).message = {required: true}]; + + // [#not-implemented-hide:] + // If this is used as the matcher, the matcher will only match CONNECT requests. + // Note that this will not match HTTP/2 upgrade-style CONNECT requests + // (WebSocket and the like) as they are normalized in Envoy as HTTP/1.1 style + // upgrades. + // This is the only way to match CONNECT requests for HTTP/1.1. For HTTP/2, + // where CONNECT requests may have a path, the path matchers will work if + // there is a path present. + ConnectMatcher connect_matcher = 12; + + string hidden_envoy_deprecated_regex = 3 [ + deprecated = true, + (validate.rules).string = {max_bytes: 1024}, + (envoy.annotations.disallowed_by_default) = true + ]; + } // Indicates that prefix/path matching should be case insensitive. The default // is true. - GrpcRouteMatchOptions grpc = 8; + google.protobuf.BoolValue case_sensitive = 4; // Indicates that the route should additionally match on a runtime key. Every time the route // is considered for a match, it must also fall under the percentage of matches indicated by @@ -455,42 +466,32 @@ message RouteMatch { // integer with the assumption that the value is an integral percentage out of 100. For // instance, a runtime key lookup returning the value "42" would parse as a FractionalPercent // whose numerator is 42 and denominator is HUNDRED. This preserves legacy semantics. - TlsContextMatchOptions tls_context = 11; - - oneof path_specifier { - option (validate.required) = true; - - // Specifies a set of headers that the route should match on. The router will - // check the request’s headers against all the specified headers in the route - // config. A match will happen if all the headers in the route are present in - // the request with the same values (or based on presence if the value field - // is not in the config). - string prefix = 1; + core.v3.RuntimeFractionalPercent runtime_fraction = 9; - // Specifies a set of URL query parameters on which the route should - // match. The router will check the query string from the *path* header - // against all the specified query parameters. If the number of specified - // query parameters is nonzero, they all must match the *path* header's - // query string for a match to occur. - string path = 2; + // Specifies a set of headers that the route should match on. The router will + // check the request’s headers against all the specified headers in the route + // config. A match will happen if all the headers in the route are present in + // the request with the same values (or based on presence if the value field + // is not in the config). + repeated HeaderMatcher headers = 6; - // If specified, only gRPC requests will be matched. The router will check - // that the content-type header has a application/grpc or one of the various - // application/grpc+ values. - type.matcher.v3.RegexMatcher safe_regex = 10 [(validate.rules).message = {required: true}]; + // Specifies a set of URL query parameters on which the route should + // match. The router will check the query string from the *path* header + // against all the specified query parameters. If the number of specified + // query parameters is nonzero, they all must match the *path* header's + // query string for a match to occur. + repeated QueryParameterMatcher query_parameters = 7; - // If specified, the client tls context will be matched against the defined - // match options. - // - // [#next-major-version: unify with RBAC] - ConnectMatcher connect_matcher = 12; + // If specified, only gRPC requests will be matched. The router will check + // that the content-type header has a application/grpc or one of the various + // application/grpc+ values. + GrpcRouteMatchOptions grpc = 8; - string hidden_envoy_deprecated_regex = 3 [ - deprecated = true, - (validate.rules).string = {max_bytes: 1024}, - (envoy.annotations.disallowed_by_default) = true - ]; - } + // If specified, the client tls context will be matched against the defined + // match options. + // + // [#next-major-version: unify with RBAC] + TlsContextMatchOptions tls_context = 11; } // [#next-free-field: 12] @@ -516,14 +517,19 @@ message CorsPolicy { // Specifies whether the resource allows credentials. google.protobuf.BoolValue allow_credentials = 6; - // Specifies the % of requests for which the CORS filter is enabled. - // - // If neither ``enabled``, ``filter_enabled``, nor ``shadow_enabled`` are specified, the CORS - // filter will be enabled for 100% of the requests. - // - // If :ref:`runtime_key ` is - // specified, Envoy will lookup the runtime key to get the percentage of requests to filter. - core.v3.RuntimeFractionalPercent shadow_enabled = 10; + oneof enabled_specifier { + // Specifies the % of requests for which the CORS filter is enabled. + // + // If neither ``enabled``, ``filter_enabled``, nor ``shadow_enabled`` are specified, the CORS + // filter will be enabled for 100% of the requests. + // + // If :ref:`runtime_key ` is + // specified, Envoy will lookup the runtime key to get the percentage of requests to filter. + core.v3.RuntimeFractionalPercent filter_enabled = 9; + + google.protobuf.BoolValue hidden_envoy_deprecated_enabled = 7 + [deprecated = true, (envoy.annotations.disallowed_by_default) = true]; + } // Specifies the % of requests for which the CORS policies will be evaluated and tracked, but not // enforced. @@ -534,18 +540,13 @@ message CorsPolicy { // If :ref:`runtime_key ` is specified, // Envoy will lookup the runtime key to get the percentage of requests for which it will evaluate // and track the request's *Origin* to determine if it's valid but will not enforce any policies. + core.v3.RuntimeFractionalPercent shadow_enabled = 10; + repeated string hidden_envoy_deprecated_allow_origin = 1 [deprecated = true, (envoy.annotations.disallowed_by_default) = true]; repeated string hidden_envoy_deprecated_allow_origin_regex = 8 [deprecated = true, (validate.rules).repeated = {items {string {max_bytes: 1024}}}]; - - oneof enabled_specifier { - core.v3.RuntimeFractionalPercent filter_enabled = 9; - - google.protobuf.BoolValue hidden_envoy_deprecated_enabled = 7 - [deprecated = true, (envoy.annotations.disallowed_by_default) = true]; - } } // [#next-free-field: 35] @@ -682,45 +683,45 @@ message RouteAction { string key = 1 [(validate.rules).string = {min_bytes: 1}]; } - // Header hash policy. - bool terminal = 4; - oneof policy_specifier { option (validate.required) = true; - // Cookie hash policy. + // Header hash policy. Header header = 1; - // Connection properties hash policy. + // Cookie hash policy. Cookie cookie = 2; - // Query parameter hash policy. + // Connection properties hash policy. ConnectionProperties connection_properties = 3; - // Filter state hash policy. + // Query parameter hash policy. QueryParameter query_parameter = 5; - // The flag that short-circuits the hash computing. This field provides a - // 'fallback' style of configuration: "if a terminal policy doesn't work, - // fallback to rest of the policy list", it saves time when the terminal - // policy works. - // - // If true, and there is already a hash computed, ignore rest of the - // list of hash polices. - // For example, if the following hash methods are configured: - // - // ========= ======== - // specifier terminal - // ========= ======== - // Header A true - // Header B false - // Header C false - // ========= ======== - // - // The generateHash process ends if policy "header A" generates a hash, as - // it's a terminal policy. + // Filter state hash policy. FilterState filter_state = 6; } + + // The flag that short-circuits the hash computing. This field provides a + // 'fallback' style of configuration: "if a terminal policy doesn't work, + // fallback to rest of the policy list", it saves time when the terminal + // policy works. + // + // If true, and there is already a hash computed, ignore rest of the + // list of hash polices. + // For example, if the following hash methods are configured: + // + // ========= ======== + // specifier terminal + // ========= ======== + // Header A true + // Header B false + // Header C false + // ========= ======== + // + // The generateHash process ends if policy "header A" generates a hash, as + // it's a terminal policy. + bool terminal = 4; } // Allows enabling and disabling upgrades on a per-route basis. @@ -758,40 +759,44 @@ message RouteAction { reserved 12, 18, 19, 16, 22, 21; - // Indicates the upstream cluster to which the request should be routed - // to. - ClusterNotFoundResponseCode cluster_not_found_response_code = 20 - [(validate.rules).enum = {defined_only: true}]; + oneof cluster_specifier { + option (validate.required) = true; - // Envoy will determine the cluster to route to by reading the value of the - // HTTP header named by cluster_header from the request headers. If the - // header is not found or the referenced cluster does not exist, Envoy will - // return a 404 response. - // - // .. attention:: - // - // Internally, Envoy always uses the HTTP/2 *:authority* header to represent the HTTP/1 - // *Host* header. Thus, if attempting to match on *Host*, match on *:authority* instead. - core.v3.Metadata metadata_match = 4; + // Indicates the upstream cluster to which the request should be routed + // to. + string cluster = 1 [(validate.rules).string = {min_bytes: 1}]; - // Multiple upstream clusters can be specified for a given route. The - // request is routed to one of the upstream clusters based on weights - // assigned to each cluster. See - // :ref:`traffic splitting ` - // for additional documentation. - string prefix_rewrite = 5 - [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; + // Envoy will determine the cluster to route to by reading the value of the + // HTTP header named by cluster_header from the request headers. If the + // header is not found or the referenced cluster does not exist, Envoy will + // return a 404 response. + // + // .. attention:: + // + // Internally, Envoy always uses the HTTP/2 *:authority* header to represent the HTTP/1 + // *Host* header. Thus, if attempting to match on *Host*, match on *:authority* instead. + string cluster_header = 2 + [(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME strict: false}]; + + // Multiple upstream clusters can be specified for a given route. The + // request is routed to one of the upstream clusters based on weights + // assigned to each cluster. See + // :ref:`traffic splitting ` + // for additional documentation. + WeightedCluster weighted_clusters = 3; + } // The HTTP status code to use when configured cluster is not found. // The default response code is 503 Service Unavailable. - type.matcher.v3.RegexMatchAndSubstitute regex_rewrite = 32; + ClusterNotFoundResponseCode cluster_not_found_response_code = 20 + [(validate.rules).enum = {defined_only: true}]; // Optional endpoint metadata match criteria used by the subset load balancer. Only endpoints // in the upstream cluster with metadata matching what's set in this field will be considered // for load balancing. If using :ref:`weighted_clusters // `, metadata will be merged, with values // provided there taking precedence. The filter name should be specified as *envoy.lb*. - google.protobuf.Duration timeout = 8; + core.v3.Metadata metadata_match = 4; // Indicates that during forwarding, the matched prefix (or path) should be // swapped with this value. This option allows application URLs to be rooted @@ -824,7 +829,8 @@ message RouteAction { // // Having above entries in the config, requests to */prefix* will be stripped to */*, while // requests to */prefix/etc* will be stripped to */etc*. - google.protobuf.Duration idle_timeout = 24; + string prefix_rewrite = 5 + [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; // Indicates that during forwarding, portions of the path that match the // pattern should be rewritten, even allowing the substitution of capture @@ -854,28 +860,32 @@ message RouteAction { // * The pattern ``(?i)/xxx/`` paired with a substitution string of ``/yyy/`` // would do a case-insensitive match and transform path ``/aaa/XxX/bbb`` to // ``/aaa/yyy/bbb``. - RetryPolicy retry_policy = 9; + type.matcher.v3.RegexMatchAndSubstitute regex_rewrite = 32; - // Indicates that during forwarding, the host header will be swapped with - // this value. - google.protobuf.Any retry_policy_typed_config = 33; + oneof host_rewrite_specifier { + // Indicates that during forwarding, the host header will be swapped with + // this value. + string host_rewrite_literal = 6 + [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; - // Indicates that during forwarding, the host header will be swapped with - // the hostname of the upstream host chosen by the cluster manager. This - // option is applicable only when the destination cluster for a route is of - // type *strict_dns* or *logical_dns*. Setting this to true with other cluster - // types has no effect. - repeated RequestMirrorPolicy request_mirror_policies = 30; + // Indicates that during forwarding, the host header will be swapped with + // the hostname of the upstream host chosen by the cluster manager. This + // option is applicable only when the destination cluster for a route is of + // type *strict_dns* or *logical_dns*. Setting this to true with other cluster + // types has no effect. + google.protobuf.BoolValue auto_host_rewrite = 7; - // Indicates that during forwarding, the host header will be swapped with the content of given - // downstream or :ref:`custom ` header. - // If header value is empty, host header is left intact. - // - // .. attention:: - // - // Pay attention to the potential security implications of using this option. Provided header - // must come from trusted source. - core.v3.RoutingPriority priority = 11 [(validate.rules).enum = {defined_only: true}]; + // Indicates that during forwarding, the host header will be swapped with the content of given + // downstream or :ref:`custom ` header. + // If header value is empty, host header is left intact. + // + // .. attention:: + // + // Pay attention to the potential security implications of using this option. Provided header + // must come from trusted source. + string host_rewrite_header = 29 + [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME strict: false}]; + } // Specifies the upstream timeout for the route. If not specified, the default is 15s. This // spans between the point at which the entire downstream request (i.e. end-of-stream) has been @@ -888,7 +898,7 @@ message RouteAction { // :ref:`config_http_filters_router_x-envoy-upstream-rq-timeout-ms`, // :ref:`config_http_filters_router_x-envoy-upstream-rq-per-try-timeout-ms`, and the // :ref:`retry overview `. - repeated RateLimit rate_limits = 13; + google.protobuf.Duration timeout = 8; // Specifies the idle timeout for the route. If not specified, there is no per-route idle timeout, // although the connection manager wide :ref:`stream_idle_timeout @@ -908,35 +918,35 @@ message RouteAction { // fires, the stream is terminated with a 408 Request Timeout error code if no // upstream response header has been received, otherwise a stream reset // occurs. - google.protobuf.BoolValue include_vh_rate_limits = 14; + google.protobuf.Duration idle_timeout = 24; // Indicates that the route has a retry policy. Note that if this is set, // it'll take precedence over the virtual host level retry policy entirely // (e.g.: policies are not merged, most internal one becomes the enforced policy). - repeated HashPolicy hash_policy = 15; + RetryPolicy retry_policy = 9; // [#not-implemented-hide:] // Specifies the configuration for retry policy extension. Note that if this is set, it'll take // precedence over the virtual host level retry policy entirely (e.g.: policies are not merged, // most internal one becomes the enforced policy). :ref:`Retry policy ` // should not be set if this field is used. - CorsPolicy cors = 17; + google.protobuf.Any retry_policy_typed_config = 33; // Indicates that the route has request mirroring policies. - google.protobuf.Duration max_grpc_timeout = 23; + repeated RequestMirrorPolicy request_mirror_policies = 30; // Optionally specifies the :ref:`routing priority `. - google.protobuf.Duration grpc_timeout_offset = 28; + core.v3.RoutingPriority priority = 11 [(validate.rules).enum = {defined_only: true}]; // Specifies a set of rate limit configurations that could be applied to the // route. - repeated UpgradeConfig upgrade_configs = 25; + repeated RateLimit rate_limits = 13; // Specifies if the rate limit filter should include the virtual host rate // limits. By default, if the route configured rate limits, the virtual host // :ref:`rate_limits ` are not applied to the // request. - InternalRedirectPolicy internal_redirect_policy = 34; + google.protobuf.BoolValue include_vh_rate_limits = 14; // Specifies a list of hash policies to use for ring hash load balancing. Each // hash policy is evaluated individually and the combined result is used to @@ -950,10 +960,10 @@ message RouteAction { // backend). If a hash policy has the "terminal" attribute set to true, and // there is already a hash generated, the hash is returned immediately, // ignoring the rest of the hash policy list. - InternalRedirectAction internal_redirect_action = 26 [deprecated = true]; + repeated HashPolicy hash_policy = 15; // Indicates that the route has a CORS policy. - google.protobuf.UInt32Value max_internal_redirects = 31 [deprecated = true]; + CorsPolicy cors = 17; // If present, and the request is a gRPC request, use the // `grpc-timeout header `_, @@ -974,7 +984,7 @@ message RouteAction { // :ref:`config_http_filters_router_x-envoy-upstream-rq-timeout-ms`, // :ref:`config_http_filters_router_x-envoy-upstream-rq-per-try-timeout-ms`, and the // :ref:`retry overview `. - HedgePolicy hedge_policy = 27; + google.protobuf.Duration max_grpc_timeout = 23; // If present, Envoy will adjust the timeout provided by the `grpc-timeout` header by subtracting // the provided duration from the header. This is useful in allowing Envoy to set its global @@ -983,49 +993,40 @@ message RouteAction { // The offset will only be applied if the provided grpc_timeout is greater than the offset. This // ensures that the offset will only ever decrease the timeout and never set it to 0 (meaning // infinity). - RequestMirrorPolicy hidden_envoy_deprecated_request_mirror_policy = 10 [deprecated = true]; - - oneof cluster_specifier { - option (validate.required) = true; + google.protobuf.Duration grpc_timeout_offset = 28; - string cluster = 1 [(validate.rules).string = {min_bytes: 1}]; + repeated UpgradeConfig upgrade_configs = 25; - // If present, Envoy will try to follow an upstream redirect response instead of proxying the - // response back to the downstream. An upstream redirect response is defined - // by :ref:`redirect_response_codes - // `. - string cluster_header = 2 - [(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME strict: false}]; + // If present, Envoy will try to follow an upstream redirect response instead of proxying the + // response back to the downstream. An upstream redirect response is defined + // by :ref:`redirect_response_codes + // `. + InternalRedirectPolicy internal_redirect_policy = 34; - WeightedCluster weighted_clusters = 3; - } + InternalRedirectAction internal_redirect_action = 26 [deprecated = true]; - oneof host_rewrite_specifier { - // An internal redirect is handled, iff the number of previous internal redirects that a - // downstream request has encountered is lower than this value, and - // :ref:`internal_redirect_action ` - // is set to :ref:`HANDLE_INTERNAL_REDIRECT - // ` - // In the case where a downstream request is bounced among multiple routes by internal redirect, - // the first route that hits this threshold, or has - // :ref:`internal_redirect_action ` - // set to - // :ref:`PASS_THROUGH_INTERNAL_REDIRECT - // ` - // will pass the redirect back to downstream. - // - // If not specified, at most one redirect will be followed. - string host_rewrite_literal = 6 - [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; + // An internal redirect is handled, iff the number of previous internal redirects that a + // downstream request has encountered is lower than this value, and + // :ref:`internal_redirect_action ` + // is set to :ref:`HANDLE_INTERNAL_REDIRECT + // ` + // In the case where a downstream request is bounced among multiple routes by internal redirect, + // the first route that hits this threshold, or has + // :ref:`internal_redirect_action ` + // set to + // :ref:`PASS_THROUGH_INTERNAL_REDIRECT + // ` + // will pass the redirect back to downstream. + // + // If not specified, at most one redirect will be followed. + google.protobuf.UInt32Value max_internal_redirects = 31 [deprecated = true]; - // Indicates that the route has a hedge policy. Note that if this is set, - // it'll take precedence over the virtual host level hedge policy entirely - // (e.g.: policies are not merged, most internal one becomes the enforced policy). - google.protobuf.BoolValue auto_host_rewrite = 7; + // Indicates that the route has a hedge policy. Note that if this is set, + // it'll take precedence over the virtual host level hedge policy entirely + // (e.g.: policies are not merged, most internal one becomes the enforced policy). + HedgePolicy hedge_policy = 27; - string host_rewrite_header = 29 - [(validate.rules).string = {well_known_regex: HTTP_HEADER_NAME strict: false}]; - } + RequestMirrorPolicy hidden_envoy_deprecated_request_mirror_policy = 10 [deprecated = true]; } // HTTP retry :ref:`architecture overview `. @@ -1185,28 +1186,31 @@ message RedirectAction { PERMANENT_REDIRECT = 4; } - // The scheme portion of the URL will be swapped with "https". - string host_redirect = 1 - [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; - - // The scheme portion of the URL will be swapped with this value. - uint32 port_redirect = 8; - - // The host portion of the URL will be swapped with this value. - RedirectResponseCode response_code = 3 [(validate.rules).enum = {defined_only: true}]; - - // The port value of the URL will be swapped with this value. - bool strip_query = 6; - // When the scheme redirection take place, the following rules apply: // 1. If the source URI scheme is `http` and the port is explicitly // set to `:80`, the port will be removed after the redirection // 2. If the source URI scheme is `https` and the port is explicitly // set to `:443`, the port will be removed after the redirection oneof scheme_rewrite_specifier { - // The path portion of the URL will be swapped with this value. + // The scheme portion of the URL will be swapped with "https". bool https_redirect = 4; + // The scheme portion of the URL will be swapped with this value. + string scheme_redirect = 7; + } + + // The host portion of the URL will be swapped with this value. + string host_redirect = 1 + [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; + + // The port value of the URL will be swapped with this value. + uint32 port_redirect = 8; + + oneof path_rewrite_specifier { + // The path portion of the URL will be swapped with this value. + string path_redirect = 2 + [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; + // Indicates that during redirection, the matched prefix (or path) // should be swapped with this value. This option allows redirect URLs be dynamically created // based on the request. @@ -1215,20 +1219,17 @@ message RedirectAction { // // Pay attention to the use of trailing slashes as mentioned in // :ref:`RouteAction's prefix_rewrite `. - string scheme_redirect = 7; - } - - oneof path_rewrite_specifier { - // The HTTP status code to use in the redirect response. The default response - // code is MOVED_PERMANENTLY (301). - string path_redirect = 2 - [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; - - // Indicates that during redirection, the query portion of the URL will - // be removed. Default value is false. string prefix_rewrite = 5 [(validate.rules).string = {well_known_regex: HTTP_HEADER_VALUE strict: false}]; } + + // The HTTP status code to use in the redirect response. The default response + // code is MOVED_PERMANENTLY (301). + RedirectResponseCode response_code = 3 [(validate.rules).enum = {defined_only: true}]; + + // Indicates that during redirection, the query portion of the URL will + // be removed. Default value is false. + bool strip_query = 6; } message DirectResponseAction { @@ -1534,15 +1535,15 @@ message HeaderMatcher { string name = 1 [(validate.rules).string = {min_bytes: 1 well_known_regex: HTTP_HEADER_NAME strict: false}]; - // If specified, header match will be performed based on the value of the header. - bool invert_match = 8; - // Specifies how the header match will be performed to route the request. oneof header_match_specifier { + // If specified, header match will be performed based on the value of the header. + string exact_match = 4; + // If specified, this regex string is a regular expression rule which implies the entire request // header value must match the regex. The rule will not match if only a subsequence of the // request header value matches the regex. - string exact_match = 4; + type.matcher.v3.RegexMatcher safe_regex_match = 11; // If specified, header match will be performed based on range. // The rule will match if the request header value is within this range. @@ -1555,11 +1556,11 @@ message HeaderMatcher { // // * For range [-10,0), route will match for header value -1, but not for 0, "somestring", 10.9, // "-1somestring" - type.matcher.v3.RegexMatcher safe_regex_match = 11; + type.v3.Int64Range range_match = 6; // If specified, header match will be performed based on whether the header is in the // request. - type.v3.Int64Range range_match = 6; + bool present_match = 7; // If specified, header match will be performed based on the prefix of the header value. // Note: empty prefix is not allowed, please use present_match instead. @@ -1567,7 +1568,7 @@ message HeaderMatcher { // Examples: // // * The prefix *abcd* matches the value *abcdxyz*, but not for *abcxyz*. - bool present_match = 7; + string prefix_match = 9 [(validate.rules).string = {min_bytes: 1}]; // If specified, header match will be performed based on the suffix of the header value. // Note: empty suffix is not allowed, please use present_match instead. @@ -1575,14 +1576,6 @@ message HeaderMatcher { // Examples: // // * The suffix *abcd* matches the value *xyzabcd*, but not for *xyzbcd*. - string prefix_match = 9 [(validate.rules).string = {min_bytes: 1}]; - - // If specified, the match result will be inverted before checking. Defaults to false. - // - // Examples: - // - // * The regex ``\d{3}`` does not match the value *1234*, so it will match when inverted. - // * The range [-10,0) will match the value -1, so it will not match when inverted. string suffix_match = 10 [(validate.rules).string = {min_bytes: 1}]; string hidden_envoy_deprecated_regex_match = 5 [ @@ -1591,6 +1584,14 @@ message HeaderMatcher { (envoy.annotations.disallowed_by_default) = true ]; } + + // If specified, the match result will be inverted before checking. Defaults to false. + // + // Examples: + // + // * The regex ``\d{3}`` does not match the value *1234*, so it will match when inverted. + // * The range [-10,0) will match the value -1, so it will not match when inverted. + bool invert_match = 8; } // Query parameter matching treats the query string of a request's :path header @@ -1604,19 +1605,19 @@ message QueryParameterMatcher { // *path*'s query string. string name = 1 [(validate.rules).string = {min_bytes: 1 max_bytes: 1024}]; - // Specifies whether a query parameter value should match against a string. - string hidden_envoy_deprecated_value = 3 - [deprecated = true, (envoy.annotations.disallowed_by_default) = true]; - - // Specifies whether a query parameter should be present. - google.protobuf.BoolValue hidden_envoy_deprecated_regex = 4 - [deprecated = true, (envoy.annotations.disallowed_by_default) = true]; - oneof query_parameter_match_specifier { + // Specifies whether a query parameter value should match against a string. type.matcher.v3.StringMatcher string_match = 5 [(validate.rules).message = {required: true}]; + // Specifies whether a query parameter should be present. bool present_match = 6; } + + string hidden_envoy_deprecated_value = 3 + [deprecated = true, (envoy.annotations.disallowed_by_default) = true]; + + google.protobuf.BoolValue hidden_envoy_deprecated_regex = 4 + [deprecated = true, (envoy.annotations.disallowed_by_default) = true]; } // HTTP Internal Redirect :ref:`architecture overview `. @@ -1639,7 +1640,7 @@ message InternalRedirectPolicy { // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated google.protobuf.Any predicates = 3; + repeated core.v3.TypedExtensionConfig predicates = 3; // Disallow internal redirect to follow a target URI with a different scheme than the value of // x-forwarded-proto. Default behavior is to follow such redirects. diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index 55ee65e331d5..be5caa1959ea 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package envoy.config.route.v4alpha; import "envoy/config/core/v4alpha/base.proto"; +import "envoy/config/core/v4alpha/extension.proto"; import "envoy/config/core/v4alpha/proxy_protocol.proto"; import "envoy/type/matcher/v4alpha/regex.proto"; import "envoy/type/matcher/v4alpha/string.proto"; @@ -1635,7 +1636,7 @@ message InternalRedirectPolicy { // Specifies a list of predicates that are queried when an upstream response is deemed // to trigger an internal redirect by all other criteria. Any predicate in the list can reject // the redirect, causing the response to be proxied to downstream. - repeated google.protobuf.Any predicates = 3; + repeated core.v4alpha.TypedExtensionConfig predicates = 3; // Disallow internal redirect to follow a target URI with a different scheme than the value of // x-forwarded-proto. Default behavior is to follow such redirects. diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index bd65b29565fa..7b8034f3af96 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -154,12 +154,13 @@ InternalRedirectPolicyImpl::InternalRedirectPolicyImpl( enabled_(true), disallow_cross_scheme_redirect_(policy_config.disallow_cross_scheme_redirect()) { for (const auto& predicate : policy_config.predicates()) { - const std::string type{TypeUtil::typeUrlToDescriptorFullName(predicate.type_url())}; + const std::string type{ + TypeUtil::typeUrlToDescriptorFullName(predicate.typed_config().type_url())}; auto* factory = Registry::FactoryRegistry::getFactoryByType(type); auto config = factory->createEmptyConfigProto(); - Envoy::Config::Utility::translateOpaqueConfig(predicate, {}, validator, *config); + Envoy::Config::Utility::translateOpaqueConfig(predicate.typed_config(), {}, validator, *config); predicate_factories_.emplace_back(factory, std::move(config)); } } diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index 6369c95394fe..ff956ca26837 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -235,7 +235,9 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByPreviousRoutesPredica internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); envoy::extensions::internal_redirect::previous_routes::v3::PreviousRoutesConfig previous_routes_config; - internal_redirect_policy->add_predicates()->PackFrom(previous_routes_config); + auto* predicate = internal_redirect_policy->add_predicates(); + predicate->set_name("previous_routes"); + predicate->mutable_typed_config()->PackFrom(previous_routes_config); config_helper_.addVirtualHost(handle_prevent_repeated_target); // Validate that header sanitization is only called once. @@ -286,10 +288,11 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowListedRoutesPred ->mutable_internal_redirect_policy(); auto* allow_listed_routes_predicate = internal_redirect_policy->add_predicates(); + allow_listed_routes_predicate->set_name("allow_listed_routes"); envoy::extensions::internal_redirect::allow_listed_routes::v3::AllowListedRoutesConfig allow_listed_routes_config; *allow_listed_routes_config.add_allowed_route_names() = "max_three_hop"; - allow_listed_routes_predicate->PackFrom(allow_listed_routes_config); + allow_listed_routes_predicate->mutable_typed_config()->PackFrom(allow_listed_routes_config); internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); From 56b01881114a3001e17a5759e958b9b408d1ea76 Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 12 May 2020 18:15:06 -0400 Subject: [PATCH 40/46] Implete OnlyAllowSafeCrossSchemeRedirect internal redirect predicate. Signed-off-by: pengg --- api/BUILD | 1 + .../v3/BUILD | 9 +++ ...ow_safe_cross_scheme_redirect_config.proto | 24 ++++++++ api/versioning/BUILD | 1 + .../v3/BUILD | 9 +++ ...ow_safe_cross_scheme_redirect_config.proto | 24 ++++++++ include/envoy/router/internal_redirect.h | 3 +- source/common/router/router.cc | 3 +- source/extensions/extensions_build_config.bzl | 1 + .../allow_listed_routes/allow_listed_routes.h | 4 +- .../allow_listed_routes/config.h | 6 +- .../BUILD | 34 +++++++++++ .../config.cc | 15 +++++ .../config.h | 36 +++++++++++ .../only_allow_safe_cross_scheme_redirect.h | 32 ++++++++++ .../previous_routes/previous_routes.cc | 2 +- .../previous_routes/previous_routes.h | 4 +- .../internal_redirect/well_known_names.h | 2 + test/common/router/router_test.cc | 2 +- test/integration/BUILD | 1 + test/integration/redirect_integration_test.cc | 61 +++++++++++++++++++ test/mocks/router/mocks.h | 2 +- 22 files changed, 263 insertions(+), 13 deletions(-) create mode 100644 api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD create mode 100644 api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto create mode 100644 generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD create mode 100644 generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto create mode 100644 source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD create mode 100644 source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.cc create mode 100644 source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h create mode 100644 source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h diff --git a/api/BUILD b/api/BUILD index c73e7e71c2c7..d3b4ea997d1b 100644 --- a/api/BUILD +++ b/api/BUILD @@ -223,6 +223,7 @@ proto_library( "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", "//envoy/extensions/filters/udp/dns_filter/v3alpha:pkg", "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", + "//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", diff --git a/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD b/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD new file mode 100644 index 000000000000..ef3541ebcb1d --- /dev/null +++ b/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto b/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto new file mode 100644 index 000000000000..32ad616f3984 --- /dev/null +++ b/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package envoy.extensions.internal_redirect.only_allow_safe_cross_scheme_redirect.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.only_allow_safe_cross_scheme_redirect.v3"; +option java_outer_classname = "OnlyAllowSafeCrossSchemeRedirectConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: OnlyAllowSafeCrossSchemeRedirect internal redirect predicate] + +// An internal redirect predicate that checks the scheme between the +// downstream url and the redirect target url and allows a) same scheme +// redirect and b) safe cross scheme redirect, which means if the downstream +// scheme is HTTPS, both HTTPS and HTTP redirect targets are allowed, but if the +// downstream scheme is HTTP, only HTTP redirect targets are allowed. +// [#extension: +// envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect] +message OnlyAllowSafeCrossSchemeRedirectConfig { +} diff --git a/api/versioning/BUILD b/api/versioning/BUILD index f90877504a71..910e608646be 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -106,6 +106,7 @@ proto_library( "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", "//envoy/extensions/filters/udp/dns_filter/v3alpha:pkg", "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", + "//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD b/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD new file mode 100644 index 000000000000..ef3541ebcb1d --- /dev/null +++ b/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD @@ -0,0 +1,9 @@ +# DO NOT EDIT. This file is generated by tools/proto_sync.py. + +load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package") + +licenses(["notice"]) # Apache 2 + +api_proto_package( + deps = ["@com_github_cncf_udpa//udpa/annotations:pkg"], +) diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto new file mode 100644 index 000000000000..32ad616f3984 --- /dev/null +++ b/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package envoy.extensions.internal_redirect.only_allow_safe_cross_scheme_redirect.v3; + +import "udpa/annotations/status.proto"; +import "udpa/annotations/versioning.proto"; +import "validate/validate.proto"; + +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.only_allow_safe_cross_scheme_redirect.v3"; +option java_outer_classname = "OnlyAllowSafeCrossSchemeRedirectConfigProto"; +option java_multiple_files = true; +option (udpa.annotations.file_status).package_version_status = ACTIVE; + +// [#protodoc-title: OnlyAllowSafeCrossSchemeRedirect internal redirect predicate] + +// An internal redirect predicate that checks the scheme between the +// downstream url and the redirect target url and allows a) same scheme +// redirect and b) safe cross scheme redirect, which means if the downstream +// scheme is HTTPS, both HTTPS and HTTP redirect targets are allowed, but if the +// downstream scheme is HTTP, only HTTP redirect targets are allowed. +// [#extension: +// envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect] +message OnlyAllowSafeCrossSchemeRedirectConfig { +} diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index 167a4bad3478..1529ef094a4b 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -27,7 +27,8 @@ class InternalRedirectPredicate : Logger::Loggable { * response to be proxied downstream. */ virtual bool acceptTargetRoute(StreamInfo::FilterState& filter_state, - absl::string_view target_route_name) PURE; + absl::string_view target_route_name, bool downstream_is_https, + bool target_is_https) PURE; /** * @return the name of the current predicate. diff --git a/source/common/router/router.cc b/source/common/router/router.cc index acf7bd72b57b..4064141cec54 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -1524,7 +1524,8 @@ bool Filter::convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& do auto& route_name = route->routeEntry()->routeName(); for (auto& predicate : policy.predicates()) { - if (!predicate->acceptTargetRoute(*filter_state, route_name)) { + if (!predicate->acceptTargetRoute(*filter_state, route_name, !scheme_is_http, + !target_is_http)) { config_.stats_.passthrough_internal_redirect_predicate_.inc(); ENVOY_STREAM_LOG(trace, "rejecting redirect targeting {}, by {} predicate", *callbacks_, route_name, predicate->name()); diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index ee3cccaf9165..079bffbd004d 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -185,5 +185,6 @@ EXTENSIONS = { # Internal redirect predicates # "envoy.internal_redirect_predicates.allow_listed_routes": "//source/extensions/internal_redirect/allow_listed_routes:config", + "envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect": "//source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect:config", "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", } diff --git a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h index df3c1202b1c1..b003648095de 100644 --- a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h +++ b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h @@ -16,12 +16,12 @@ namespace InternalRedirect { class AllowListedRoutesPredicate : public Router::InternalRedirectPredicate { public: AllowListedRoutesPredicate( - absl::string_view, const envoy::extensions::internal_redirect::allow_listed_routes::v3::AllowListedRoutesConfig& config) : allowed_routes_(config.allowed_route_names().begin(), config.allowed_route_names().end()) {} - bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name) override { + bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view route_name, bool, + bool) override { return allowed_routes_.contains(route_name); } diff --git a/source/extensions/internal_redirect/allow_listed_routes/config.h b/source/extensions/internal_redirect/allow_listed_routes/config.h index 1ec9e2e62d4a..1a122f4f31b6 100644 --- a/source/extensions/internal_redirect/allow_listed_routes/config.h +++ b/source/extensions/internal_redirect/allow_listed_routes/config.h @@ -17,14 +17,12 @@ namespace InternalRedirect { class AllowListedRoutesPredicateFactory : public Router::InternalRedirectPredicateFactory { public: Router::InternalRedirectPredicateSharedPtr - createInternalRedirectPredicate(const Protobuf::Message& config, - absl::string_view current_route_name) override { + createInternalRedirectPredicate(const Protobuf::Message& config, absl::string_view) override { auto allow_listed_routes_config = MessageUtil::downcastAndValidate( config, ProtobufMessage::getStrictValidationVisitor()); - return std::make_shared(current_route_name, - allow_listed_routes_config); + return std::make_shared(allow_listed_routes_config); } std::string name() const override { diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD new file mode 100644 index 000000000000..e73d4deb1560 --- /dev/null +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD @@ -0,0 +1,34 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_extension", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "only_allow_safe_cross_scheme_redirect_lib", + hdrs = ["only_allow_safe_cross_scheme_redirect.h"], + deps = [ + "//include/envoy/router:internal_redirect_interface", + "//include/envoy/stream_info:filter_state_interface", + "//source/extensions/internal_redirect:well_known_names", + ], +) + +envoy_cc_extension( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + security_posture = "robust_to_untrusted_downstream_and_upstream", + deps = [ + ":only_allow_safe_cross_scheme_redirect_lib", + "//include/envoy/registry", + "//include/envoy/router:internal_redirect_interface", + "//source/extensions/internal_redirect:well_known_names", + "@envoy_api//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg_cc_proto", + ], +) diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.cc b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.cc new file mode 100644 index 000000000000..d38906d6bd00 --- /dev/null +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.cc @@ -0,0 +1,15 @@ +#include "extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h" + +#include "envoy/registry/registry.h" +#include "envoy/router/internal_redirect.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +REGISTER_FACTORY(OnlyAllowSafeCrossSchemeRedirectPredicateFactory, + Router::InternalRedirectPredicateFactory); + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h new file mode 100644 index 000000000000..008ba96f4ba8 --- /dev/null +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h @@ -0,0 +1,36 @@ +#pragma once + +#include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.h" +#include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.validate.h" +#include "envoy/router/internal_redirect.h" + +#include "extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h" +#include "extensions/internal_redirect/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +class OnlyAllowSafeCrossSchemeRedirectPredicateFactory + : public Router::InternalRedirectPredicateFactory { +public: + Router::InternalRedirectPredicateSharedPtr + createInternalRedirectPredicate(const Protobuf::Message&, + absl::string_view) override { + return std::make_shared(); + } + + std::string name() const override { + return InternalRedirectPredicateValues::get().OnlyAllowSafeCrossSchemeRedirectPredicate; + } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique< + envoy::extensions::internal_redirect::only_allow_safe_cross_scheme_redirect::v3:: + OnlyAllowSafeCrossSchemeRedirectConfig>(); + } +}; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h new file mode 100644 index 000000000000..604c2bb32318 --- /dev/null +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h @@ -0,0 +1,32 @@ +#pragma once + +#include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.h" +#include "envoy/router/internal_redirect.h" +#include "envoy/stream_info/filter_state.h" + +#include "extensions/internal_redirect/well_known_names.h" + +#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +class OnlyAllowSafeCrossSchemeRedirectPredicate : public Router::InternalRedirectPredicate { +public: + OnlyAllowSafeCrossSchemeRedirectPredicate() {} + + bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view, bool downstream_is_https, + bool target_is_https) override { + return downstream_is_https || !target_is_https; + } + + absl::string_view name() const override { + return InternalRedirectPredicateValues::get().OnlyAllowSafeCrossSchemeRedirectPredicate; + } +}; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.cc b/source/extensions/internal_redirect/previous_routes/previous_routes.cc index bcbf3f1165cb..a29187e29d43 100644 --- a/source/extensions/internal_redirect/previous_routes/previous_routes.cc +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.cc @@ -34,7 +34,7 @@ class PreviousRoutesPredicateState : public StreamInfo::FilterState::Object { } // namespace bool PreviousRoutesPredicate::acceptTargetRoute(StreamInfo::FilterState& filter_state, - absl::string_view route_name) { + absl::string_view route_name, bool, bool) { auto filter_state_name = absl::StrCat(PreviousRoutesPredicateStateNamePrefix, ".", current_route_name_); if (!filter_state.hasData(filter_state_name)) { diff --git a/source/extensions/internal_redirect/previous_routes/previous_routes.h b/source/extensions/internal_redirect/previous_routes/previous_routes.h index cd96ff04419b..b79f4f8b1754 100644 --- a/source/extensions/internal_redirect/previous_routes/previous_routes.h +++ b/source/extensions/internal_redirect/previous_routes/previous_routes.h @@ -16,8 +16,8 @@ class PreviousRoutesPredicate : public Router::InternalRedirectPredicate { explicit PreviousRoutesPredicate(absl::string_view current_route_name) : current_route_name_(current_route_name) {} - bool acceptTargetRoute(StreamInfo::FilterState& filter_state, - absl::string_view route_name) override; + bool acceptTargetRoute(StreamInfo::FilterState& filter_state, absl::string_view route_name, bool, + bool) override; absl::string_view name() const override { return InternalRedirectPredicateValues::get().PreviousRoutesPredicate; diff --git a/source/extensions/internal_redirect/well_known_names.h b/source/extensions/internal_redirect/well_known_names.h index 1a530a398561..79e675e83cc5 100644 --- a/source/extensions/internal_redirect/well_known_names.h +++ b/source/extensions/internal_redirect/well_known_names.h @@ -16,6 +16,8 @@ class InternalRedirectPredicatesNameValues { const std::string PreviousRoutesPredicate = "envoy.internal_redirect_predicates.previous_routes"; const std::string AllowListedRoutesPredicate = "envoy.internal_redirect_predicates.allow_listed_routes"; + const std::string OnlyAllowSafeCrossSchemeRedirectPredicate = + "envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect"; }; using InternalRedirectPredicateValues = ConstSingleton; diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index a182113e7c01..886ab739d6b7 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -4571,7 +4571,7 @@ TEST_F(RouterTest, InternalRedirectRejectedByPredicate) { EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, predicates()) .WillOnce(Return(std::vector({mock_predicate}))); - EXPECT_CALL(*mock_predicate, acceptTargetRoute(_, _)).WillOnce(Return(false)); + EXPECT_CALL(*mock_predicate, acceptTargetRoute(_, _, _, _)).WillOnce(Return(false)); ON_CALL(*mock_predicate, name()).WillByDefault(Return("mock_predicate")); EXPECT_CALL(callbacks_, recreateStream()).Times(0); diff --git a/test/integration/BUILD b/test/integration/BUILD index 98bfe5704920..5d4119510ef3 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -633,6 +633,7 @@ envoy_cc_test( ":http_protocol_integration_lib", "//source/common/http:header_map_lib", "//source/extensions/internal_redirect/allow_listed_routes:config", + "//source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect:config", "//source/extensions/internal_redirect/previous_routes:config", "//test/test_common:utility_lib", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index ff956ca26837..c64508724c78 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -1,6 +1,7 @@ #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" #include "envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.h" #include "envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.pb.h" #include "test/integration/http_protocol_integration.h" @@ -339,6 +340,66 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowListedRoutesPred test_server_->counter("http.config_test.passthrough_internal_redirect_predicate")->value()); } +TEST_P(RedirectIntegrationTest, + InternalRedirectPreventedByOnlyAllowSafeCrossSchemeRedirectPredicate) { + auto handle_only_allow_safe_cross_scheme_redirect_route = config_helper_.createVirtualHost( + "handle.internal.redirect.only.allow.safe.cross.scheme.redirect"); + auto* internal_redirect_policy = + handle_only_allow_safe_cross_scheme_redirect_route.mutable_routes(0) + ->mutable_route() + ->mutable_internal_redirect_policy(); + + auto* predicate = internal_redirect_policy->add_predicates(); + predicate->set_name("only_allow_safe_cross_scheme_redirect_predicate"); + envoy::extensions::internal_redirect::only_allow_safe_cross_scheme_redirect::v3:: + OnlyAllowSafeCrossSchemeRedirectConfig predicate_config; + predicate->mutable_typed_config()->PackFrom(predicate_config); + + internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); + + config_helper_.addVirtualHost(handle_only_allow_safe_cross_scheme_redirect_route); + + // Validate that header sanitization is only called once. + config_helper_.addConfigModifier( + [](envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + hcm) { hcm.set_via("via_value"); }); + initialize(); + fake_upstreams_[0]->set_allow_unexpected_disconnects(true); + + codec_client_ = makeHttpConnection(lookupPort("http")); + + default_request_headers_.setHost("handle.internal.redirect.only.allow.safe.cross.scheme.redirect"); + IntegrationStreamDecoderPtr response = + codec_client_->makeHeaderOnlyRequest(default_request_headers_); + + auto first_request = waitForNextStream(); + // Redirect to another route + redirect_response_.setLocation("http://handle.internal.redirect.max.three.hop/random/path"); + first_request->encodeHeaders(redirect_response_, true); + + auto second_request = waitForNextStream(); + // Redirect back to the original route. + redirect_response_.setLocation( + "http://handle.internal.redirect.only.allow.safe.cross.scheme.redirect/another/path"); + second_request->encodeHeaders(redirect_response_, true); + + auto third_request = waitForNextStream(); + // Redirect to https target. This should fail. + redirect_response_.setLocation("https://handle.internal.redirect/yet/another/path"); + third_request->encodeHeaders(redirect_response_, true); + + response->waitForEndStream(); + ASSERT_TRUE(response->complete()); + EXPECT_EQ("302", response->headers().Status()->value().getStringView()); + EXPECT_EQ("https://handle.internal.redirect/yet/another/path", + response->headers().Location()->value().getStringView()); + EXPECT_EQ(2, test_server_->counter("cluster.cluster_0.upstream_internal_redirect_succeeded_total") + ->value()); + EXPECT_EQ( + 1, + test_server_->counter("http.config_test.passthrough_internal_redirect_predicate")->value()); +} + TEST_P(RedirectIntegrationTest, InvalidRedirect) { initialize(); diff --git a/test/mocks/router/mocks.h b/test/mocks/router/mocks.h index e1c4fd005fe9..d221d81b3439 100644 --- a/test/mocks/router/mocks.h +++ b/test/mocks/router/mocks.h @@ -143,7 +143,7 @@ class MockInternalRedirectPolicy : public InternalRedirectPolicy { class MockInternalRedirectPredicate : public InternalRedirectPredicate { public: - MOCK_METHOD(bool, acceptTargetRoute, (StreamInfo::FilterState&, absl::string_view)); + MOCK_METHOD(bool, acceptTargetRoute, (StreamInfo::FilterState&, absl::string_view, bool, bool)); MOCK_METHOD(absl::string_view, name, (), (const)); }; From 15a4d3f39d704b13b826fe9e6e2c68ba1d6aa464 Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 12 May 2020 18:23:39 -0400 Subject: [PATCH 41/46] Fix format. Signed-off-by: pengg --- .../only_allow_safe_cross_scheme_redirect/BUILD | 1 + .../only_allow_safe_cross_scheme_redirect/config.h | 3 +-- test/integration/BUILD | 1 + test/integration/redirect_integration_test.cc | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD index e73d4deb1560..3b51ddea96ce 100644 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD @@ -16,6 +16,7 @@ envoy_cc_library( "//include/envoy/router:internal_redirect_interface", "//include/envoy/stream_info:filter_state_interface", "//source/extensions/internal_redirect:well_known_names", + "@envoy_api//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h index 008ba96f4ba8..e02f403c815e 100644 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h @@ -15,8 +15,7 @@ class OnlyAllowSafeCrossSchemeRedirectPredicateFactory : public Router::InternalRedirectPredicateFactory { public: Router::InternalRedirectPredicateSharedPtr - createInternalRedirectPredicate(const Protobuf::Message&, - absl::string_view) override { + createInternalRedirectPredicate(const Protobuf::Message&, absl::string_view) override { return std::make_shared(); } diff --git a/test/integration/BUILD b/test/integration/BUILD index 5d4119510ef3..2e0322938bcc 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -639,6 +639,7 @@ envoy_cc_test( "@envoy_api//envoy/config/route/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/internal_redirect/previous_routes/v3:pkg_cc_proto", ], ) diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index c64508724c78..51697435bfc3 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -368,7 +368,8 @@ TEST_P(RedirectIntegrationTest, codec_client_ = makeHttpConnection(lookupPort("http")); - default_request_headers_.setHost("handle.internal.redirect.only.allow.safe.cross.scheme.redirect"); + default_request_headers_.setHost( + "handle.internal.redirect.only.allow.safe.cross.scheme.redirect"); IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(default_request_headers_); From 948ca63e092ee354f9fa1825c4754345add3f5f1 Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 12 May 2020 18:37:16 -0400 Subject: [PATCH 42/46] Add OnlyAllowSafeCrossSchemeRedirect predicate to docs. Make sure the behavior change is documented in the release note. Signed-off-by: pengg --- .../arch_overview/http/http_connection_management.rst | 4 ++++ docs/root/version_history/current.rst | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index 8d76d142c4a0..cb87da8380ea 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -201,6 +201,10 @@ Specifically, the *allow listed routes* predicate defines edges of individual no and the *previous routes* predicate defines "visited" state of the edges, so that loop can be avoided if so desired. +A third predicate :ref:`only_allow_safe_cross_scheme_redirect +` +can be used to prevent HTTP -> HTTPS redirect. + Once the redirect has passed these checks, the request headers which were shipped to the original upstream will be modified by: diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index d4d2c02def97..c205dcc29907 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -48,7 +48,12 @@ Changes * router: allow retries by default when upstream responds with :ref:`x-envoy-overloaded `. * router: internal redirect configs are moved to the :ref`internal_redirect_policy ` field, which defines more fine - grained control of the internal redirect behavior. + grained control of the internal redirect behavior. This changes the default cross scheme redirect behavior. + All cross scheme redirect is allowed by default, but http downstream to https target is disallowed. To restore + the previous behavior, set disallow_cross_scheme_redirect=true and use + :ref:`only_allow_safe_cross_scheme_redirect + `, + in :ref:`predicates `. * runtime: add new gauge :ref:`deprecated_feature_seen_since_process_start ` that gets reset across hot restarts. * stats: added the option to :ref:`report counters as deltas ` to the metrics service stats sink. * tracing: tracing configuration has been made fully dynamic and every HTTP connection manager From 93cd0e63ec2fac45e191696b104053013b7c418f Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 12 May 2020 19:23:54 -0400 Subject: [PATCH 43/46] Fix previous_route_test. Signed-off-by: pengg --- .../internal_redirect/previous_routes/config_test.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/extensions/internal_redirect/previous_routes/config_test.cc b/test/extensions/internal_redirect/previous_routes/config_test.cc index 8a081ccac5a9..1d69320fc2ed 100644 --- a/test/extensions/internal_redirect/previous_routes/config_test.cc +++ b/test/extensions/internal_redirect/previous_routes/config_test.cc @@ -38,7 +38,7 @@ TEST_F(PreviousRoutesTest, TargetIsOnlyTakenOnce) { auto predicate = factory_->createInternalRedirectPredicate(*config_, current_route_name); ASSERT(predicate); - EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_1")); + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_1", false, false)); // New filter state data is created with route name. EXPECT_TRUE(filter_state_.hasDataWithName( "envoy.internal_redirect.previous_routes_predicate_state.fake_current_route")); @@ -49,7 +49,7 @@ TEST_F(PreviousRoutesTest, TargetIsOnlyTakenOnce) { auto predicate = factory_->createInternalRedirectPredicate(*config_, current_route_name); ASSERT(predicate); - EXPECT_FALSE(predicate->acceptTargetRoute(filter_state_, "route_1")); + EXPECT_FALSE(predicate->acceptTargetRoute(filter_state_, "route_1", false, false)); } } @@ -59,7 +59,7 @@ TEST_F(PreviousRoutesTest, RoutesAreIndependent) { auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_0"); ASSERT(predicate); - EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_2")); + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_2", false, false)); // New filter state data is created with route name. EXPECT_TRUE(filter_state_.hasDataWithName( "envoy.internal_redirect.previous_routes_predicate_state.route_0")); @@ -70,7 +70,7 @@ TEST_F(PreviousRoutesTest, RoutesAreIndependent) { auto predicate = factory_->createInternalRedirectPredicate(*config_, "route_1"); ASSERT(predicate); - EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_2")); + EXPECT_TRUE(predicate->acceptTargetRoute(filter_state_, "route_2", false, false)); // New filter state data is created with route name. EXPECT_TRUE(filter_state_.hasDataWithName( "envoy.internal_redirect.previous_routes_predicate_state.route_1")); From c62f9da4ddfadc98ae153ef949251d8e77fb8b70 Mon Sep 17 00:00:00 2001 From: pengg Date: Tue, 12 May 2020 22:36:47 -0400 Subject: [PATCH 44/46] Fix clang tidy finding. Signed-off-by: pengg --- source/extensions/extensions_build_config.bzl | 2 +- .../allow_listed_routes/allow_listed_routes.h | 2 +- .../only_allow_safe_cross_scheme_redirect/BUILD | 1 - .../only_allow_safe_cross_scheme_redirect/config.h | 1 - .../only_allow_safe_cross_scheme_redirect.h | 4 ---- 5 files changed, 2 insertions(+), 8 deletions(-) diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 079bffbd004d..8231cea6c686 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -185,6 +185,6 @@ EXTENSIONS = { # Internal redirect predicates # "envoy.internal_redirect_predicates.allow_listed_routes": "//source/extensions/internal_redirect/allow_listed_routes:config", - "envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect": "//source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect:config", "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", + "envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect": "//source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect:config", } diff --git a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h index b003648095de..72d8d605db0f 100644 --- a/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h +++ b/source/extensions/internal_redirect/allow_listed_routes/allow_listed_routes.h @@ -15,7 +15,7 @@ namespace InternalRedirect { class AllowListedRoutesPredicate : public Router::InternalRedirectPredicate { public: - AllowListedRoutesPredicate( + explicit AllowListedRoutesPredicate( const envoy::extensions::internal_redirect::allow_listed_routes::v3::AllowListedRoutesConfig& config) : allowed_routes_(config.allowed_route_names().begin(), config.allowed_route_names().end()) {} diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD index 3b51ddea96ce..e73d4deb1560 100644 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD @@ -16,7 +16,6 @@ envoy_cc_library( "//include/envoy/router:internal_redirect_interface", "//include/envoy/stream_info:filter_state_interface", "//source/extensions/internal_redirect:well_known_names", - "@envoy_api//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h index e02f403c815e..abb9ca40936d 100644 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h @@ -1,7 +1,6 @@ #pragma once #include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.h" -#include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.validate.h" #include "envoy/router/internal_redirect.h" #include "extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h" diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h index 604c2bb32318..6fe0eb01840d 100644 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h +++ b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h @@ -1,12 +1,10 @@ #pragma once -#include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.h" #include "envoy/router/internal_redirect.h" #include "envoy/stream_info/filter_state.h" #include "extensions/internal_redirect/well_known_names.h" -#include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" namespace Envoy { @@ -15,8 +13,6 @@ namespace InternalRedirect { class OnlyAllowSafeCrossSchemeRedirectPredicate : public Router::InternalRedirectPredicate { public: - OnlyAllowSafeCrossSchemeRedirectPredicate() {} - bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view, bool downstream_is_https, bool target_is_https) override { return downstream_is_https || !target_is_https; From 4e660fd5d3679c139e0be672a8049596052ec412 Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 13 May 2020 09:56:50 -0400 Subject: [PATCH 45/46] Rename OnlyAllowSafeCrossSchemeRedirect to SafeCrossScheme to get around windows path length limitation. Change default behavior to be disallowing cross scheme redirect. Rename the option to allow_cross_scheme (default to false). Update tests. Document implemeneted default behavior change. Signed-off-by: pengg --- api/BUILD | 2 +- .../config/route/v3/route_components.proto | 6 ++-- .../route/v4alpha/route_components.proto | 6 ++-- .../v3/BUILD | 0 .../v3/safe_cross_scheme_config.proto} | 12 +++---- api/versioning/BUILD | 2 +- .../http/http_connection_management.rst | 8 ++--- docs/root/version_history/current.rst | 17 +++++----- .../config/route/v3/route_components.proto | 6 ++-- .../route/v4alpha/route_components.proto | 6 ++-- .../v3/BUILD | 0 .../v3/safe_cross_scheme_config.proto} | 12 +++---- source/common/router/config_impl.cc | 3 +- source/common/router/config_impl.h | 4 +-- source/extensions/extensions_build_config.bzl | 2 +- .../config.h | 34 ------------------- .../BUILD | 8 ++--- .../config.cc | 5 ++- .../safe_cross_scheme/config.h | 32 +++++++++++++++++ .../safe_cross_scheme.h} | 4 +-- .../internal_redirect/well_known_names.h | 6 ++-- test/common/router/router_test.cc | 11 +++--- test/integration/BUILD | 4 +-- test/integration/redirect_integration_test.cc | 24 ++++++------- 24 files changed, 104 insertions(+), 110 deletions(-) rename api/envoy/extensions/internal_redirect/{only_allow_safe_cross_scheme_redirect => safe_cross_scheme}/v3/BUILD (100%) rename api/envoy/extensions/internal_redirect/{only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto => safe_cross_scheme/v3/safe_cross_scheme_config.proto} (62%) rename generated_api_shadow/envoy/extensions/internal_redirect/{only_allow_safe_cross_scheme_redirect => safe_cross_scheme}/v3/BUILD (100%) rename generated_api_shadow/envoy/extensions/internal_redirect/{only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto => safe_cross_scheme/v3/safe_cross_scheme_config.proto} (62%) delete mode 100644 source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h rename source/extensions/internal_redirect/{only_allow_safe_cross_scheme_redirect => safe_cross_scheme}/BUILD (72%) rename source/extensions/internal_redirect/{only_allow_safe_cross_scheme_redirect => safe_cross_scheme}/config.cc (52%) create mode 100644 source/extensions/internal_redirect/safe_cross_scheme/config.h rename source/extensions/internal_redirect/{only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h => safe_cross_scheme/safe_cross_scheme.h} (77%) diff --git a/api/BUILD b/api/BUILD index d3b4ea997d1b..c701bdcf4833 100644 --- a/api/BUILD +++ b/api/BUILD @@ -223,8 +223,8 @@ proto_library( "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", "//envoy/extensions/filters/udp/dns_filter/v3alpha:pkg", "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", - "//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", + "//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", "//envoy/extensions/transport_sockets/alts/v3:pkg", diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index c016500bb773..55aba282b2aa 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1624,7 +1624,7 @@ message InternalRedirectPolicy { // the redirect, causing the response to be proxied to downstream. repeated core.v3.TypedExtensionConfig predicates = 3; - // Disallow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to follow such redirects. - bool disallow_cross_scheme_redirect = 4; + // Allow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to proxy such redirects downstream. + bool allow_cross_scheme_redirect = 4; } diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index c4876929170d..57b7279052bb 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1619,7 +1619,7 @@ message InternalRedirectPolicy { // the redirect, causing the response to be proxied to downstream. repeated core.v4alpha.TypedExtensionConfig predicates = 3; - // Disallow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to follow such redirects. - bool disallow_cross_scheme_redirect = 4; + // Allow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to proxy such redirects downstream. + bool allow_cross_scheme_redirect = 4; } diff --git a/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD b/api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/BUILD similarity index 100% rename from api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD rename to api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/BUILD diff --git a/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto b/api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto similarity index 62% rename from api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto rename to api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto index 32ad616f3984..54cec2f09bbb 100644 --- a/api/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto +++ b/api/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto @@ -1,17 +1,17 @@ syntax = "proto3"; -package envoy.extensions.internal_redirect.only_allow_safe_cross_scheme_redirect.v3; +package envoy.extensions.internal_redirect.safe_cross_scheme.v3; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.only_allow_safe_cross_scheme_redirect.v3"; -option java_outer_classname = "OnlyAllowSafeCrossSchemeRedirectConfigProto"; +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.safe_cross_scheme.v3"; +option java_outer_classname = "SafeCrossSchemeConfigProto"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; -// [#protodoc-title: OnlyAllowSafeCrossSchemeRedirect internal redirect predicate] +// [#protodoc-title: SafeCrossScheme internal redirect predicate] // An internal redirect predicate that checks the scheme between the // downstream url and the redirect target url and allows a) same scheme @@ -19,6 +19,6 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // scheme is HTTPS, both HTTPS and HTTP redirect targets are allowed, but if the // downstream scheme is HTTP, only HTTP redirect targets are allowed. // [#extension: -// envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect] -message OnlyAllowSafeCrossSchemeRedirectConfig { +// envoy.internal_redirect_predicates.safe_cross_scheme] +message SafeCrossSchemeConfig { } diff --git a/api/versioning/BUILD b/api/versioning/BUILD index 910e608646be..d7771fbbd29e 100644 --- a/api/versioning/BUILD +++ b/api/versioning/BUILD @@ -106,8 +106,8 @@ proto_library( "//envoy/extensions/filters/network/zookeeper_proxy/v3:pkg", "//envoy/extensions/filters/udp/dns_filter/v3alpha:pkg", "//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg", - "//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg", "//envoy/extensions/internal_redirect/previous_routes/v3:pkg", + "//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg", "//envoy/extensions/retry/host/omit_host_metadata/v3:pkg", "//envoy/extensions/retry/priority/previous_priorities/v3:pkg", "//envoy/extensions/transport_sockets/alts/v3:pkg", diff --git a/docs/root/intro/arch_overview/http/http_connection_management.rst b/docs/root/intro/arch_overview/http/http_connection_management.rst index cb87da8380ea..74e8d90b99e8 100644 --- a/docs/root/intro/arch_overview/http/http_connection_management.rst +++ b/docs/root/intro/arch_overview/http/http_connection_management.rst @@ -170,8 +170,8 @@ For a redirect to be handled successfully it must pass the following checks: 2. Have a *location* header with a valid, fully qualified URL. 3. The request must have been fully processed by Envoy. 4. The request must not have a body. -5. :ref:`disallow_cross_scheme_redirect - ` is false (default), +5. :ref:`allow_cross_scheme_redirect + ` is true (default to false), or the scheme of the downstream request and the *location* header are the same. 6. The number of previously handled internal redirect within a given downstream request does not exceed :ref:`max internal redirects @@ -201,8 +201,8 @@ Specifically, the *allow listed routes* predicate defines edges of individual no and the *previous routes* predicate defines "visited" state of the edges, so that loop can be avoided if so desired. -A third predicate :ref:`only_allow_safe_cross_scheme_redirect -` +A third predicate :ref:`safe_cross_scheme +` can be used to prevent HTTP -> HTTPS redirect. Once the redirect has passed these checks, the request headers which were shipped to the original diff --git a/docs/root/version_history/current.rst b/docs/root/version_history/current.rst index c205dcc29907..4b0bf0bf5ce9 100644 --- a/docs/root/version_history/current.rst +++ b/docs/root/version_history/current.rst @@ -46,14 +46,8 @@ Changes tracing is not forced. * router: allow retries of streaming or incomplete requests. This removes stat `rq_retry_skipped_request_not_complete`. * router: allow retries by default when upstream responds with :ref:`x-envoy-overloaded `. -* router: internal redirect configs are moved to the :ref`internal_redirect_policy - ` field, which defines more fine - grained control of the internal redirect behavior. This changes the default cross scheme redirect behavior. - All cross scheme redirect is allowed by default, but http downstream to https target is disallowed. To restore - the previous behavior, set disallow_cross_scheme_redirect=true and use - :ref:`only_allow_safe_cross_scheme_redirect - `, - in :ref:`predicates `. +* router: more fine grained internal redirect configs are added to the :ref`internal_redirect_policy + ` field. * runtime: add new gauge :ref:`deprecated_feature_seen_since_process_start ` that gets reset across hot restarts. * stats: added the option to :ref:`report counters as deltas ` to the metrics service stats sink. * tracing: tracing configuration has been made fully dynamic and every HTTP connection manager @@ -71,3 +65,10 @@ Deprecated * The * :ref:`GoogleRE2.max_program_size` field is now deprecated. Management servers are expected to validate regexp program sizes instead of expecting the client to do it. +* The :ref:`internal_redirect_action ` + field and :ref:`max_internal_redirects ` field + are now deprecated. This changes the implemented default cross scheme redirect behavior. + All cross scheme redirect are disallowed by default. To restore + the previous behavior, set allow_cross_scheme_redirect=true and use + :ref:`safe_cross_scheme`, + in :ref:`predicates `. diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index a9506b6262b5..af3796f0e953 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -1642,7 +1642,7 @@ message InternalRedirectPolicy { // the redirect, causing the response to be proxied to downstream. repeated core.v3.TypedExtensionConfig predicates = 3; - // Disallow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to follow such redirects. - bool disallow_cross_scheme_redirect = 4; + // Allow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to proxy such redirects downstream. + bool allow_cross_scheme_redirect = 4; } diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index be5caa1959ea..c187a2ced813 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -1638,7 +1638,7 @@ message InternalRedirectPolicy { // the redirect, causing the response to be proxied to downstream. repeated core.v4alpha.TypedExtensionConfig predicates = 3; - // Disallow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to follow such redirects. - bool disallow_cross_scheme_redirect = 4; + // Allow internal redirect to follow a target URI with a different scheme than the value of + // x-forwarded-proto. Default behavior is to proxy such redirects downstream. + bool allow_cross_scheme_redirect = 4; } diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD b/generated_api_shadow/envoy/extensions/internal_redirect/safe_cross_scheme/v3/BUILD similarity index 100% rename from generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/BUILD rename to generated_api_shadow/envoy/extensions/internal_redirect/safe_cross_scheme/v3/BUILD diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto similarity index 62% rename from generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto rename to generated_api_shadow/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto index 32ad616f3984..54cec2f09bbb 100644 --- a/generated_api_shadow/envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.proto +++ b/generated_api_shadow/envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.proto @@ -1,17 +1,17 @@ syntax = "proto3"; -package envoy.extensions.internal_redirect.only_allow_safe_cross_scheme_redirect.v3; +package envoy.extensions.internal_redirect.safe_cross_scheme.v3; import "udpa/annotations/status.proto"; import "udpa/annotations/versioning.proto"; import "validate/validate.proto"; -option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.only_allow_safe_cross_scheme_redirect.v3"; -option java_outer_classname = "OnlyAllowSafeCrossSchemeRedirectConfigProto"; +option java_package = "io.envoyproxy.envoy.extensions.internal_redirect.safe_cross_scheme.v3"; +option java_outer_classname = "SafeCrossSchemeConfigProto"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; -// [#protodoc-title: OnlyAllowSafeCrossSchemeRedirect internal redirect predicate] +// [#protodoc-title: SafeCrossScheme internal redirect predicate] // An internal redirect predicate that checks the scheme between the // downstream url and the redirect target url and allows a) same scheme @@ -19,6 +19,6 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // scheme is HTTPS, both HTTPS and HTTP redirect targets are allowed, but if the // downstream scheme is HTTP, only HTTP redirect targets are allowed. // [#extension: -// envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect] -message OnlyAllowSafeCrossSchemeRedirectConfig { +// envoy.internal_redirect_predicates.safe_cross_scheme] +message SafeCrossSchemeConfig { } diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 7b8034f3af96..fb8ec0d8449d 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -151,8 +151,7 @@ InternalRedirectPolicyImpl::InternalRedirectPolicyImpl( redirect_response_codes_(buildRedirectResponseCodes(policy_config)), max_internal_redirects_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(policy_config, max_internal_redirects, 1)), - enabled_(true), - disallow_cross_scheme_redirect_(policy_config.disallow_cross_scheme_redirect()) { + enabled_(true), allow_cross_scheme_redirect_(policy_config.allow_cross_scheme_redirect()) { for (const auto& predicate : policy_config.predicates()) { const std::string type{ TypeUtil::typeUrlToDescriptorFullName(predicate.typed_config().type_url())}; diff --git a/source/common/router/config_impl.h b/source/common/router/config_impl.h index f7180f9d3e07..31a81abf7e84 100644 --- a/source/common/router/config_impl.h +++ b/source/common/router/config_impl.h @@ -410,7 +410,7 @@ class InternalRedirectPolicyImpl : public InternalRedirectPolicy { uint32_t maxInternalRedirects() const override { return max_internal_redirects_; } - bool isCrossSchemeRedirectAllowed() const override { return !disallow_cross_scheme_redirect_; } + bool isCrossSchemeRedirectAllowed() const override { return allow_cross_scheme_redirect_; } private: absl::flat_hash_set buildRedirectResponseCodes( @@ -420,7 +420,7 @@ class InternalRedirectPolicyImpl : public InternalRedirectPolicy { const absl::flat_hash_set redirect_response_codes_; const uint32_t max_internal_redirects_{1}; const bool enabled_{false}; - const bool disallow_cross_scheme_redirect_{false}; + const bool allow_cross_scheme_redirect_{false}; std::vector> predicate_factories_; diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 8231cea6c686..9bd2531dcdb7 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -186,5 +186,5 @@ EXTENSIONS = { # "envoy.internal_redirect_predicates.allow_listed_routes": "//source/extensions/internal_redirect/allow_listed_routes:config", "envoy.internal_redirect_predicates.previous_routes": "//source/extensions/internal_redirect/previous_routes:config", - "envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect": "//source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect:config", + "envoy.internal_redirect_predicates.safe_cross_scheme": "//source/extensions/internal_redirect/safe_cross_scheme:config", } diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h b/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h deleted file mode 100644 index abb9ca40936d..000000000000 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.h" -#include "envoy/router/internal_redirect.h" - -#include "extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h" -#include "extensions/internal_redirect/well_known_names.h" - -namespace Envoy { -namespace Extensions { -namespace InternalRedirect { - -class OnlyAllowSafeCrossSchemeRedirectPredicateFactory - : public Router::InternalRedirectPredicateFactory { -public: - Router::InternalRedirectPredicateSharedPtr - createInternalRedirectPredicate(const Protobuf::Message&, absl::string_view) override { - return std::make_shared(); - } - - std::string name() const override { - return InternalRedirectPredicateValues::get().OnlyAllowSafeCrossSchemeRedirectPredicate; - } - - ProtobufTypes::MessagePtr createEmptyConfigProto() override { - return std::make_unique< - envoy::extensions::internal_redirect::only_allow_safe_cross_scheme_redirect::v3:: - OnlyAllowSafeCrossSchemeRedirectConfig>(); - } -}; - -} // namespace InternalRedirect -} // namespace Extensions -} // namespace Envoy diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD b/source/extensions/internal_redirect/safe_cross_scheme/BUILD similarity index 72% rename from source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD rename to source/extensions/internal_redirect/safe_cross_scheme/BUILD index e73d4deb1560..94293850b53b 100644 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/BUILD +++ b/source/extensions/internal_redirect/safe_cross_scheme/BUILD @@ -10,8 +10,8 @@ load( envoy_package() envoy_cc_library( - name = "only_allow_safe_cross_scheme_redirect_lib", - hdrs = ["only_allow_safe_cross_scheme_redirect.h"], + name = "safe_cross_scheme_lib", + hdrs = ["safe_cross_scheme.h"], deps = [ "//include/envoy/router:internal_redirect_interface", "//include/envoy/stream_info:filter_state_interface", @@ -25,10 +25,10 @@ envoy_cc_extension( hdrs = ["config.h"], security_posture = "robust_to_untrusted_downstream_and_upstream", deps = [ - ":only_allow_safe_cross_scheme_redirect_lib", + ":safe_cross_scheme_lib", "//include/envoy/registry", "//include/envoy/router:internal_redirect_interface", "//source/extensions/internal_redirect:well_known_names", - "@envoy_api//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.cc b/source/extensions/internal_redirect/safe_cross_scheme/config.cc similarity index 52% rename from source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.cc rename to source/extensions/internal_redirect/safe_cross_scheme/config.cc index d38906d6bd00..43b7664fd7ff 100644 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.cc +++ b/source/extensions/internal_redirect/safe_cross_scheme/config.cc @@ -1,4 +1,4 @@ -#include "extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/config.h" +#include "extensions/internal_redirect/safe_cross_scheme/config.h" #include "envoy/registry/registry.h" #include "envoy/router/internal_redirect.h" @@ -7,8 +7,7 @@ namespace Envoy { namespace Extensions { namespace InternalRedirect { -REGISTER_FACTORY(OnlyAllowSafeCrossSchemeRedirectPredicateFactory, - Router::InternalRedirectPredicateFactory); +REGISTER_FACTORY(SafeCrossSchemePredicateFactory, Router::InternalRedirectPredicateFactory); } // namespace InternalRedirect } // namespace Extensions diff --git a/source/extensions/internal_redirect/safe_cross_scheme/config.h b/source/extensions/internal_redirect/safe_cross_scheme/config.h new file mode 100644 index 000000000000..49a8fdfa8b69 --- /dev/null +++ b/source/extensions/internal_redirect/safe_cross_scheme/config.h @@ -0,0 +1,32 @@ +#pragma once + +#include "envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.pb.h" +#include "envoy/router/internal_redirect.h" + +#include "extensions/internal_redirect/safe_cross_scheme/safe_cross_scheme.h" +#include "extensions/internal_redirect/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace InternalRedirect { + +class SafeCrossSchemePredicateFactory : public Router::InternalRedirectPredicateFactory { +public: + Router::InternalRedirectPredicateSharedPtr + createInternalRedirectPredicate(const Protobuf::Message&, absl::string_view) override { + return std::make_shared(); + } + + std::string name() const override { + return InternalRedirectPredicateValues::get().SafeCrossSchemePredicate; + } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique< + envoy::extensions::internal_redirect::safe_cross_scheme::v3::SafeCrossSchemeConfig>(); + } +}; + +} // namespace InternalRedirect +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h b/source/extensions/internal_redirect/safe_cross_scheme/safe_cross_scheme.h similarity index 77% rename from source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h rename to source/extensions/internal_redirect/safe_cross_scheme/safe_cross_scheme.h index 6fe0eb01840d..fb33e58b6fdd 100644 --- a/source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/only_allow_safe_cross_scheme_redirect.h +++ b/source/extensions/internal_redirect/safe_cross_scheme/safe_cross_scheme.h @@ -11,7 +11,7 @@ namespace Envoy { namespace Extensions { namespace InternalRedirect { -class OnlyAllowSafeCrossSchemeRedirectPredicate : public Router::InternalRedirectPredicate { +class SafeCrossSchemePredicate : public Router::InternalRedirectPredicate { public: bool acceptTargetRoute(StreamInfo::FilterState&, absl::string_view, bool downstream_is_https, bool target_is_https) override { @@ -19,7 +19,7 @@ class OnlyAllowSafeCrossSchemeRedirectPredicate : public Router::InternalRedirec } absl::string_view name() const override { - return InternalRedirectPredicateValues::get().OnlyAllowSafeCrossSchemeRedirectPredicate; + return InternalRedirectPredicateValues::get().SafeCrossSchemePredicate; } }; diff --git a/source/extensions/internal_redirect/well_known_names.h b/source/extensions/internal_redirect/well_known_names.h index 79e675e83cc5..003e270329d6 100644 --- a/source/extensions/internal_redirect/well_known_names.h +++ b/source/extensions/internal_redirect/well_known_names.h @@ -13,11 +13,11 @@ namespace InternalRedirect { */ class InternalRedirectPredicatesNameValues { public: - const std::string PreviousRoutesPredicate = "envoy.internal_redirect_predicates.previous_routes"; const std::string AllowListedRoutesPredicate = "envoy.internal_redirect_predicates.allow_listed_routes"; - const std::string OnlyAllowSafeCrossSchemeRedirectPredicate = - "envoy.internal_redirect_predicates.only_allow_safe_cross_scheme_redirect"; + const std::string PreviousRoutesPredicate = "envoy.internal_redirect_predicates.previous_routes"; + const std::string SafeCrossSchemePredicate = + "envoy.internal_redirect_predicates.safe_cross_scheme"; }; using InternalRedirectPredicateValues = ConstSingleton; diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index 886ab739d6b7..ff40ce748fe4 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -304,7 +304,7 @@ class RouterTestBase : public testing::Test { .WillByDefault(Return(max_internal_redirects)); ON_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, isCrossSchemeRedirectAllowed()) - .WillByDefault(Return(true)); + .WillByDefault(Return(false)); ON_CALL(callbacks_, connection()).WillByDefault(Return(&connection_)); } @@ -4546,9 +4546,6 @@ TEST_F(RouterTest, CrossSchemeRedirectRejectedByPolicy) { redirect_headers_->setLocation("https://www.foo.com"); EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); - EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, - isCrossSchemeRedirectAllowed()) - .WillOnce(Return(false)); EXPECT_CALL(callbacks_, recreateStream()).Times(0); response_decoder_->decodeHeaders(std::move(redirect_headers_), true); @@ -4595,9 +4592,6 @@ TEST_F(RouterTest, HttpInternalRedirectSucceeded) { sendRequest(); EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); - EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, - isCrossSchemeRedirectAllowed()) - .WillOnce(Return(false)); EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); EXPECT_CALL(callbacks_, recreateStream()).Times(1).WillOnce(Return(true)); response_decoder_->decodeHeaders(std::move(redirect_headers_), false); @@ -4643,6 +4637,9 @@ TEST_F(RouterTest, CrossSchemeRedirectAllowedByPolicy) { redirect_headers_->setLocation("http://www.foo.com"); EXPECT_CALL(connection_, ssl()).Times(1).WillOnce(Return(ssl_connection)); EXPECT_CALL(callbacks_, decodingBuffer()).Times(1); + EXPECT_CALL(callbacks_.route_->route_entry_.internal_redirect_policy_, + isCrossSchemeRedirectAllowed()) + .WillOnce(Return(true)); EXPECT_CALL(callbacks_, clearRouteCache()).Times(1); EXPECT_CALL(callbacks_, recreateStream()).Times(1).WillOnce(Return(true)); response_decoder_->decodeHeaders(std::move(redirect_headers_), false); diff --git a/test/integration/BUILD b/test/integration/BUILD index 2e0322938bcc..0d838146ee65 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -633,14 +633,14 @@ envoy_cc_test( ":http_protocol_integration_lib", "//source/common/http:header_map_lib", "//source/extensions/internal_redirect/allow_listed_routes:config", - "//source/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect:config", "//source/extensions/internal_redirect/previous_routes:config", + "//source/extensions/internal_redirect/safe_cross_scheme:config", "//test/test_common:utility_lib", "@envoy_api//envoy/config/route/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/network/http_connection_manager/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/internal_redirect/allow_listed_routes/v3:pkg_cc_proto", - "@envoy_api//envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/internal_redirect/previous_routes/v3:pkg_cc_proto", + "@envoy_api//envoy/extensions/internal_redirect/safe_cross_scheme/v3:pkg_cc_proto", ], ) diff --git a/test/integration/redirect_integration_test.cc b/test/integration/redirect_integration_test.cc index 51697435bfc3..02861c34b6c8 100644 --- a/test/integration/redirect_integration_test.cc +++ b/test/integration/redirect_integration_test.cc @@ -1,8 +1,8 @@ #include "envoy/config/route/v3/route_components.pb.h" #include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.pb.h" #include "envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.pb.h" -#include "envoy/extensions/internal_redirect/only_allow_safe_cross_scheme_redirect/v3/only_allow_safe_cross_scheme_redirect_config.pb.h" #include "envoy/extensions/internal_redirect/previous_routes/v3/previous_routes_config.pb.h" +#include "envoy/extensions/internal_redirect/safe_cross_scheme/v3/safe_cross_scheme_config.pb.h" #include "test/integration/http_protocol_integration.h" @@ -340,24 +340,24 @@ TEST_P(RedirectIntegrationTest, InternalRedirectPreventedByAllowListedRoutesPred test_server_->counter("http.config_test.passthrough_internal_redirect_predicate")->value()); } -TEST_P(RedirectIntegrationTest, - InternalRedirectPreventedByOnlyAllowSafeCrossSchemeRedirectPredicate) { - auto handle_only_allow_safe_cross_scheme_redirect_route = config_helper_.createVirtualHost( +TEST_P(RedirectIntegrationTest, InternalRedirectPreventedBySafeCrossSchemePredicate) { + auto handle_safe_cross_scheme_route = config_helper_.createVirtualHost( "handle.internal.redirect.only.allow.safe.cross.scheme.redirect"); - auto* internal_redirect_policy = - handle_only_allow_safe_cross_scheme_redirect_route.mutable_routes(0) - ->mutable_route() - ->mutable_internal_redirect_policy(); + auto* internal_redirect_policy = handle_safe_cross_scheme_route.mutable_routes(0) + ->mutable_route() + ->mutable_internal_redirect_policy(); + + internal_redirect_policy->set_allow_cross_scheme_redirect(true); auto* predicate = internal_redirect_policy->add_predicates(); - predicate->set_name("only_allow_safe_cross_scheme_redirect_predicate"); - envoy::extensions::internal_redirect::only_allow_safe_cross_scheme_redirect::v3:: - OnlyAllowSafeCrossSchemeRedirectConfig predicate_config; + predicate->set_name("safe_cross_scheme_predicate"); + envoy::extensions::internal_redirect::safe_cross_scheme::v3::SafeCrossSchemeConfig + predicate_config; predicate->mutable_typed_config()->PackFrom(predicate_config); internal_redirect_policy->mutable_max_internal_redirects()->set_value(10); - config_helper_.addVirtualHost(handle_only_allow_safe_cross_scheme_redirect_route); + config_helper_.addVirtualHost(handle_safe_cross_scheme_route); // Validate that header sanitization is only called once. config_helper_.addConfigModifier( From 416e79eff4316af8efa3d8f112e605cc5dc7c5d0 Mon Sep 17 00:00:00 2001 From: pengg Date: Wed, 13 May 2020 11:32:15 -0400 Subject: [PATCH 46/46] API comment fix. const vars. Add @param to interface documentation. AllowListed -> Allow listed. Signed-off-by: pengg --- api/envoy/config/route/v3/route_components.proto | 2 +- .../config/route/v4alpha/route_components.proto | 2 +- .../v3/allow_listed_routes_config.proto | 2 +- .../envoy/config/route/v3/route_components.proto | 2 +- .../config/route/v4alpha/route_components.proto | 2 +- .../v3/allow_listed_routes_config.proto | 2 +- include/envoy/router/internal_redirect.h | 8 +++++++- include/envoy/router/router.h | 1 + source/common/router/router.cc | 14 +++++++------- test/common/router/config_impl_test.cc | 2 +- 10 files changed, 22 insertions(+), 15 deletions(-) diff --git a/api/envoy/config/route/v3/route_components.proto b/api/envoy/config/route/v3/route_components.proto index 55aba282b2aa..cbf50ad16448 100644 --- a/api/envoy/config/route/v3/route_components.proto +++ b/api/envoy/config/route/v3/route_components.proto @@ -1625,6 +1625,6 @@ message InternalRedirectPolicy { repeated core.v3.TypedExtensionConfig predicates = 3; // Allow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to proxy such redirects downstream. + // x-forwarded-proto. The default is false. bool allow_cross_scheme_redirect = 4; } diff --git a/api/envoy/config/route/v4alpha/route_components.proto b/api/envoy/config/route/v4alpha/route_components.proto index 57b7279052bb..bd111aaac6a8 100644 --- a/api/envoy/config/route/v4alpha/route_components.proto +++ b/api/envoy/config/route/v4alpha/route_components.proto @@ -1620,6 +1620,6 @@ message InternalRedirectPolicy { repeated core.v4alpha.TypedExtensionConfig predicates = 3; // Allow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to proxy such redirects downstream. + // x-forwarded-proto. The default is false. bool allow_cross_scheme_redirect = 4; } diff --git a/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto b/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto index 026d727ddfaf..a6da5b0f5d9b 100644 --- a/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto +++ b/api/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto @@ -11,7 +11,7 @@ option java_outer_classname = "AllowListedRoutesConfigProto"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; -// [#protodoc-title: AllowListed routes internal redirect predicate] +// [#protodoc-title: Allow listed routes internal redirect predicate] // An internal redirect predicate that accepts only explicitly allowed target routes. // [#extension: envoy.internal_redirect_predicates.allow_listed_routes] diff --git a/generated_api_shadow/envoy/config/route/v3/route_components.proto b/generated_api_shadow/envoy/config/route/v3/route_components.proto index af3796f0e953..a72c8a226001 100644 --- a/generated_api_shadow/envoy/config/route/v3/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v3/route_components.proto @@ -1643,6 +1643,6 @@ message InternalRedirectPolicy { repeated core.v3.TypedExtensionConfig predicates = 3; // Allow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to proxy such redirects downstream. + // x-forwarded-proto. The default is false. bool allow_cross_scheme_redirect = 4; } diff --git a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto index c187a2ced813..ee42a986ced8 100644 --- a/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto +++ b/generated_api_shadow/envoy/config/route/v4alpha/route_components.proto @@ -1639,6 +1639,6 @@ message InternalRedirectPolicy { repeated core.v4alpha.TypedExtensionConfig predicates = 3; // Allow internal redirect to follow a target URI with a different scheme than the value of - // x-forwarded-proto. Default behavior is to proxy such redirects downstream. + // x-forwarded-proto. The default is false. bool allow_cross_scheme_redirect = 4; } diff --git a/generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto b/generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto index 026d727ddfaf..a6da5b0f5d9b 100644 --- a/generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto +++ b/generated_api_shadow/envoy/extensions/internal_redirect/allow_listed_routes/v3/allow_listed_routes_config.proto @@ -11,7 +11,7 @@ option java_outer_classname = "AllowListedRoutesConfigProto"; option java_multiple_files = true; option (udpa.annotations.file_status).package_version_status = ACTIVE; -// [#protodoc-title: AllowListed routes internal redirect predicate] +// [#protodoc-title: Allow listed routes internal redirect predicate] // An internal redirect predicate that accepts only explicitly allowed target routes. // [#extension: envoy.internal_redirect_predicates.allow_listed_routes] diff --git a/include/envoy/router/internal_redirect.h b/include/envoy/router/internal_redirect.h index 1529ef094a4b..95f624255ace 100644 --- a/include/envoy/router/internal_redirect.h +++ b/include/envoy/router/internal_redirect.h @@ -21,7 +21,11 @@ class InternalRedirectPredicate : Logger::Loggable { /** * A FilterState is provided so that predicate implementation can use it to preserve state across * internal redirects. - * + * @param filter_state supplies the filter state associated with the current request so that the + * predicates can use it to persist states across filter chains. + * @param target_route_name indicates the route that an internal redirect is targeting. + * @param downstream_is_https indicates the downstream request is using https. + * @param target_is_https indicates the internal redirect target url has https in the url. * @return whether the route specified by target_route_name is allowed to be followed. Any * predicate returning false will prevent the redirect from being followed, causing the * response to be proxied downstream. @@ -46,6 +50,8 @@ class InternalRedirectPredicateFactory : public Config::TypedFactory { ~InternalRedirectPredicateFactory() override = default; /** + * @param config contains the proto stored in TypedExtensionConfig.typed_config for the predicate. + * @param current_route_name stores the route name of the route where the predicate is installed. * @return an InternalRedirectPredicate. The given current_route_name is useful for predicates * that need to create per-route FilterState. */ diff --git a/include/envoy/router/router.h b/include/envoy/router/router.h index 275b1c476dc0..39be039e7cfd 100644 --- a/include/envoy/router/router.h +++ b/include/envoy/router/router.h @@ -250,6 +250,7 @@ class InternalRedirectPolicy { virtual bool enabled() const PURE; /** + * @param response_code the response code from the upstream. * @return whether the given response_code should trigger an internal redirect on this route. */ virtual bool shouldRedirectForResponseCode(const Http::Code& response_code) const PURE; diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 4064141cec54..516f5b403c1e 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -1472,10 +1472,10 @@ bool Filter::convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& do return false; } - auto& policy = route_entry_->internalRedirectPolicy(); + const auto& policy = route_entry_->internalRedirectPolicy(); // Don't allow serving TLS responses over plaintext unless allowed by policy. - bool scheme_is_http = schemeIsHttp(downstream_headers, *callbacks_->connection()); - bool target_is_http = absolute_url.scheme() == Http::Headers::get().SchemeValues.Http; + const bool scheme_is_http = schemeIsHttp(downstream_headers, *callbacks_->connection()); + const bool target_is_http = absolute_url.scheme() == Http::Headers::get().SchemeValues.Http; if (!policy.isCrossSchemeRedirectAllowed() && scheme_is_http != target_is_http) { config_.stats_.passthrough_internal_redirect_unsafe_scheme_.inc(); return false; @@ -1498,7 +1498,7 @@ bool Filter::convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& do } std::string original_host(downstream_headers.Host()->value().getStringView()); std::string original_path(downstream_headers.Path()->value().getStringView()); - bool scheme_is_set = (downstream_headers.Scheme() != nullptr); + const bool scheme_is_set = (downstream_headers.Scheme() != nullptr); Cleanup restore_original_headers( [&downstream_headers, original_host, original_path, scheme_is_set, scheme_is_http]() { downstream_headers.setHost(original_host); @@ -1515,15 +1515,15 @@ bool Filter::convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& do downstream_headers.setPath(absolute_url.pathAndQueryParams()); callbacks_->clearRouteCache(); - auto route = callbacks_->route(); + const auto route = callbacks_->route(); // Don't allow a redirect to a non existing route. if (!route) { config_.stats_.passthrough_internal_redirect_no_route_.inc(); return false; } - auto& route_name = route->routeEntry()->routeName(); - for (auto& predicate : policy.predicates()) { + const auto& route_name = route->routeEntry()->routeName(); + for (const auto& predicate : policy.predicates()) { if (!predicate->acceptTargetRoute(*filter_state, route_name, !scheme_is_http, !target_is_http)) { config_.stats_.passthrough_internal_redirect_predicate_.inc(); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index 1a622c497938..a48a9174f9e8 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -6937,7 +6937,7 @@ name: InternalRedirectEnabled internal_redirect_policy.shouldRedirectForResponseCode(static_cast(200))); EXPECT_EQ(1, internal_redirect_policy.maxInternalRedirects()); EXPECT_TRUE(internal_redirect_policy.predicates().empty()); - EXPECT_TRUE(internal_redirect_policy.isCrossSchemeRedirectAllowed()); + EXPECT_FALSE(internal_redirect_policy.isCrossSchemeRedirectAllowed()); } TEST_F(RouteConfigurationV2, InternalRedirctPolicyDropsInvalidRedirectCode) {