From 5f79638a362978d53ab4186d72b889e1fb36ca4c Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Tue, 17 Oct 2023 18:03:04 +0800 Subject: [PATCH 01/16] design for ext_proc Signed-off-by: gang.liu --- design/external-processing-design.md | 618 +++++++++++++++++++++++++++ design/images/ext_proc_flow.png | Bin 0 -> 92732 bytes 2 files changed, 618 insertions(+) create mode 100644 design/external-processing-design.md create mode 100644 design/images/ext_proc_flow.png diff --git a/design/external-processing-design.md b/design/external-processing-design.md new file mode 100644 index 00000000000..436dfa0341b --- /dev/null +++ b/design/external-processing-design.md @@ -0,0 +1,618 @@ +# Design for Supporting External Processing In Contour + +Status: Draft + +## Abstract + +This document describes a design for performing external processing for virtual hosts hosted by Contour. + +## Background + +For a long time, the community has been advocating for the inclusion of custom HTTP message processing in Contour, leveraging technologies such as `Lua`, `WebAssembly`, `Go`, or `ExtProc`. However, finding a suitable solution that addresses the potential [NACK][2] issues has proven to be challenging, leaving this feature unresolved. Fortunately, after thorough exploration and with the release of Envoy v1.27, the community has ultimately reached a solution utilizing [ExtProc][9]. For further information, please refer to the following links: + +- [#1015][1] Enable Lua filter +- [#1176][2] internal/grpc: properly handle Envoy ACK/NACK protocol +- [#2385][3] Add proxy WASM support +- [#2475][4] Need Envoy http filter in contour +- [#3006][5] Support for Envoy Filters (Particularly Lua) +- [#4276][6] Exploration: WASM in Contour +- [#5038][7] Support for External Processing Filter +- [#5123][8] [Feature Discussion] The ways to extend/custom functionalities for Envoy: WASM vs. Lua vs. External Processing vs. GO filter. + + +## Goals + +- Support Envoy's L7 External Processing filter for HTTP Virtual Hosts. +- Allow operators to integrate existing custom HTTP message processing service(s) with Contour. +- Decouple Contour from external processing, so it can evolve at an independent rate. +- Integrate cleanly with the HTTPProxy API. +- Supporting >=1 External Processing Services for HTTP endpoints. +- Supporting set the Global External Processing Service(s) for Virtual Hosts. +- Supporting add External processing Service(s) at different processing phases. + +## Non Goals + +- Abstracting the Envoy external HTTP message processing mechanism. + The scope of abstracting the Envoy external processing mechanism is too large to be tractable. + Abstracting the protocol would also work against the goal of being able to integrate existing HTTP message processing servers. + + +## High-Level Design + +Contour will add HTTP support for Envoy's External Processing. + + +new type: `ExternalProcessor` and its friends: `ExtProc`, `ExtProcOverride`, `ProcessingMode`, `HeaderMutationRules`,`GRPCService`, `ProcessingPhase`,`ExtProcPolicy`, will be defined for implement the design. + +A `globalExtProc` config would define a global external processing configuration for all hosts and routes. + +```yaml +apiVersion: projectcontour.io/v1alpha1 +kind: ContourConfiguration +... +spec: + globalExtProc: + processors: + - grpcService: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc + namespace: extproc-test + failOpen: false + responseTimeout: 30s + processingMode: + requestBodyMode: 0 + requestHeaderMode: 1 + requestTrailerMode: 2 + responseBodyMode: 0 + responseHeaderMode: 1 + responseTrailerMode: 2 + - grpcService: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc2 + namespace: extproc-test + failOpen: true + responseTimeout: 60s + processingMode: + requestBodyMode: 0 + requestHeaderMode: 1 + requestTrailerMode: 2 + responseBodyMode: 0 + responseHeaderMode: 1 + responseTrailerMode: 2 +... +``` +### Opting out from Global/VirtualHost External Processing + +By default, enabling `Global` external processing would allow custom HTTP message processing on all virtual hosts. On the other hand, if processing is done at the `virtualHost` level, custom HTTP message processing would only be enabled for the specific virtual host in question. However, individual owners of HTTPProxy will have the flexibility to modify or override this setting. + +#### Override/Disabling External Processing + +##### Virtual Host level + +`Override(Only for HTTPS)/Disable` the global external processing on the virtual host. This setting would **override/disable** all routes on said virtual host. + +```yaml +kind: HTTPProxy +... +spec: + virtualhost: + extProc: + extProcPolicy: + disabled: false # true: for disable + processors: + - grpcService: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc3 + namespace: extproc-test + failOpen: true + responseTimeout: 60s + processingMode: + requestBodyMode: 0 + requestHeaderMode: 1 + requestTrailerMode: 2 + responseBodyMode: 0 + responseHeaderMode: 1 + responseTrailerMode: 2 +... +``` + +##### Route level + +For more precise control, the `Global` and/or `VirtualHost` external processing can also be **overrideed/toggled** on an individual route. the priority is as follows: `Global` < `VirtualHost` < `Route` + + +```yaml +kind: HTTPProxy +... +spec: + routes: + - conditions: + - prefix: /disabled + extProcPolicy: + disabled: true # diabled for /disabled + services: + - name: http-echo-service + port: 5678 + - conditions: + - prefix: /override + extProcPolicy: + overrides: # override for /override + grpcService: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc4 + namespace: extproc-test + failOpen: true + responseTimeout: 60s + processingMode: + requestBodyMode: 0 + requestHeaderMode: 1 + requestTrailerMode: 2 + responseBodyMode: 0 + responseHeaderMode: 1 + responseTrailerMode: 2 + services: + - name: http-echo-service2 + port: 5678 + - conditions: + - prefix: /noop # noop + services: + - name: http-echo-service3 + port: 5678 +... + +``` + + +## Detailed Design + +### HTTPProxy Newly Added Types: + +```go + +// HeaderSendMode control how headers and trailers are handled +type HeaderSendMode int32 + +const ( + // The default HeaderSendMode depends on which part of the message is being + // processed. By default, request and response headers are sent, + // while trailers are skipped. + ProcessingMode_DEFAULT HeaderSendMode = 0 + + // Send the header or trailer. + ProcessingMode_SEND HeaderSendMode = 1 + + // Do not send the header or trailer. + ProcessingMode_SKIP HeaderSendMode = 2 +) + +// BodySendMode control how the request and response bodies are handled +type BodySendMode int32 + +const ( + // Do not send the body at all. This is the default. + ProcessingMode_NONE BodySendMode = 0 + + // Stream the body to the server in pieces as they arrive at the + // proxy. + ProcessingMode_STREAMED BodySendMode = 1 + + // Buffer the message body in memory and send the entire body at once. + // If the body exceeds the configured buffer limit, then the + // downstream system will receive an error. + ProcessingMode_BUFFERED BodySendMode = 2 + + // Buffer the message body in memory and send the entire body in one + // chunk. If the body exceeds the configured buffer limit, then the body contents + // up to the buffer limit will be sent. + ProcessingMode_BUFFERED_PARTIAL BodySendMode = 3 +) + +// HeaderMutationRules specifies what headers may be manipulated by a processing filter. +// This set of rules makes it possible to control which modifications a filter may make. +type HeaderMutationRules struct { + // By default, certain headers that could affect processing of subsequent + // filters or request routing cannot be modified. These headers are + // ``host``, ``:authority``, ``:scheme``, and ``:method``. Setting this parameter + // to true allows these headers to be modified as well. + // + // +optional + AllowAllRouting bool `json:"allowAllRouting,omitempty"` + + // If true, allow modification of envoy internal headers. By default, these + // start with ``x-envoy`` but this may be overridden in the ``Bootstrap`` + // configuration. Default is false. + // + // +optional + AllowEnvoy bool `json:"allowEnvoy,omitempty"` + + // If true, prevent modification of any system header, defined as a header + // that starts with a ``:`` character, regardless of any other settings. + // A processing server may still override the ``:status`` of an HTTP response + // using an ``ImmediateResponse`` message. Default is false. + // + // +optional + DisallowSystem bool `json:"disallowSystem,omitempty"` + + // If true, prevent modifications of all header values, regardless of any + // other settings. A processing server may still override the ``:status`` + // of an HTTP response using an ``ImmediateResponse`` message. Default is false. + // + // +optional + DisallowAll bool `json:"disallowAll,omitempty"` + + // If true, and if the rules in this list cause a header mutation to be + // disallowed, then the filter using this configuration will terminate the + // request with a 500 error. In addition, regardless of the setting of this + // parameter, any attempt to set, add, or modify a disallowed header will + // cause the ``rejected_header_mutations`` counter to be incremented. + // Default is false. + // + // +optional + DisallowIsError bool `json:"disallowIsError,omitempty"` +} + +// ProcessingMode describes which parts of an HTTP request and response are sent to a remote server +// and how they are delivered. +type ProcessingMode struct { + // How to handle the request header. Default is "SEND". + // + // +optional + RequestHeaderMode HeaderSendMode `json:"requestHeaderMode,omitempty"` + + // How to handle the response header. Default is "SEND". + // + // +optional + ResponseHeaderMode HeaderSendMode `json:"responseHeaderMode,omitempty"` + + // How to handle the request body. Default is "NONE". + // + // +optional + RequestBodyMode BodySendMode `json:"requestBodyMode,omitempty"` + + // How do handle the response body. Default is "NONE". + // + // +optional + ResponseBodyMode BodySendMode `json:"responseBodyMode,omitempty"` + + // How to handle the request trailers. Default is "SKIP". + // + // +optional + RequestTrailerMode HeaderSendMode `json:"requestTrailerMode,omitempty"` + + // How to handle the response trailers. Default is "SKIP". + // + // +optional + ResponseTrailerMode HeaderSendMode `json:"responseTrailerMode,omitempty"` +} + +// GRPCService configure the gRPC service that the filter will communicate with. +type GRPCService struct { + // ExtensionServiceRef specifies the extension resource that will handle the client requests. + // + // +optional + ExtensionServiceRef ExtensionServiceReference `json:"extensionRef,omitempty"` + + // ResponseTimeout sets how long the proxy should wait for responses. + // Timeout durations are expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + // The string "infinity" is also a valid input and specifies no timeout. + // + // +optional + // +kubebuilder:validation:Pattern=`^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$` + ResponseTimeout string `json:"responseTimeout,omitempty"` + + // If FailOpen is true, the client request is forwarded to the upstream service + // even if the server fails to respond. This field should not be + // set in most cases. + // + // +optional + FailOpen bool `json:"failOpen,omitempty"` +} + +// ProcessingPhase define the phase in the filter chain where the external processing filter will be injected +type ProcessingPhase string + +const ( + // UnspecifiedPhase decides where to insert the external processing service. + // This will generally be at the end of the filter chain, right before the Router + UnspecifiedPhase ProcessingPhase = "UnspecifiedPhase" + + // Insert before contour authentication filter(s). + AuthN ProcessingPhase = "AuthN" + + // Insert before contour authorization filter(s) and after the authentication filter(s). + AuthZ ProcessingPhase = "AuthZ" + + // Insert before contour CORS filter(s). + CORS ProcessingPhase = "CORS" + + // Insert before contour RateLimit. + RateLimit ProcessingPhase = "RateLimit" +) + +// ExtProc defines the envoy External Processing filter which allows an external service to act on HTTP traffic in a flexible way +// The external server must implement the v3 Envoy external processing GRPC protocol +// (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). +type ExtProc struct { + // Phase determines where in the filter chain this extProc is to be injected. + // + // +optional + Phase ProcessingPhase `json:"phase,omitempty"` + + // Priority determines ordering of processing filters in the same phase. When multiple extProc are applied to the same workload in the same phase, + // they will be applied by priority, in descending order, If priority is not set or two extProc exist with the same value, + // they will follow the order in which extProc(s) are added, Defaults to 0. + // + // +optional + Priority int32 `json:"priority,omitempty"` + + // GRPCService configure the gRPC service that the filter will communicate with. + // + // +optional + GRPCService *GRPCService `json:"grpcService,omitempty"` + + // ProcessingMode describes which parts of an HTTP request and response are sent to a remote server + // and how they are delivered. + // + // +optional + ProcessingMode *ProcessingMode `json:"processingMode,omitempty"` + + // MutationRules specifies what headers may be manipulated by a processing filter. + // This set of rules makes it possible to control which modifications a filter may make. + // + // +optional + MutationRules *HeaderMutationRules `json:"mutationRules,omitempty"` +} + +// ExtProcOverride override aspects of the configuration for this route. +// A set of overrides in a more specific configuration will override a “disabled” flag set in a less-specific one. +type ExtProcOverride struct { + // GRPCService configure the gRPC service that the filter will communicate with. + // + // +optional + GRPCService *GRPCService `json:"grpcService,omitempty"` + + // ProcessingMode describes which parts of an HTTP request and response are sent to a remote server + // and how they are delivered. + // + // +optional + ProcessingMode *ProcessingMode `json:"processingMode,omitempty"` +} + +// ExternalProcessor defines a processing filter list and the policy for fine-grained at VirutalHost and/or Route level. +type ExternalProcessor struct { + // Processors defines a processing filter list,and each filter in the list + // will be added to the corresponding processing Priority in ascending order of it's Priority within the same phase. + // If no phase is specified, it will be added before the Router. + // If no Priority is specified, the filters will be added in the order they appear in the list. + // + // +optional + Processors []ExtProc `json:"processors,omitempty"` + + // ExtProcPolicy sets a external processing policy. + // This policy will be used unless overridden by individual routes. + // + // **Note: for the Global External Processor, it's must be nil. + // + // +optional + ExtProcPolicy *ExtProcPolicy `json:"extProcPolicy,omitempty"` +} + +// ExtProcPolicy modifies how requests/responses are operated. +type ExtProcPolicy struct { + // When true, this field disables client request external processing + // for the scope of the policy. + // Precisely one of disabled, overrides must be set. + // + // +optional + Disabled bool `json:"disabled,omitempty"` + + // Overrides aspects of the configuration for this route. + // + // **Note: for VirtualHost, it's must be nil. + // + // +optional + Overrides *ExtProcOverride `json:"overrides,omitempty"` +} +``` + +### HTTPProxy Changes + +```Go +// VirtualHost appears at most once. If it is present, the object is considered +// to be a "root". +type VirtualHost struct { + ... + // ExternalProcessor contains a list of external processors which allow to act on HTTP traffic in a flexible way + // and the policy for fine-grained at VirtualHost level. + // + // +optional + ExternalProcessor *ExternalProcessor `json:"extProc,omitempty"` +} + +// Route contains the set of routes for a virtual host. +type Route struct { + ... + // ExtProcPolicy updates the external processing policy that was set + // on the root HTTPProxy object for client requests/responses that + // match this route. + // + // +optional + ExtProcPolicy *ExtProcPolicy `json:"extProcPolicy,omitempty"` +} +``` + +### Contour Configuration changes + +An external processing service can be configured in the Contour config file. +This External processing configuration will be used for all HTTP & HTTPS(if not ovrride at VirtualHost and/or Route Level) routes. + +```go +type Parameters struct { + ... + // GlobalExternalProcessor optionally holds properties of the global external processing configurations. + GlobalExternalProcessor *contour_api_v1.ExternalProcessor `yaml:"globalExtProc,omitempty"` + ... +} + +type ContourConfigurationSpec struct { + ... + // GlobalExternalProcessor allows envoys external processing filters + // to be enabled for all virtual hosts. + // + // +optional + GlobalExternalProcessor *contour_api_v1.ExternalProcessor `json:"globalExtProc,omitempty"` + ... +} +``` + +An operator configures external processing on a root `HTTPProxy` by setting the `VirtualHost.ExternalProcessor` field. +Setting this field without also setting the `TLS` field is an error. + +### Progressing Flow + +This chart (copy from [External Processing Filter][10]) shows the simplest possible implementation of the filter -- a filter server receives the HTTP request headers, decides to accept the response (and can optionally modify the headers) so it closes the stream cleanly. At this point it is no longer involved in filter processing. see [External Processing Filter][10] for more information +![drawing](images/ext_proc_flow.png) + +### Sample configurations + +Please refer to examples/external-processing + +With this proposal, contour will generate the envoy configuration snippet below. +NOTE: this snippet only represents the relevant bits of the Route. + +##### Envoy + +```yaml +{ + ... + "dynamic_route_configs": [ + { + "route_config": { + "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "name": "https/https.projectcontour.io", + "virtual_hosts": [ + { + "domains": [ + "https.projectcontour.io" + ], + "name": "https.projectcontour.io", + "routes": [ + { + "match": { + "prefix": "/disabled" + }, + "route": { + "cluster": "extproc-test/http-echo-service/5678/da39a3ee5e" + }, + "typed_per_filter_config": { + "envoy.filters.http.ext_proc": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExtProcPerRoute", + "disabled": true + } + } + }, + { + "match": { + "prefix": "/use-route" + }, + "route": { + "cluster": "extproc-test/http-echo-service3/5678/da39a3ee5e" + }, + "typed_per_filter_config": { + "envoy.filters.http.ext_proc": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExtProcPerRoute", + "overrides": { + "grpc_service": { + "envoy_grpc": { + "authority": "extension.extproc-test.extproc-extsvc3", + "cluster_name": "extension/extproc-test/extproc-extsvc3" + }, + "timeout": "30s" + }, + "processing_mode": { + "request_header_mode": "SEND", + "request_trailer_mode": "SKIP", + "response_header_mode": "SEND", + "response_trailer_mode": "SKIP" + } + } + } + } + }, + { + "match": { + "prefix": "/use-vh" + }, + "route": { + "cluster": "extproc-test/http-echo-service2/5678/da39a3ee5e" + } + } + ] + } + ] + }, + "version_info": "7" + }, + { + "route_config": { + "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", + "name": "ingress_http", + "virtual_hosts": [ + { + "domains": [ + "http.projectcontour.io" + ], + "name": "http.projectcontour.io", + "routes": [ + { + "match": { + "prefix": "/use-default" + }, + "route": { + "cluster": "extproc-test/http-echo-service4/5678/da39a3ee5e" + } + } + ] + } + ] + }, + "version_info": "7" + } + ] + ... +} +``` + +## Alternatives Considered +_TBD_ + +# Metrics +_TBD_ + +## Security Considerations + +- HTTPS sessions between Envoy and the processing server are required. + TLS validation can be configured if necessary (should be recommended), +- Processing services can run in separate Kubernetes namespaces with limited privilege. + +## Compatibility + +HTTPProxy will opt-out to use the default global external processing explicitly and the `GlobalExtProc` in the Contour configuration is optional. This solution should not introduce any regressions or breaking changes. + + +[1]: https://github.com/projectcontour/contour/issues/1015 +[2]: https://github.com/projectcontour/contour/issues/1176 +[3]: https://github.com/projectcontour/contour/issues/2385 +[4]: https://github.com/projectcontour/contour/issues/2475 +[5]: https://github.com/projectcontour/contour/issues/3006 +[6]: https://github.com/projectcontour/contour/issues/4276 +[7]: https://github.com/projectcontour/contour/issues/5038 +[8]: https://github.com/projectcontour/contour/issues/5123 +[9]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto +[10]: https://docs.google.com/document/d/1IZqm5IUnG9gc2VqwGaN5C2TZAD9_QbsY9Vvy5vr9Zmw/edit#heading=h.5irk4csrpu0y + diff --git a/design/images/ext_proc_flow.png b/design/images/ext_proc_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..7a5b13481ad638cdaf3f167725415bb39e4f5800 GIT binary patch literal 92732 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 literal 0 HcmV?d00001 From dfd7d4a0ab3771194773697d83c9b0d4eef381da Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Tue, 17 Oct 2023 18:06:18 +0800 Subject: [PATCH 02/16] fix typo Signed-off-by: gang.liu --- design/external-processing-design.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 436dfa0341b..c036e69e2d7 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -475,14 +475,15 @@ Setting this field without also setting the `TLS` field is an error. ### Progressing Flow -This chart (copy from [External Processing Filter][10]) shows the simplest possible implementation of the filter -- a filter server receives the HTTP request headers, decides to accept the response (and can optionally modify the headers) so it closes the stream cleanly. At this point it is no longer involved in filter processing. see [External Processing Filter][10] for more information +This chart (copy from [External Processing Filter][10]) shows the simplest possible implementation of the filter -- a filter server receives the HTTP request headers, decides to accept the response (and can optionally modify the headers) so it closes the stream cleanly. At this point it is no longer involved in filter processing. see [External Processing Filter][10] for more information. + ![drawing](images/ext_proc_flow.png) ### Sample configurations -Please refer to examples/external-processing +Please refer to `examples/external-processing`. -With this proposal, contour will generate the envoy configuration snippet below. +With this proposal, contour will generate the envoy configuration snippet below for `examples/external-processing`. NOTE: this snippet only represents the relevant bits of the Route. ##### Envoy From 2b66cc66fb55954e34f466b497d8a918020d6f87 Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Wed, 10 Jan 2024 15:38:57 +0800 Subject: [PATCH 03/16] change field's type Signed-off-by: gang.liu --- design/external-processing-design.md | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index c036e69e2d7..7d1cb929cbe 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -174,41 +174,41 @@ spec: ```go // HeaderSendMode control how headers and trailers are handled -type HeaderSendMode int32 +type HeaderSendMode string const ( // The default HeaderSendMode depends on which part of the message is being // processed. By default, request and response headers are sent, // while trailers are skipped. - ProcessingMode_DEFAULT HeaderSendMode = 0 + ProcessingMode_DEFAULT HeaderSendMode = "DEFAULT" // Send the header or trailer. - ProcessingMode_SEND HeaderSendMode = 1 + ProcessingMode_SEND HeaderSendMode = "SEND" // Do not send the header or trailer. - ProcessingMode_SKIP HeaderSendMode = 2 + ProcessingMode_SKIP HeaderSendMode = "SKIP" ) // BodySendMode control how the request and response bodies are handled -type BodySendMode int32 +type BodySendMode string const ( // Do not send the body at all. This is the default. - ProcessingMode_NONE BodySendMode = 0 + ProcessingMode_NONE BodySendMode = "NONE" // Stream the body to the server in pieces as they arrive at the // proxy. - ProcessingMode_STREAMED BodySendMode = 1 + ProcessingMode_STREAMED BodySendMode = "STREAMED" // Buffer the message body in memory and send the entire body at once. // If the body exceeds the configured buffer limit, then the // downstream system will receive an error. - ProcessingMode_BUFFERED BodySendMode = 2 + ProcessingMode_BUFFERED BodySendMode = "BUFFERED" // Buffer the message body in memory and send the entire body in one // chunk. If the body exceeds the configured buffer limit, then the body contents // up to the buffer limit will be sent. - ProcessingMode_BUFFERED_PARTIAL BodySendMode = 3 + ProcessingMode_BUFFERED_PARTIAL BodySendMode = "BUFFERED_PARTIAL" ) // HeaderMutationRules specifies what headers may be manipulated by a processing filter. @@ -317,20 +317,19 @@ type GRPCService struct { type ProcessingPhase string const ( - // UnspecifiedPhase decides where to insert the external processing service. - // This will generally be at the end of the filter chain, right before the Router - UnspecifiedPhase ProcessingPhase = "UnspecifiedPhase" + // DefaultPhase decides insert the external processing service at the end of the filter chain, right before the Router. + DefaultPhase ProcessingPhase = "DefaultPhase" // Insert before contour authentication filter(s). AuthN ProcessingPhase = "AuthN" - // Insert before contour authorization filter(s) and after the authentication filter(s). + // Insert before contour authorization filter(s) and after the authentication filter(s). AuthZ ProcessingPhase = "AuthZ" - // Insert before contour CORS filter(s). + // Insert before contour CORS filter(s). CORS ProcessingPhase = "CORS" - // Insert before contour RateLimit. + // Insert before contour RateLimit. RateLimit ProcessingPhase = "RateLimit" ) From a3544229e14e57adac83d16afe6b274392c2fb2e Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Wed, 10 Jan 2024 16:01:32 +0800 Subject: [PATCH 04/16] change field's definition & comments Signed-off-by: gang.liu --- design/external-processing-design.md | 46 ++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 7d1cb929cbe..127076ce55d 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -173,7 +173,7 @@ spec: ```go -// HeaderSendMode control how headers and trailers are handled +// HeaderSendMode control how headers and trailers are handled. type HeaderSendMode string const ( @@ -216,15 +216,15 @@ const ( type HeaderMutationRules struct { // By default, certain headers that could affect processing of subsequent // filters or request routing cannot be modified. These headers are - // ``host``, ``:authority``, ``:scheme``, and ``:method``. Setting this parameter - // to true allows these headers to be modified as well. + // ``host``, ``:authority``, ``:scheme``, and ``:method``. + // Setting this parameter to true allows these headers to be modified as well. // // +optional AllowAllRouting bool `json:"allowAllRouting,omitempty"` // If true, allow modification of envoy internal headers. By default, these - // start with ``x-envoy`` but this may be overridden in the ``Bootstrap`` - // configuration. Default is false. + // start with ``x-envoy`` but this may be overridden in the ``Bootstrap`` configuration. + // Default is false. // // +optional AllowEnvoy bool `json:"allowEnvoy,omitempty"` @@ -232,14 +232,16 @@ type HeaderMutationRules struct { // If true, prevent modification of any system header, defined as a header // that starts with a ``:`` character, regardless of any other settings. // A processing server may still override the ``:status`` of an HTTP response - // using an ``ImmediateResponse`` message. Default is false. + // using an ``ImmediateResponse`` message. + // Default is false. // // +optional DisallowSystem bool `json:"disallowSystem,omitempty"` // If true, prevent modifications of all header values, regardless of any // other settings. A processing server may still override the ``:status`` - // of an HTTP response using an ``ImmediateResponse`` message. Default is false. + // of an HTTP response using an ``ImmediateResponse`` message. + // Default is false. // // +optional DisallowAll bool `json:"disallowAll,omitempty"` @@ -258,33 +260,51 @@ type HeaderMutationRules struct { // ProcessingMode describes which parts of an HTTP request and response are sent to a remote server // and how they are delivered. type ProcessingMode struct { - // How to handle the request header. Default is "SEND". + // How to handle the request header. + // Default is "SEND". // + // +kubebuilder:validation:Enum=DEFAULT;SEND;SKIP + // +kubebuilder:default=SEND // +optional RequestHeaderMode HeaderSendMode `json:"requestHeaderMode,omitempty"` - // How to handle the response header. Default is "SEND". + // How to handle the response header. + // Default is "SEND". // + // +kubebuilder:validation:Enum=DEFAULT;SEND;SKIP + // +kubebuilder:default=SEND // +optional ResponseHeaderMode HeaderSendMode `json:"responseHeaderMode,omitempty"` - // How to handle the request body. Default is "NONE". + // How to handle the request body. + // Default is "NONE". // + // +kubebuilder:validation:Enum=NONE;STREAMED;BUFFERED;BUFFERED_PARTIAL + // +kubebuilder:default=NONE // +optional RequestBodyMode BodySendMode `json:"requestBodyMode,omitempty"` - // How do handle the response body. Default is "NONE". + // How do handle the response body. + // Default is "NONE". // + // +kubebuilder:validation:Enum=NONE;STREAMED;BUFFERED;BUFFERED_PARTIAL + // +kubebuilder:default=NONE // +optional ResponseBodyMode BodySendMode `json:"responseBodyMode,omitempty"` - // How to handle the request trailers. Default is "SKIP". + // How to handle the request trailers. + // Default is "SKIP". // + // +kubebuilder:validation:Enum=DEFAULT;SEND;SKIP + // +kubebuilder:default=SKIP // +optional RequestTrailerMode HeaderSendMode `json:"requestTrailerMode,omitempty"` - // How to handle the response trailers. Default is "SKIP". + // How to handle the response trailers. + // Default is "SKIP". // + // +kubebuilder:validation:Enum=DEFAULT;SEND;SKIP + // +kubebuilder:default=SKIP // +optional ResponseTrailerMode HeaderSendMode `json:"responseTrailerMode,omitempty"` } From 106347b98384f986042f56f8c28aadeb2f59b387 Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Wed, 10 Jan 2024 17:08:57 +0800 Subject: [PATCH 05/16] change field's definition Signed-off-by: gang.liu --- design/external-processing-design.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 127076ce55d..dc737e029b7 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -180,13 +180,13 @@ const ( // The default HeaderSendMode depends on which part of the message is being // processed. By default, request and response headers are sent, // while trailers are skipped. - ProcessingMode_DEFAULT HeaderSendMode = "DEFAULT" + ProcessingModeDefault HeaderSendMode = "DEFAULT" // Send the header or trailer. - ProcessingMode_SEND HeaderSendMode = "SEND" + ProcessingModeSend HeaderSendMode = "SEND" // Do not send the header or trailer. - ProcessingMode_SKIP HeaderSendMode = "SKIP" + ProcessingModeSkip HeaderSendMode = "SKIP" ) // BodySendMode control how the request and response bodies are handled @@ -194,21 +194,21 @@ type BodySendMode string const ( // Do not send the body at all. This is the default. - ProcessingMode_NONE BodySendMode = "NONE" + ProcessingModeNone BodySendMode = "NONE" // Stream the body to the server in pieces as they arrive at the // proxy. - ProcessingMode_STREAMED BodySendMode = "STREAMED" + ProcessingModeStreamed BodySendMode = "STREAMED" // Buffer the message body in memory and send the entire body at once. // If the body exceeds the configured buffer limit, then the // downstream system will receive an error. - ProcessingMode_BUFFERED BodySendMode = "BUFFERED" + ProcessingModeBuffered BodySendMode = "BUFFERED" // Buffer the message body in memory and send the entire body in one // chunk. If the body exceeds the configured buffer limit, then the body contents // up to the buffer limit will be sent. - ProcessingMode_BUFFERED_PARTIAL BodySendMode = "BUFFERED_PARTIAL" + ProcessingModeBufferedPartial BodySendMode = "BUFFERED_PARTIAL" ) // HeaderMutationRules specifies what headers may be manipulated by a processing filter. From 772c3f4c3dea90df24672984872b5d6a3ae3eccb Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Wed, 10 Jan 2024 17:15:20 +0800 Subject: [PATCH 06/16] change the demo's field Signed-off-by: gang.liu --- design/external-processing-design.md | 48 ++++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index dc737e029b7..a1fa1754bcb 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -61,12 +61,12 @@ spec: failOpen: false responseTimeout: 30s processingMode: - requestBodyMode: 0 - requestHeaderMode: 1 - requestTrailerMode: 2 - responseBodyMode: 0 - responseHeaderMode: 1 - responseTrailerMode: 2 + requestBodyMode: NONE + requestHeaderMode: SEND + requestTrailerMode: SKIP + responseBodyMode: NONE + responseHeaderMode: SEND + responseTrailerMode: SKIP - grpcService: extensionRef: apiVersion: projectcontour.io/v1alpha1 @@ -75,12 +75,12 @@ spec: failOpen: true responseTimeout: 60s processingMode: - requestBodyMode: 0 - requestHeaderMode: 1 - requestTrailerMode: 2 - responseBodyMode: 0 - responseHeaderMode: 1 - responseTrailerMode: 2 + requestBodyMode: NONE + requestHeaderMode: SEND + requestTrailerMode: SKIP + responseBodyMode: NONE + responseHeaderMode: SEND + responseTrailerMode: SKIP ... ``` ### Opting out from Global/VirtualHost External Processing @@ -110,12 +110,12 @@ spec: failOpen: true responseTimeout: 60s processingMode: - requestBodyMode: 0 - requestHeaderMode: 1 - requestTrailerMode: 2 - responseBodyMode: 0 - responseHeaderMode: 1 - responseTrailerMode: 2 + requestBodyMode: NONE + requestHeaderMode: SEND + requestTrailerMode: SKIP + responseBodyMode: NONE + responseHeaderMode: SEND + responseTrailerMode: SKIP ... ``` @@ -148,12 +148,12 @@ spec: failOpen: true responseTimeout: 60s processingMode: - requestBodyMode: 0 - requestHeaderMode: 1 - requestTrailerMode: 2 - responseBodyMode: 0 - responseHeaderMode: 1 - responseTrailerMode: 2 + requestBodyMode: NONE + requestHeaderMode: SEND + requestTrailerMode: SKIP + responseBodyMode: NONE + responseHeaderMode: SEND + responseTrailerMode: SKIP services: - name: http-echo-service2 port: 5678 From 0e2851f7f92fbe2f40b95c90c8e11f7e02b5a923 Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Wed, 21 Feb 2024 13:46:52 +0800 Subject: [PATCH 07/16] fix typo Signed-off-by: gang.liu --- design/external-processing-design.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index a1fa1754bcb..72f75554ad4 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -132,7 +132,7 @@ spec: - conditions: - prefix: /disabled extProcPolicy: - disabled: true # diabled for /disabled + disabled: true # disabled for /disabled services: - name: http-echo-service port: 5678 @@ -468,7 +468,7 @@ type Route struct { ### Contour Configuration changes An external processing service can be configured in the Contour config file. -This External processing configuration will be used for all HTTP & HTTPS(if not ovrride at VirtualHost and/or Route Level) routes. +This External processing configuration will be used for all HTTP & HTTPS(if not override at VirtualHost and/or Route Level) routes. ```go type Parameters struct { From 6d8883f1097715ce2023aade50e1aa6359293a32 Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Tue, 12 Mar 2024 18:22:22 +0800 Subject: [PATCH 08/16] no global ext_proc & add name to ext_proc Signed-off-by: gang.liu --- design/external-processing-design.md | 171 ++++++++++----------------- 1 file changed, 61 insertions(+), 110 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 72f75554ad4..19af3359856 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -27,7 +27,6 @@ For a long time, the community has been advocating for the inclusion of custom H - Decouple Contour from external processing, so it can evolve at an independent rate. - Integrate cleanly with the HTTPProxy API. - Supporting >=1 External Processing Services for HTTP endpoints. -- Supporting set the Global External Processing Service(s) for Virtual Hosts. - Supporting add External processing Service(s) at different processing phases. ## Non Goals @@ -44,63 +43,21 @@ Contour will add HTTP support for Envoy's External Processing. new type: `ExternalProcessor` and its friends: `ExtProc`, `ExtProcOverride`, `ProcessingMode`, `HeaderMutationRules`,`GRPCService`, `ProcessingPhase`,`ExtProcPolicy`, will be defined for implement the design. -A `globalExtProc` config would define a global external processing configuration for all hosts and routes. +### Opting out from VirtualHost External Processing -```yaml -apiVersion: projectcontour.io/v1alpha1 -kind: ContourConfiguration -... -spec: - globalExtProc: - processors: - - grpcService: - extensionRef: - apiVersion: projectcontour.io/v1alpha1 - name: extproc-extsvc - namespace: extproc-test - failOpen: false - responseTimeout: 30s - processingMode: - requestBodyMode: NONE - requestHeaderMode: SEND - requestTrailerMode: SKIP - responseBodyMode: NONE - responseHeaderMode: SEND - responseTrailerMode: SKIP - - grpcService: - extensionRef: - apiVersion: projectcontour.io/v1alpha1 - name: extproc-extsvc2 - namespace: extproc-test - failOpen: true - responseTimeout: 60s - processingMode: - requestBodyMode: NONE - requestHeaderMode: SEND - requestTrailerMode: SKIP - responseBodyMode: NONE - responseHeaderMode: SEND - responseTrailerMode: SKIP -... -``` -### Opting out from Global/VirtualHost External Processing - -By default, enabling `Global` external processing would allow custom HTTP message processing on all virtual hosts. On the other hand, if processing is done at the `virtualHost` level, custom HTTP message processing would only be enabled for the specific virtual host in question. However, individual owners of HTTPProxy will have the flexibility to modify or override this setting. +If processing is done at the `VirtualHost` level, custom HTTP message processing would only be enabled for the specific virtual host in question. However, individual owners of HTTPProxy will have the flexibility to modify or override this setting. #### Override/Disabling External Processing ##### Virtual Host level -`Override(Only for HTTPS)/Disable` the global external processing on the virtual host. This setting would **override/disable** all routes on said virtual host. - +If `disabled` is set to true, the external processor will not be added to the filter chain of the HCM generated by Contour, and the name for it must be unique because it will be used as the key to find the external processor being operated on when implementing a `Route` level external processing policy. ```yaml kind: HTTPProxy ... spec: virtualhost: extProc: - extProcPolicy: - disabled: false # true: for disable processors: - grpcService: extensionRef: @@ -116,12 +73,16 @@ spec: responseBodyMode: NONE responseHeaderMode: SEND responseTrailerMode: SKIP + name: extproc-extsvc3 # the name must be unique + disabled: false + phase: DefaultPhase + priority: 0 ... ``` ##### Route level -For more precise control, the `Global` and/or `VirtualHost` external processing can also be **overrideed/toggled** on an individual route. the priority is as follows: `Global` < `VirtualHost` < `Route` +For more precise control, the `VirtualHost` external processing can also be **overrideed/toggled** on an individual route. ```yaml @@ -131,29 +92,32 @@ spec: routes: - conditions: - prefix: /disabled - extProcPolicy: - disabled: true # disabled for /disabled + extProcPolicies: + - disabled: true # disabled for /disabled + name: extsvc3 services: - name: http-echo-service port: 5678 - conditions: - prefix: /override - extProcPolicy: - overrides: # override for /override - grpcService: - extensionRef: - apiVersion: projectcontour.io/v1alpha1 - name: extproc-extsvc4 - namespace: extproc-test - failOpen: true - responseTimeout: 60s - processingMode: - requestBodyMode: NONE - requestHeaderMode: SEND - requestTrailerMode: SKIP - responseBodyMode: NONE - responseHeaderMode: SEND - responseTrailerMode: SKIP + extProcPolicies: # override extsvc3's grpc sevice to extproc-extsvc4 + - disabled: false + name: extsvc3 + overrides: + grpcService: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc4 + namespace: lg-test + failOpen: true + responseTimeout: 31s + processingMode: + requestBodyMode: NONE + requestHeaderMode: SKIP + requestTrailerMode: SKIP + responseBodyMode: NONE + responseHeaderMode: SEND + responseTrailerMode: SKIP services: - name: http-echo-service2 port: 5678 @@ -357,6 +321,16 @@ const ( // The external server must implement the v3 Envoy external processing GRPC protocol // (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). type ExtProc struct { + // Unique name for the external processor. + // +kubebuilder:validation:Required + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + + // When true, this external processor will not be added to the listener's filter chain + // + // +optional + Disabled bool `json:"disabled,omitempty"` + // Phase determines where in the filter chain this extProc is to be injected. // // +optional @@ -412,31 +386,28 @@ type ExternalProcessor struct { // +optional Processors []ExtProc `json:"processors,omitempty"` - // ExtProcPolicy sets a external processing policy. - // This policy will be used unless overridden by individual routes. - // - // **Note: for the Global External Processor, it's must be nil. - // - // +optional - ExtProcPolicy *ExtProcPolicy `json:"extProcPolicy,omitempty"` } // ExtProcPolicy modifies how requests/responses are operated. type ExtProcPolicy struct { - // When true, this field disables client request external processing + // The name of the external processor being overrided. + // +kubebuilder:validation:Required + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + + // When true, this field disables the specific client request external processor // for the scope of the policy. - // Precisely one of disabled, overrides must be set. + // if both disabled and overrides are set. disabled. // // +optional Disabled bool `json:"disabled,omitempty"` // Overrides aspects of the configuration for this route. // - // **Note: for VirtualHost, it's must be nil. - // // +optional Overrides *ExtProcOverride `json:"overrides,omitempty"` } + ``` ### HTTPProxy Changes @@ -456,41 +427,16 @@ type VirtualHost struct { // Route contains the set of routes for a virtual host. type Route struct { ... - // ExtProcPolicy updates the external processing policy that was set - // on the root HTTPProxy object for client requests/responses that - // match this route. + // ExtProcPolicies updates the external processing policy/policies that were set + // on the root HTTPProxy object for client requests/responses // // +optional - ExtProcPolicy *ExtProcPolicy `json:"extProcPolicy,omitempty"` + ExtProcPolicies []ExtProcPolicy `json:"extProcPolicies,omitempty"` } ``` -### Contour Configuration changes - -An external processing service can be configured in the Contour config file. -This External processing configuration will be used for all HTTP & HTTPS(if not override at VirtualHost and/or Route Level) routes. - -```go -type Parameters struct { - ... - // GlobalExternalProcessor optionally holds properties of the global external processing configurations. - GlobalExternalProcessor *contour_api_v1.ExternalProcessor `yaml:"globalExtProc,omitempty"` - ... -} - -type ContourConfigurationSpec struct { - ... - // GlobalExternalProcessor allows envoys external processing filters - // to be enabled for all virtual hosts. - // - // +optional - GlobalExternalProcessor *contour_api_v1.ExternalProcessor `json:"globalExtProc,omitempty"` - ... -} -``` An operator configures external processing on a root `HTTPProxy` by setting the `VirtualHost.ExternalProcessor` field. -Setting this field without also setting the `TLS` field is an error. ### Progressing Flow @@ -538,7 +484,7 @@ NOTE: this snippet only represents the relevant bits of the Route. }, { "match": { - "prefix": "/use-route" + "prefix": "/override" }, "route": { "cluster": "extproc-test/http-echo-service3/5678/da39a3ee5e" @@ -549,8 +495,8 @@ NOTE: this snippet only represents the relevant bits of the Route. "overrides": { "grpc_service": { "envoy_grpc": { - "authority": "extension.extproc-test.extproc-extsvc3", - "cluster_name": "extension/extproc-test/extproc-extsvc3" + "authority": "extension.extproc-test.extproc-extsvc4", + "cluster_name": "extension/extproc-test/extproc-extsvc4" }, "timeout": "30s" }, @@ -609,7 +555,13 @@ NOTE: this snippet only represents the relevant bits of the Route. ``` ## Alternatives Considered -_TBD_ +Q: Generate a name for the `VirtualHost` level's external processor instead of being provided by the user? + +A: It may involve override/disabled at `Route` level, it may be more appropriate for the user to explicitly provide the name. + +Q: Only set external processor at the `VirtualHost` level, and then toggle it at the `Route` level? + +A: Okay, but not flexible enough, becuase you must set the all processors at `VirtualHost` level, then toggle it at every routes. # Metrics _TBD_ @@ -621,8 +573,7 @@ _TBD_ - Processing services can run in separate Kubernetes namespaces with limited privilege. ## Compatibility - -HTTPProxy will opt-out to use the default global external processing explicitly and the `GlobalExtProc` in the Contour configuration is optional. This solution should not introduce any regressions or breaking changes. + This solution should not introduce any regressions or breaking changes. [1]: https://github.com/projectcontour/contour/issues/1015 From 67424c089f5f9af4f50ad6a3a8fca93762a243cb Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Tue, 12 Mar 2024 18:24:51 +0800 Subject: [PATCH 09/16] fix typo Signed-off-by: gang.liu --- design/external-processing-design.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 19af3359856..f38e852636a 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -100,7 +100,7 @@ spec: port: 5678 - conditions: - prefix: /override - extProcPolicies: # override extsvc3's grpc sevice to extproc-extsvc4 + extProcPolicies: # override extsvc3's grpc service to extproc-extsvc4 - disabled: false name: extsvc3 overrides: @@ -390,7 +390,7 @@ type ExternalProcessor struct { // ExtProcPolicy modifies how requests/responses are operated. type ExtProcPolicy struct { - // The name of the external processor being overrided. + // The name of the external processor being overridden. // +kubebuilder:validation:Required // +kubebuilder:validation:MinLength=1 Name string `json:"name"` @@ -561,7 +561,7 @@ A: It may involve override/disabled at `Route` level, it may be more appropriate Q: Only set external processor at the `VirtualHost` level, and then toggle it at the `Route` level? -A: Okay, but not flexible enough, becuase you must set the all processors at `VirtualHost` level, then toggle it at every routes. +A: Okay, but not flexible enough, because you must set the all processors at `VirtualHost` level, then toggle it at every routes. # Metrics _TBD_ From f0fe10152dae34df00b0f25a1bee9e33b0490757 Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Mon, 18 Mar 2024 15:11:24 +0800 Subject: [PATCH 10/16] update design Signed-off-by: gang.liu --- design/external-processing-design.md | 284 ++++++++++++++++----------- 1 file changed, 164 insertions(+), 120 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index f38e852636a..8a78d1aac0b 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -19,15 +19,15 @@ For a long time, the community has been advocating for the inclusion of custom H - [#5038][7] Support for External Processing Filter - [#5123][8] [Feature Discussion] The ways to extend/custom functionalities for Envoy: WASM vs. Lua vs. External Processing vs. GO filter. - ## Goals -- Support Envoy's L7 External Processing filter for HTTP Virtual Hosts. -- Allow operators to integrate existing custom HTTP message processing service(s) with Contour. +- Stick to positioning as an escape hatch. +- Support Envoy's L7 External Processing filter for HTTP(s) Virtual Hosts. +- Allow operators to integrate existing custom HTTP message processing service with Contour. - Decouple Contour from external processing, so it can evolve at an independent rate. - Integrate cleanly with the HTTPProxy API. -- Supporting >=1 External Processing Services for HTTP endpoints. -- Supporting add External processing Service(s) at different processing phases. +- Supporting set **at most one** External Processing Service for HTTP(s) endpoints. +- Supporting set **at most one** the Global External Processing Service for Virtual Hosts. ## Non Goals @@ -35,55 +35,118 @@ For a long time, the community has been advocating for the inclusion of custom H The scope of abstracting the Envoy external processing mechanism is too large to be tractable. Abstracting the protocol would also work against the goal of being able to integrate existing HTTP message processing servers. - ## High-Level Design Contour will add HTTP support for Envoy's External Processing. +new type: `ExternalProcessor` and its friends: `ExtProc`, `ExtProcOverride`, `ProcessingMode`, `HeaderMutationRules`,`GRPCService`, `ExtProcPolicy` and more, will be defined for implement the design. + +In this design, the configuration is divided into three levels: `Global`, `VirtualHost`, `Route`, each level can be set up to one External processing; each level has a `Disabled` option, but at different levels, it has different meanings. + +If the external procssing is added to the filter chain(s), it will be inserted just before the `Router` filter. + +### Global level + +At the `Global` level, there is at most one external processing configured, and if the `globalExtProc` is NOT nil, and the `processor` is set but `Disabled == false`, then it will be append to the filter chain for HTTP and the fallback chain for HTTPS, and for HTTPS it varies depending on the configuration at the `VirtualHost` level(see below). If `Disabled == true`, it will be ignored. + +```yaml +kind: ContourConfiguration +... + globalExtProc: + disabled: false # ignore processor, if it's true + processor: + grpcService: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc + namespace: extproc-test + failOpen: true + responseTimeout: 60s + mutationRules: + allowAllRouting: true + processingMode: + requestBodyMode: NONE + requestHeaderMode: SEND + requestTrailerMode: SKIP + responseBodyMode: NONE + responseHeaderMode: SEND + responseTrailerMode: SKIP +... +``` + +### VirtualHost level -new type: `ExternalProcessor` and its friends: `ExtProc`, `ExtProcOverride`, `ProcessingMode`, `HeaderMutationRules`,`GRPCService`, `ProcessingPhase`,`ExtProcPolicy`, will be defined for implement the design. +Only available at HTTPS,for the FQDN. -### Opting out from VirtualHost External Processing +#### Global Level Set and not disabled -If processing is done at the `VirtualHost` level, custom HTTP message processing would only be enabled for the specific virtual host in question. However, individual owners of HTTPProxy will have the flexibility to modify or override this setting. +- ##### extProc == nil -#### Override/Disabling External Processing + use the `Global` external processing. -##### Virtual Host level +- ##### extProc != nil + + - disabled == true + + - The `processor` is NOT set: the `Global` external processing will be disabled. + + - The `processor` is set: both the `Global` and `VirtualHost` external processing will be disabled. + + - disabled == false + + - The `processor` is NOT set: use the `Global` external processing. + + - The `processor` is set: use the `VirtualHost` external processing. -If `disabled` is set to true, the external processor will not be added to the filter chain of the HCM generated by Contour, and the name for it must be unique because it will be used as the key to find the external processor being operated on when implementing a `Route` level external processing policy. ```yaml kind: HTTPProxy ... spec: virtualhost: extProc: - processors: - - grpcService: - extensionRef: - apiVersion: projectcontour.io/v1alpha1 - name: extproc-extsvc3 - namespace: extproc-test - failOpen: true - responseTimeout: 60s - processingMode: - requestBodyMode: NONE - requestHeaderMode: SEND - requestTrailerMode: SKIP - responseBodyMode: NONE - responseHeaderMode: SEND - responseTrailerMode: SKIP - name: extproc-extsvc3 # the name must be unique - disabled: false - phase: DefaultPhase - priority: 0 + disabled: true # both the `Global` and `VirtualHost` will be disabled + processor: + grpcService: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc2 + namespace: extproc-test + failOpen: true + responseTimeout: 60s + mutationRules: + allowAllRouting: false + processingMode: + requestBodyMode: NONE + requestHeaderMode: SEND + requestTrailerMode: SKIP + responseBodyMode: NONE + responseHeaderMode: SEND + responseTrailerMode: SKIP ... ``` ##### Route level -For more precise control, the `VirtualHost` external processing can also be **overrideed/toggled** on an individual route. +For more precise control, the `Global/VirtualHost` external processing can also be **overrideed/toggled** on an individual route. + + +- ##### extProcPolicy == nil + + use the `Global` or `VirtualHost` external processing. + +- ##### extProcPolicy != nil + + - disabled == true + + - The `overrides` is NOT set: the `Global` or `VirtualHost` external processing will be disabled. + + - The `overrides` is set: the `Global` or `VirtualHost` or `Route` external processing will be disabled. + - disabled == false + + - The `overrides` is NOT set: the `Global` or `VirtualHost` external processing will be disabled. + + - The `overrides` is set: the `Global` or `VirtualHost` or `Route` external processing will be disabled. ```yaml kind: HTTPProxy @@ -92,37 +155,35 @@ spec: routes: - conditions: - prefix: /disabled - extProcPolicies: - - disabled: true # disabled for /disabled - name: extsvc3 + extProcPolicy: # disabled + disabled: true services: - name: http-echo-service port: 5678 - - conditions: + - conditions: # overrided - prefix: /override - extProcPolicies: # override extsvc3's grpc service to extproc-extsvc4 - - disabled: false - name: extsvc3 - overrides: - grpcService: - extensionRef: - apiVersion: projectcontour.io/v1alpha1 - name: extproc-extsvc4 - namespace: lg-test - failOpen: true - responseTimeout: 31s - processingMode: - requestBodyMode: NONE - requestHeaderMode: SKIP - requestTrailerMode: SKIP - responseBodyMode: NONE - responseHeaderMode: SEND - responseTrailerMode: SKIP + extProcPolicy: + disabled: false + overrides: + grpcService: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc3 + namespace: extproc-test + failOpen: true + responseTimeout: 31s + processingMode: + requestBodyMode: NONE + requestHeaderMode: SKIP + requestTrailerMode: SKIP + responseBodyMode: NONE + responseHeaderMode: SEND + responseTrailerMode: SKIP services: - name: http-echo-service2 port: 5678 - conditions: - - prefix: /noop # noop + - prefix: /inherit # use `global` or `virtualhost` services: - name: http-echo-service3 port: 5678 @@ -297,52 +358,10 @@ type GRPCService struct { FailOpen bool `json:"failOpen,omitempty"` } -// ProcessingPhase define the phase in the filter chain where the external processing filter will be injected -type ProcessingPhase string - -const ( - // DefaultPhase decides insert the external processing service at the end of the filter chain, right before the Router. - DefaultPhase ProcessingPhase = "DefaultPhase" - - // Insert before contour authentication filter(s). - AuthN ProcessingPhase = "AuthN" - - // Insert before contour authorization filter(s) and after the authentication filter(s). - AuthZ ProcessingPhase = "AuthZ" - - // Insert before contour CORS filter(s). - CORS ProcessingPhase = "CORS" - - // Insert before contour RateLimit. - RateLimit ProcessingPhase = "RateLimit" -) - // ExtProc defines the envoy External Processing filter which allows an external service to act on HTTP traffic in a flexible way // The external server must implement the v3 Envoy external processing GRPC protocol // (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). type ExtProc struct { - // Unique name for the external processor. - // +kubebuilder:validation:Required - // +kubebuilder:validation:MinLength=1 - Name string `json:"name"` - - // When true, this external processor will not be added to the listener's filter chain - // - // +optional - Disabled bool `json:"disabled,omitempty"` - - // Phase determines where in the filter chain this extProc is to be injected. - // - // +optional - Phase ProcessingPhase `json:"phase,omitempty"` - - // Priority determines ordering of processing filters in the same phase. When multiple extProc are applied to the same workload in the same phase, - // they will be applied by priority, in descending order, If priority is not set or two extProc exist with the same value, - // they will follow the order in which extProc(s) are added, Defaults to 0. - // - // +optional - Priority int32 `json:"priority,omitempty"` - // GRPCService configure the gRPC service that the filter will communicate with. // // +optional @@ -378,26 +397,30 @@ type ExtProcOverride struct { // ExternalProcessor defines a processing filter list and the policy for fine-grained at VirutalHost and/or Route level. type ExternalProcessor struct { - // Processors defines a processing filter list,and each filter in the list + // Processor defines a processing filter list,and each filter in the list // will be added to the corresponding processing Priority in ascending order of it's Priority within the same phase. // If no phase is specified, it will be added before the Router. // If no Priority is specified, the filters will be added in the order they appear in the list. // // +optional - Processors []ExtProc `json:"processors,omitempty"` + Processor *ExtProc `json:"processor,omitempty"` + // When true, this field disables the external processor: (neither global nor virtualHost) + // for the scope of the policy. + // + // if both Disabled and Processor are set. use disabled. + // + // it just work for virtualhost + // +optional + Disabled bool `json:"disabled,omitempty"` } // ExtProcPolicy modifies how requests/responses are operated. type ExtProcPolicy struct { - // The name of the external processor being overridden. - // +kubebuilder:validation:Required - // +kubebuilder:validation:MinLength=1 - Name string `json:"name"` - // When true, this field disables the specific client request external processor // for the scope of the policy. - // if both disabled and overrides are set. disabled. + // + // if both disabled and overrides are set. use disabled. // // +optional Disabled bool `json:"disabled,omitempty"` @@ -417,26 +440,52 @@ type ExtProcPolicy struct { // to be a "root". type VirtualHost struct { ... - // ExternalProcessor contains a list of external processors which allow to act on HTTP traffic in a flexible way + // ExtProc which allow to act on HTTP traffic in a flexible way // and the policy for fine-grained at VirtualHost level. // // +optional - ExternalProcessor *ExternalProcessor `json:"extProc,omitempty"` + ExtProc *ExternalProcessor `json:"extProc,omitempty"` + } // Route contains the set of routes for a virtual host. type Route struct { ... - // ExtProcPolicies updates the external processing policy/policies that were set + // ExtProcPolicy updates the external processing policy that were set // on the root HTTPProxy object for client requests/responses // // +optional - ExtProcPolicies []ExtProcPolicy `json:"extProcPolicies,omitempty"` + ExtProcPolicy *ExtProcPolicy `json:"extProcPolicy,omitempty"` + } ``` +### Contour Configuration changes + +An external processing service can be configured in the Contour config file. +This External processing configuration will be used for all HTTP & HTTPS(if not override at VirtualHost and/or Route Level) routes. + +```go +type Parameters struct { + ... + // GlobalExternalProcessor optionally holds properties of the global external processing configurations. + GlobalExternalProcessor *contour_api_v1.ExternalProcessor `yaml:"globalExtProc,omitempty"` + ... +} + +type ContourConfigurationSpec struct { + ... + // GlobalExternalProcessor allows envoys external processing filters + // to be enabled for all virtual hosts. + // + // +optional + GlobalExternalProcessor *contour_api_v1.ExternalProcessor `json:"globalExtProc,omitempty"` + ... +} +``` An operator configures external processing on a root `HTTPProxy` by setting the `VirtualHost.ExternalProcessor` field. +Setting this field without also setting the `TLS` field is an error. ### Progressing Flow @@ -484,7 +533,7 @@ NOTE: this snippet only represents the relevant bits of the Route. }, { "match": { - "prefix": "/override" + "prefix": "/use-route" }, "route": { "cluster": "extproc-test/http-echo-service3/5678/da39a3ee5e" @@ -495,8 +544,8 @@ NOTE: this snippet only represents the relevant bits of the Route. "overrides": { "grpc_service": { "envoy_grpc": { - "authority": "extension.extproc-test.extproc-extsvc4", - "cluster_name": "extension/extproc-test/extproc-extsvc4" + "authority": "extension.extproc-test.extproc-extsvc3", + "cluster_name": "extension/extproc-test/extproc-extsvc3" }, "timeout": "30s" }, @@ -555,13 +604,7 @@ NOTE: this snippet only represents the relevant bits of the Route. ``` ## Alternatives Considered -Q: Generate a name for the `VirtualHost` level's external processor instead of being provided by the user? - -A: It may involve override/disabled at `Route` level, it may be more appropriate for the user to explicitly provide the name. - -Q: Only set external processor at the `VirtualHost` level, and then toggle it at the `Route` level? - -A: Okay, but not flexible enough, because you must set the all processors at `VirtualHost` level, then toggle it at every routes. +_TBD_ # Metrics _TBD_ @@ -573,7 +616,8 @@ _TBD_ - Processing services can run in separate Kubernetes namespaces with limited privilege. ## Compatibility - This solution should not introduce any regressions or breaking changes. + +HTTPProxy will opt-out to use the default global external processing explicitly and the `GlobalExtProc` in the Contour configuration is optional. This solution should not introduce any regressions or breaking changes. [1]: https://github.com/projectcontour/contour/issues/1015 From 013487b914eeb6373d3f1f3e3a435a66102979e1 Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Mon, 18 Mar 2024 16:23:36 +0800 Subject: [PATCH 11/16] update design Signed-off-by: gang.liu --- design/external-processing-design.md | 89 ++++++++++++++++------------ 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 8a78d1aac0b..371da1550b7 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -41,13 +41,13 @@ Contour will add HTTP support for Envoy's External Processing. new type: `ExternalProcessor` and its friends: `ExtProc`, `ExtProcOverride`, `ProcessingMode`, `HeaderMutationRules`,`GRPCService`, `ExtProcPolicy` and more, will be defined for implement the design. -In this design, the configuration is divided into three levels: `Global`, `VirtualHost`, `Route`, each level can be set up to one External processing; each level has a `Disabled` option, but at different levels, it has different meanings. +In this design, the configuration is divided into three levels: `Global`, `VirtualHost`, `Route`, each level can be set up to one External processing; each level has a `disabled` option, but at different levels, it has different meanings. If the external procssing is added to the filter chain(s), it will be inserted just before the `Router` filter. ### Global level -At the `Global` level, there is at most one external processing configured, and if the `globalExtProc` is NOT nil, and the `processor` is set but `Disabled == false`, then it will be append to the filter chain for HTTP and the fallback chain for HTTPS, and for HTTPS it varies depending on the configuration at the `VirtualHost` level(see below). If `Disabled == true`, it will be ignored. +At the `Global` level, there is at most one external processing configured, and if the `globalExtProc` is NOT nil, and the `processor` is set but `disabled == false`, then it will be append to the filter chain for HTTP and the default chain for HTTPS if this VirtualHost has enabled the fallback certificate; but for the normal HTTPS it varies depending on the configuration at the `VirtualHost` level(see below). If `disabled == true`, it will be ignored. ```yaml kind: ContourConfiguration @@ -466,25 +466,67 @@ An external processing service can be configured in the Contour config file. This External processing configuration will be used for all HTTP & HTTPS(if not override at VirtualHost and/or Route Level) routes. ```go + +// The External Processing filter allows an external service to act on HTTP traffic in a flexible way +// The external server must implement the v3 Envoy +// external processing GRPC protocol (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). +type ExternalProcessor struct { + // ExtensionService identifies the extension service defining the RLS, + // formatted as /. + ExtensionService string `yaml:"extensionService,omitempty"` + + // ResponseTimeout configures maximum time to wait for a check response from the expProc server. + // Timeout durations are expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). + // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + // The string "infinity" is also a valid input and specifies no timeout. + // + // +optional + ResponseTimeout string `yaml:"responseTimeout,omitempty"` + + // If FailOpen is true, the client request is forwarded to the upstream service + // even if the authorization server fails to respond. This field should not be + // set in most cases. It is intended for use only while migrating applications + // from internal authorization to Contour external authorization. + // + // +optional + FailOpen bool `yaml:"failOpen,omitempty"` +} + +// The External Processing filter allows an external service to act on HTTP traffic in a flexible way +// The external server must implement the v3 Envoy +// external processing GRPC protocol (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). +type GlobalExternalProcessor struct { + // Processor configures the global external processing + // + // +optional + Processor *ExternalProcessor `yaml:"processor,omitempty"` + + // If Disabled is true, no external processing will be append to the filter chain. + // + // +optional + Disabled bool `yaml:"disabled,omitempty"` +} + + type Parameters struct { ... - // GlobalExternalProcessor optionally holds properties of the global external processing configurations. - GlobalExternalProcessor *contour_api_v1.ExternalProcessor `yaml:"globalExtProc,omitempty"` + // GlobalExtProc optionally holds properties of the global external processing configurations. + GlobalExtProc *GlobalExternalProcessor `yaml:"globalExtProc,omitempty"` ... } type ContourConfigurationSpec struct { ... - // GlobalExternalProcessor allows envoys external processing filters + // GlobalExtProc allows envoys external processing filter // to be enabled for all virtual hosts. - // + // // +optional - GlobalExternalProcessor *contour_api_v1.ExternalProcessor `json:"globalExtProc,omitempty"` + GlobalExtProc *contour_v1.ExternalProcessor `json:"globalExtProc,omitempty"` ... } ``` -An operator configures external processing on a root `HTTPProxy` by setting the `VirtualHost.ExternalProcessor` field. +An operator configures external processing on a root `HTTPProxy` by setting the `VirtualHost.ExtProc` field. Setting this field without also setting the `TLS` field is an error. ### Progressing Flow @@ -533,7 +575,7 @@ NOTE: this snippet only represents the relevant bits of the Route. }, { "match": { - "prefix": "/use-route" + "prefix": "/override" }, "route": { "cluster": "extproc-test/http-echo-service3/5678/da39a3ee5e" @@ -561,7 +603,7 @@ NOTE: this snippet only represents the relevant bits of the Route. }, { "match": { - "prefix": "/use-vh" + "prefix": "/inherit" }, "route": { "cluster": "extproc-test/http-echo-service2/5678/da39a3ee5e" @@ -573,31 +615,6 @@ NOTE: this snippet only represents the relevant bits of the Route. }, "version_info": "7" }, - { - "route_config": { - "@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration", - "name": "ingress_http", - "virtual_hosts": [ - { - "domains": [ - "http.projectcontour.io" - ], - "name": "http.projectcontour.io", - "routes": [ - { - "match": { - "prefix": "/use-default" - }, - "route": { - "cluster": "extproc-test/http-echo-service4/5678/da39a3ee5e" - } - } - ] - } - ] - }, - "version_info": "7" - } ] ... } @@ -619,7 +636,6 @@ _TBD_ HTTPProxy will opt-out to use the default global external processing explicitly and the `GlobalExtProc` in the Contour configuration is optional. This solution should not introduce any regressions or breaking changes. - [1]: https://github.com/projectcontour/contour/issues/1015 [2]: https://github.com/projectcontour/contour/issues/1176 [3]: https://github.com/projectcontour/contour/issues/2385 @@ -630,4 +646,3 @@ HTTPProxy will opt-out to use the default global external processing explicitly [8]: https://github.com/projectcontour/contour/issues/5123 [9]: https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto [10]: https://docs.google.com/document/d/1IZqm5IUnG9gc2VqwGaN5C2TZAD9_QbsY9Vvy5vr9Zmw/edit#heading=h.5irk4csrpu0y - From 4abfe5174921db42780753e2135f5bd9777255bb Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Mon, 18 Mar 2024 16:27:25 +0800 Subject: [PATCH 12/16] fix typo Signed-off-by: gang.liu --- design/external-processing-design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 371da1550b7..fc7a66e647f 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -160,7 +160,7 @@ spec: services: - name: http-echo-service port: 5678 - - conditions: # overrided + - conditions: # overridden - prefix: /override extProcPolicy: disabled: false From b4c1ed9f09ab1935506fae6c53119465e9a8cc1c Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Tue, 19 Mar 2024 10:37:22 +0800 Subject: [PATCH 13/16] refactor Signed-off-by: gang.liu --- design/external-processing-design.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index fc7a66e647f..7916ab614ef 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -43,7 +43,7 @@ new type: `ExternalProcessor` and its friends: `ExtProc`, `ExtProcOverride`, `Pr In this design, the configuration is divided into three levels: `Global`, `VirtualHost`, `Route`, each level can be set up to one External processing; each level has a `disabled` option, but at different levels, it has different meanings. -If the external procssing is added to the filter chain(s), it will be inserted just before the `Router` filter. +If the external processing is added to the filter chain(s), it will be inserted just before the `Router` filter. ### Global level @@ -358,6 +358,7 @@ type GRPCService struct { FailOpen bool `json:"failOpen,omitempty"` } + // ExtProc defines the envoy External Processing filter which allows an external service to act on HTTP traffic in a flexible way // The external server must implement the v3 Envoy external processing GRPC protocol // (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). @@ -376,23 +377,16 @@ type ExtProc struct { // MutationRules specifies what headers may be manipulated by a processing filter. // This set of rules makes it possible to control which modifications a filter may make. // - // +optional - MutationRules *HeaderMutationRules `json:"mutationRules,omitempty"` -} - -// ExtProcOverride override aspects of the configuration for this route. -// A set of overrides in a more specific configuration will override a “disabled” flag set in a less-specific one. -type ExtProcOverride struct { - // GRPCService configure the gRPC service that the filter will communicate with. + // for Overrides is must be nil // // +optional - GRPCService *GRPCService `json:"grpcService,omitempty"` + MutationRules *HeaderMutationRules `json:"mutationRules,omitempty"` - // ProcessingMode describes which parts of an HTTP request and response are sent to a remote server - // and how they are delivered. + // If true, the filter config processingMode can be overridden by the response message from the external processing server `mode_override``. + // If false, `mode_override` API in the response message will be ignored. // // +optional - ProcessingMode *ProcessingMode `json:"processingMode,omitempty"` + AllowModeOverride bool `json:"allowModeOverride,omitempty"` } // ExternalProcessor defines a processing filter list and the policy for fine-grained at VirutalHost and/or Route level. @@ -410,7 +404,6 @@ type ExternalProcessor struct { // // if both Disabled and Processor are set. use disabled. // - // it just work for virtualhost // +optional Disabled bool `json:"disabled,omitempty"` } @@ -428,9 +421,10 @@ type ExtProcPolicy struct { // Overrides aspects of the configuration for this route. // // +optional - Overrides *ExtProcOverride `json:"overrides,omitempty"` + Overrides *ExtProc `json:"overrides,omitempty"` } + ``` ### HTTPProxy Changes From 17de463f5565be77eb3825856bf29a8a6e5fde5b Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Thu, 21 Mar 2024 17:44:49 +0800 Subject: [PATCH 14/16] fix comments Signed-off-by: gang.liu --- design/external-processing-design.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 7916ab614ef..06b4a1f8b00 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -389,12 +389,9 @@ type ExtProc struct { AllowModeOverride bool `json:"allowModeOverride,omitempty"` } -// ExternalProcessor defines a processing filter list and the policy for fine-grained at VirutalHost and/or Route level. +// ExternalProcessor defines a external processing filter and the policy for fine-grained at VirutalHost and/or Route level. type ExternalProcessor struct { - // Processor defines a processing filter list,and each filter in the list - // will be added to the corresponding processing Priority in ascending order of it's Priority within the same phase. - // If no phase is specified, it will be added before the Router. - // If no Priority is specified, the filters will be added in the order they appear in the list. + // Processor defines a external processing filter which allows an external service to act on HTTP traffic in a flexible way. // // +optional Processor *ExtProc `json:"processor,omitempty"` From 8ba3e2b7aace0ee7651871e4b45ff921cdff34bc Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Wed, 27 Mar 2024 11:01:23 +0800 Subject: [PATCH 15/16] use mermaid chart Signed-off-by: gang.liu --- design/external-processing-design.md | 27 ++++++++++++++++++++++++--- design/images/ext_proc_flow.png | Bin 92732 -> 0 bytes 2 files changed, 24 insertions(+), 3 deletions(-) delete mode 100644 design/images/ext_proc_flow.png diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 06b4a1f8b00..154c4f5db60 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -522,9 +522,30 @@ Setting this field without also setting the `TLS` field is an error. ### Progressing Flow -This chart (copy from [External Processing Filter][10]) shows the simplest possible implementation of the filter -- a filter server receives the HTTP request headers, decides to accept the response (and can optionally modify the headers) so it closes the stream cleanly. At this point it is no longer involved in filter processing. see [External Processing Filter][10] for more information. - -![drawing](images/ext_proc_flow.png) +This chart (copy/change from [External Processing Filter][10]) shows the simplest possible implementation of the filter -- a filter server receives the HTTP request headers, decides to accept the response (and can optionally modify the headers) so it closes the stream cleanly. At this point it is no longer involved in filter processing. see [External Processing Filter][10] for more information. + +```mermaid +sequenceDiagram + participant D as Downstream + participant E as Envoy + participant F as Filter Server + Participant U as Upstream + D->>E: Request headers + E->>F: request_headers + F->>E: response_headers_response + F->>E: close stream (status = 0) + E->>U: Request headers + D->>E: Request body chunks + E->>U: Request body chunks + D-->>E: Request trailers + E-->>U: Request trailers + U->>E: Response headers + E->>D: Response headers + U->>E: Response body chunks + E->>D: Response body chunks + U-->>E: Response trailers + E-->>D: Response trailers +``` ### Sample configurations diff --git a/design/images/ext_proc_flow.png b/design/images/ext_proc_flow.png deleted file mode 100644 index 7a5b13481ad638cdaf3f167725415bb39e4f5800..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92732 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 From 3f09188faef72ff974a285f7ec5aa111609461c4 Mon Sep 17 00:00:00 2001 From: "gang.liu" Date: Tue, 2 Apr 2024 18:39:26 +0800 Subject: [PATCH 16/16] refactor Signed-off-by: gang.liu --- design/external-processing-design.md | 175 +++++++++++++-------------- 1 file changed, 83 insertions(+), 92 deletions(-) diff --git a/design/external-processing-design.md b/design/external-processing-design.md index 154c4f5db60..61ed62ba2af 100644 --- a/design/external-processing-design.md +++ b/design/external-processing-design.md @@ -39,7 +39,7 @@ For a long time, the community has been advocating for the inclusion of custom H Contour will add HTTP support for Envoy's External Processing. -new type: `ExternalProcessor` and its friends: `ExtProc`, `ExtProcOverride`, `ProcessingMode`, `HeaderMutationRules`,`GRPCService`, `ExtProcPolicy` and more, will be defined for implement the design. +new type: `ExternalProcessing` and its friends: `ExternalProcessor`, `ProcessingMode`, `HeaderMutationRules` and more, will be defined for implement the design. In this design, the configuration is divided into three levels: `Global`, `VirtualHost`, `Route`, each level can be set up to one External processing; each level has a `disabled` option, but at different levels, it has different meanings. @@ -47,21 +47,20 @@ If the external processing is added to the filter chain(s), it will be inserted ### Global level -At the `Global` level, there is at most one external processing configured, and if the `globalExtProc` is NOT nil, and the `processor` is set but `disabled == false`, then it will be append to the filter chain for HTTP and the default chain for HTTPS if this VirtualHost has enabled the fallback certificate; but for the normal HTTPS it varies depending on the configuration at the `VirtualHost` level(see below). If `disabled == true`, it will be ignored. +At the `Global` level, there is at most one external processing configured, and if the `globalExternalProcessing` is NOT nil, and the `processor` is set but `disabled == false`, then it will be append to the filter chain for HTTP and the default chain for HTTPS if this VirtualHost has enabled the fallback certificate; but for the normal HTTPS it varies depending on the configuration at the `VirtualHost` level(see below). If `disabled == true`, it will be ignored. ```yaml kind: ContourConfiguration ... - globalExtProc: + globalExternalProcessing: disabled: false # ignore processor, if it's true processor: - grpcService: - extensionRef: - apiVersion: projectcontour.io/v1alpha1 - name: extproc-extsvc - namespace: extproc-test - failOpen: true - responseTimeout: 60s + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc + namespace: extproc-test + failOpen: true + responseTimeout: 60s mutationRules: allowAllRouting: true processingMode: @@ -80,11 +79,11 @@ Only available at HTTPS,for the FQDN. #### Global Level Set and not disabled -- ##### extProc == nil +- ##### externalProcessing == nil use the `Global` external processing. -- ##### extProc != nil +- ##### externalProcessing != nil - disabled == true @@ -103,16 +102,15 @@ kind: HTTPProxy ... spec: virtualhost: - extProc: + externalProcessing: disabled: true # both the `Global` and `VirtualHost` will be disabled processor: - grpcService: - extensionRef: - apiVersion: projectcontour.io/v1alpha1 - name: extproc-extsvc2 + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc2 namespace: extproc-test - failOpen: true - responseTimeout: 60s + failOpen: true + responseTimeout: 60s mutationRules: allowAllRouting: false processingMode: @@ -130,23 +128,23 @@ spec: For more precise control, the `Global/VirtualHost` external processing can also be **overrideed/toggled** on an individual route. -- ##### extProcPolicy == nil +- ##### externalProcessing == nil use the `Global` or `VirtualHost` external processing. -- ##### extProcPolicy != nil +- ##### externalProcessing != nil - disabled == true - - The `overrides` is NOT set: the `Global` or `VirtualHost` external processing will be disabled. + - The `processor` is NOT set: the `Global` or `VirtualHost` external processing will be disabled. - - The `overrides` is set: the `Global` or `VirtualHost` or `Route` external processing will be disabled. + - The `processor` is set: the `Global` or `VirtualHost` or `Route` external processing will be disabled. - disabled == false - - The `overrides` is NOT set: the `Global` or `VirtualHost` external processing will be disabled. + - The `processor` is NOT set: the `Global` or `VirtualHost` external processing will be disabled. - - The `overrides` is set: the `Global` or `VirtualHost` or `Route` external processing will be disabled. + - The `processor` is set: the `Global` or `VirtualHost` or `Route` external processing will be disabled. ```yaml kind: HTTPProxy @@ -155,23 +153,22 @@ spec: routes: - conditions: - prefix: /disabled - extProcPolicy: # disabled + externalProcessing: # disabled disabled: true services: - name: http-echo-service port: 5678 - conditions: # overridden - prefix: /override - extProcPolicy: + externalProcessing: disabled: false - overrides: - grpcService: - extensionRef: - apiVersion: projectcontour.io/v1alpha1 - name: extproc-extsvc3 - namespace: extproc-test - failOpen: true - responseTimeout: 31s + processor: + extensionRef: + apiVersion: projectcontour.io/v1alpha1 + name: extproc-extsvc3 + namespace: extproc-test + failOpen: true + responseTimeout: 31s processingMode: requestBodyMode: NONE requestHeaderMode: SKIP @@ -191,7 +188,6 @@ spec: ``` - ## Detailed Design ### HTTPProxy Newly Added Types: @@ -334,8 +330,11 @@ type ProcessingMode struct { ResponseTrailerMode HeaderSendMode `json:"responseTrailerMode,omitempty"` } -// GRPCService configure the gRPC service that the filter will communicate with. -type GRPCService struct { + +// ExternalProcessor defines the envoy External Processing filter which allows an external service to act on HTTP traffic in a flexible way +// The external server must implement the v3 Envoy external processing GRPC protocol +// (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). +type ExternalProcessor struct { // ExtensionServiceRef specifies the extension resource that will handle the client requests. // // +optional @@ -356,17 +355,6 @@ type GRPCService struct { // // +optional FailOpen bool `json:"failOpen,omitempty"` -} - - -// ExtProc defines the envoy External Processing filter which allows an external service to act on HTTP traffic in a flexible way -// The external server must implement the v3 Envoy external processing GRPC protocol -// (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). -type ExtProc struct { - // GRPCService configure the gRPC service that the filter will communicate with. - // - // +optional - GRPCService *GRPCService `json:"grpcService,omitempty"` // ProcessingMode describes which parts of an HTTP request and response are sent to a remote server // and how they are delivered. @@ -389,15 +377,15 @@ type ExtProc struct { AllowModeOverride bool `json:"allowModeOverride,omitempty"` } -// ExternalProcessor defines a external processing filter and the policy for fine-grained at VirutalHost and/or Route level. -type ExternalProcessor struct { +// ExternalProcessing defines a external processing filter and the policy to act on HTTP traffic in a flexible way. +type ExternalProcessing struct { // Processor defines a external processing filter which allows an external service to act on HTTP traffic in a flexible way. // // +optional - Processor *ExtProc `json:"processor,omitempty"` + Processor *ExternalProcessor `json:"processor,omitempty"` - // When true, this field disables the external processor: (neither global nor virtualHost) - // for the scope of the policy. + // When true, this field disables the external processor for the scope of the policy. + // - for global: no external processing will be append to the filter chain // // if both Disabled and Processor are set. use disabled. // @@ -405,22 +393,6 @@ type ExternalProcessor struct { Disabled bool `json:"disabled,omitempty"` } -// ExtProcPolicy modifies how requests/responses are operated. -type ExtProcPolicy struct { - // When true, this field disables the specific client request external processor - // for the scope of the policy. - // - // if both disabled and overrides are set. use disabled. - // - // +optional - Disabled bool `json:"disabled,omitempty"` - - // Overrides aspects of the configuration for this route. - // - // +optional - Overrides *ExtProc `json:"overrides,omitempty"` -} - ``` @@ -431,22 +403,21 @@ type ExtProcPolicy struct { // to be a "root". type VirtualHost struct { ... - // ExtProc which allow to act on HTTP traffic in a flexible way - // and the policy for fine-grained at VirtualHost level. + // ExternalProcessing defines a external processing filter and the policy + // to act on HTTP traffic in a flexible way. // // +optional - ExtProc *ExternalProcessor `json:"extProc,omitempty"` + ExternalProcessing *ExternalProcessing `json:"externalProcessing,omitempty"` } // Route contains the set of routes for a virtual host. type Route struct { ... - // ExtProcPolicy updates the external processing policy that were set - // on the root HTTPProxy object for client requests/responses + // ExternalProcessing override/disable the policy to act on HTTP traffic for the specific route in a flexible way. // // +optional - ExtProcPolicy *ExtProcPolicy `json:"extProcPolicy,omitempty"` + ExternalProcessing *ExternalProcessing `json:"externalProcessing,omitempty"` } ``` @@ -458,41 +429,61 @@ This External processing configuration will be used for all HTTP & HTTPS(if not ```go -// The External Processing filter allows an external service to act on HTTP traffic in a flexible way -// The external server must implement the v3 Envoy -// external processing GRPC protocol (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). +// ExternalProcessor defines the envoy External Processing filter which allows an external service to act on HTTP traffic in a flexible way +// The external server must implement the v3 Envoy external processing GRPC protocol +// (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). type ExternalProcessor struct { // ExtensionService identifies the extension service defining the RLS, // formatted as /. ExtensionService string `yaml:"extensionService,omitempty"` - // ResponseTimeout configures maximum time to wait for a check response from the expProc server. + // ResponseTimeout sets how long the proxy should wait for responses. // Timeout durations are expressed in the Go [Duration format](https://godoc.org/time#ParseDuration). // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". // The string "infinity" is also a valid input and specifies no timeout. // // +optional + // +kubebuilder:validation:Pattern=`^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$` ResponseTimeout string `yaml:"responseTimeout,omitempty"` // If FailOpen is true, the client request is forwarded to the upstream service - // even if the authorization server fails to respond. This field should not be - // set in most cases. It is intended for use only while migrating applications - // from internal authorization to Contour external authorization. + // even if the server fails to respond. This field should not be + // set in most cases. // // +optional FailOpen bool `yaml:"failOpen,omitempty"` + + // ProcessingMode describes which parts of an HTTP request and response are sent to a remote server + // and how they are delivered. + // + // +optional + ProcessingMode *contour_v1.ProcessingMode `yaml:"processingMode,omitempty"` + + // MutationRules specifies what headers may be manipulated by a processing filter. + // This set of rules makes it possible to control which modifications a filter may make. + // + // for Overrides is must be nil + // + // +optional + MutationRules *contour_v1.HeaderMutationRules `yaml:"mutationRules,omitempty"` + + // If true, the filter config processingMode can be overridden by the response message from the external processing server `mode_override``. + // If false, `mode_override` API in the response message will be ignored. + // + // +optional + AllowModeOverride bool `yaml:"allowModeOverride,omitempty"` } // The External Processing filter allows an external service to act on HTTP traffic in a flexible way // The external server must implement the v3 Envoy // external processing GRPC protocol (https://www.envoyproxy.io/docs/envoy/v1.27.0/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto). -type GlobalExternalProcessor struct { +type GlobalExternalProcessing struct { // Processor configures the global external processing // // +optional Processor *ExternalProcessor `yaml:"processor,omitempty"` - // If Disabled is true, no external processing will be append to the filter chain. + // If Disabled is true, no external processing will be append to the filter chain // // +optional Disabled bool `yaml:"disabled,omitempty"` @@ -501,23 +492,23 @@ type GlobalExternalProcessor struct { type Parameters struct { ... - // GlobalExtProc optionally holds properties of the global external processing configurations. - GlobalExtProc *GlobalExternalProcessor `yaml:"globalExtProc,omitempty"` + // GlobalExternalProcessing optionally holds properties of the global external processing configurations. + GlobalExternalProcessing *GlobalExternalProcessing `yaml:"globalExternalProcessing,omitempty"` ... } type ContourConfigurationSpec struct { ... - // GlobalExtProc allows envoys external processing filter + // GlobalExternalProcessing allows envoys external processing filter // to be enabled for all virtual hosts. - // // +optional - GlobalExtProc *contour_v1.ExternalProcessor `json:"globalExtProc,omitempty"` + GlobalExternalProcessing *contour_v1.ExternalProcessing `json:"globalExternalProcessing,omitempty"` + ... } ``` -An operator configures external processing on a root `HTTPProxy` by setting the `VirtualHost.ExtProc` field. +An operator configures external processing on a root `HTTPProxy` by setting the `VirtualHost.ExternalProcessing` field. Setting this field without also setting the `TLS` field is an error. ### Progressing Flow @@ -551,7 +542,7 @@ sequenceDiagram Please refer to `examples/external-processing`. -With this proposal, contour will generate the envoy configuration snippet below for `examples/external-processing`. +With this proposal, contour will generate the envoy configuration snippet below for `examples/external-processing`. NOTE: this snippet only represents the relevant bits of the Route. ##### Envoy