diff --git a/api/v1alpha1/envoyproxy_types.go b/api/v1alpha1/envoyproxy_types.go index a8bb1505717..6fcc35e18ca 100644 --- a/api/v1alpha1/envoyproxy_types.go +++ b/api/v1alpha1/envoyproxy_types.go @@ -86,8 +86,92 @@ type EnvoyProxySpec struct { // // +optional Shutdown *ShutdownConfig `json:"shutdown,omitempty"` + + // FilterOrder defines the order of filters in the Envoy proxy's HTTP filter chain. + // If unspecified, the default filter order is applied. + // Default filter order is: + // + // - envoy.filters.http.fault + // + // - envoy.filters.http.cors + // + // - envoy.filters.http.ext_authz + // + // - envoy.filters.http.basic_authn + // + // - envoy.filters.http.oauth2 + // + // - envoy.filters.http.jwt_authn + // + // - envoy.filters.http.ext_proc + // + // - envoy.filters.http.wasm + // + // - envoy.filters.http.local_ratelimit + // + // - envoy.filters.http.ratelimit + // + // - envoy.filters.http.router + // + // +optional + // +notImplementedHide + FilterOrder []FilterPosition `json:"filterOrder,omitempty"` } +// FilterPosition defines the position of an Envoy HTTP filter in the filter chain. +// +kubebuilder:validation:XValidation:rule="(has(self.before) || has(self.after))",message="one of before or after must be specified" +// +kubebuilder:validation:XValidation:rule="(has(self.before) && !has(self.after)) || (!has(self.before) && has(self.after))",message="only one of before or after can be specified" +type FilterPosition struct { + // Name of the filter. + Name EnvoyFilter `json:"filter"` + + // Before defines the filter that should come before the filter. + // Only one of Before or After must be set. + Before *EnvoyFilter `json:"before,omitempty"` + + // After defines the filter that should come after the filter. + // Only one of Before or After must be set. + After *EnvoyFilter `json:"after,omitempty"` +} + +// EnvoyFilter defines the type of Envoy HTTP filter. +// +kubebuilder:validation:Enum=envoy.filters.http.cors;envoy.filters.http.ext_authz;envoy.filters.http.basic_authn;envoy.filters.http.oauth2;envoy.filters.http.jwt_authn;envoy.filters.http.fault;envoy.filters.http.local_ratelimit;envoy.filters.http.ratelimit;envoy.filters.http.wasm;envoy.filters.http.ext_proc +type EnvoyFilter string + +const ( + // EnvoyFilterFault defines the Envoy HTTP fault filter. + EnvoyFilterFault EnvoyFilter = "envoy.filters.http.fault" + // EnvoyFilterCORS defines the Envoy HTTP CORS filter. + EnvoyFilterCORS EnvoyFilter = "envoy.filters.http.cors" + + // EnvoyFilterExtAuthz defines the Envoy HTTP external authorization filter. + EnvoyFilterExtAuthz EnvoyFilter = "envoy.filters.http.ext_authz" + + // EnvoyFilterBasicAuthn defines the Envoy HTTP basic authentication filter. + EnvoyFilterBasicAuthn EnvoyFilter = "envoy.filters.http.basic_authn" + + // EnvoyFilterOAuth2 defines the Envoy HTTP OAuth2 filter. + EnvoyFilterOAuth2 EnvoyFilter = "envoy.filters.http.oauth2" + + // EnvoyFilterJWTAuthn defines the Envoy HTTP JWT authentication filter. + EnvoyFilterJWTAuthn EnvoyFilter = "envoy.filters.http.jwt_authn" + + // EnvoyFilterExtProc defines the Envoy HTTP external process filter. + EnvoyFilterExtProc EnvoyFilter = "envoy.filters.http.ext_proc" + + // EnvoyFilterWasm defines the Envoy HTTP WebAssembly filter. + EnvoyFilterWasm EnvoyFilter = "envoy.filters.http.wasm" + + // EnvoyFilterLocalRateLimit defines the Envoy HTTP local rate limit filter. + EnvoyFilterLocalRateLimit EnvoyFilter = "envoy.filters.http.local_ratelimit" + + // EnvoyFilterRateLimit defines the Envoy HTTP rate limit filter. + EnvoyFilterRateLimit EnvoyFilter = "envoy.filters.http.ratelimit" + + // EnvoyFilterRouter defines the Envoy HTTP router filter. + EnvoyFilterRouter EnvoyFilter = "envoy.filters.http.router" +) + type ProxyTelemetry struct { // AccessLogs defines accesslog parameters for managed proxies. // If unspecified, will send default format to stdout. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 0055d7669f1..a1a7ca77f73 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1507,6 +1507,13 @@ func (in *EnvoyProxySpec) DeepCopyInto(out *EnvoyProxySpec) { *out = new(ShutdownConfig) (*in).DeepCopyInto(*out) } + if in.FilterOrder != nil { + in, out := &in.FilterOrder, &out.FilterOrder + *out = make([]FilterPosition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvoyProxySpec. @@ -1814,6 +1821,31 @@ func (in *FileEnvoyProxyAccessLog) DeepCopy() *FileEnvoyProxyAccessLog { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FilterPosition) DeepCopyInto(out *FilterPosition) { + *out = *in + if in.Before != nil { + in, out := &in.Before, &out.Before + *out = new(EnvoyFilter) + **out = **in + } + if in.After != nil { + in, out := &in.After, &out.After + *out = new(EnvoyFilter) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FilterPosition. +func (in *FilterPosition) DeepCopy() *FilterPosition { + if in == nil { + return nil + } + out := new(FilterPosition) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GRPCExtAuthService) DeepCopyInto(out *GRPCExtAuthService) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index 76d96eccaea..beecbf18eb3 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -85,6 +85,104 @@ spec: items: type: string type: array + filterOrder: + description: |- + FilterOrder defines the order of filters in the Envoy proxy's HTTP filter chain. + If unspecified, the default filter order is applied. + Default filter order is: + + + - envoy.filters.http.fault + + + - envoy.filters.http.cors + + + - envoy.filters.http.ext_authz + + + - envoy.filters.http.basic_authn + + + - envoy.filters.http.oauth2 + + + - envoy.filters.http.jwt_authn + + + - envoy.filters.http.ext_proc + + + - envoy.filters.http.wasm + + + - envoy.filters.http.local_ratelimit + + + - envoy.filters.http.ratelimit + + + - envoy.filters.http.router + items: + description: FilterPosition defines the position of an Envoy HTTP + filter in the filter chain. + properties: + after: + description: |- + After defines the filter that should come after the filter. + Only one of Before or After must be set. + enum: + - envoy.filters.http.cors + - envoy.filters.http.ext_authz + - envoy.filters.http.basic_authn + - envoy.filters.http.oauth2 + - envoy.filters.http.jwt_authn + - envoy.filters.http.fault + - envoy.filters.http.local_ratelimit + - envoy.filters.http.ratelimit + - envoy.filters.http.wasm + - envoy.filters.http.ext_proc + type: string + before: + description: |- + Before defines the filter that should come before the filter. + Only one of Before or After must be set. + enum: + - envoy.filters.http.cors + - envoy.filters.http.ext_authz + - envoy.filters.http.basic_authn + - envoy.filters.http.oauth2 + - envoy.filters.http.jwt_authn + - envoy.filters.http.fault + - envoy.filters.http.local_ratelimit + - envoy.filters.http.ratelimit + - envoy.filters.http.wasm + - envoy.filters.http.ext_proc + type: string + filter: + description: Name of the filter. + enum: + - envoy.filters.http.cors + - envoy.filters.http.ext_authz + - envoy.filters.http.basic_authn + - envoy.filters.http.oauth2 + - envoy.filters.http.jwt_authn + - envoy.filters.http.fault + - envoy.filters.http.local_ratelimit + - envoy.filters.http.ratelimit + - envoy.filters.http.wasm + - envoy.filters.http.ext_proc + type: string + required: + - filter + type: object + x-kubernetes-validations: + - message: one of before or after must be specified + rule: (has(self.before) || has(self.after)) + - message: only one of before or after can be specified + rule: (has(self.before) && !has(self.after)) || (!has(self.before) + && has(self.after)) + type: array logging: default: level: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index a9847ac0645..526e8012502 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -648,6 +648,30 @@ _Appears in:_ | `extProc` | _[ExtProc](#extproc) array_ | true | ExtProc is an ordered list of external processing filters
that should added to the envoy filter chain | +#### EnvoyFilter + +_Underlying type:_ _string_ + +EnvoyFilter defines the type of Envoy HTTP filter. + +_Appears in:_ +- [FilterPosition](#filterposition) + +| Value | Description | +| ----- | ----------- | +| `envoy.filters.http.fault` | EnvoyFilterFault defines the Envoy HTTP fault filter.
| +| `envoy.filters.http.cors` | EnvoyFilterCORS defines the Envoy HTTP CORS filter.
| +| `envoy.filters.http.ext_authz` | EnvoyFilterExtAuthz defines the Envoy HTTP external authorization filter.
| +| `envoy.filters.http.basic_authn` | EnvoyFilterBasicAuthn defines the Envoy HTTP basic authentication filter.
| +| `envoy.filters.http.oauth2` | EnvoyFilterOAuth2 defines the Envoy HTTP OAuth2 filter.
| +| `envoy.filters.http.jwt_authn` | EnvoyFilterJWTAuthn defines the Envoy HTTP JWT authentication filter.
| +| `envoy.filters.http.ext_proc` | EnvoyFilterExtProc defines the Envoy HTTP external process filter.
| +| `envoy.filters.http.wasm` | EnvoyFilterWasm defines the Envoy HTTP WebAssembly filter.
| +| `envoy.filters.http.local_ratelimit` | EnvoyFilterLocalRateLimit defines the Envoy HTTP local rate limit filter.
| +| `envoy.filters.http.ratelimit` | EnvoyFilterRateLimit defines the Envoy HTTP rate limit filter.
| +| `envoy.filters.http.router` | EnvoyFilterRouter defines the Envoy HTTP router filter.
| + + #### EnvoyGateway @@ -1302,6 +1326,22 @@ _Appears in:_ | `path` | _string_ | true | Path defines the file path used to expose envoy access log(e.g. /dev/stdout). | +#### FilterPosition + + + +FilterPosition defines the position of an Envoy HTTP filter in the filter chain. + +_Appears in:_ +- [EnvoyProxySpec](#envoyproxyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `filter` | _[EnvoyFilter](#envoyfilter)_ | true | Name of the filter. | +| `before` | _[EnvoyFilter](#envoyfilter)_ | true | Before defines the filter that should come before the filter.
Only one of Before or After must be set. | +| `after` | _[EnvoyFilter](#envoyfilter)_ | true | After defines the filter that should come after the filter.
Only one of Before or After must be set. | + + #### GRPCExtAuthService diff --git a/test/cel-validation/envoyproxy_test.go b/test/cel-validation/envoyproxy_test.go index 3b2785f713d..e6d88ff2c77 100644 --- a/test/cel-validation/envoyproxy_test.go +++ b/test/cel-validation/envoyproxy_test.go @@ -1219,6 +1219,62 @@ func TestEnvoyProxyProvider(t *testing.T) { } }, }, + { + desc: "ProxyFilterOrder-with-before-and-after", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + FilterOrder: []egv1a1.FilterPosition{ + { + Name: egv1a1.EnvoyFilterRateLimit, + Before: ptr.To(egv1a1.EnvoyFilterCORS), + After: ptr.To(egv1a1.EnvoyFilterBasicAuthn), + }, + }, + } + }, + wantErrors: []string{"only one of before or after can be specified"}, + }, + { + desc: "ProxyFilterOrder-without-before-or-after", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + FilterOrder: []egv1a1.FilterPosition{ + { + Name: egv1a1.EnvoyFilterRateLimit, + }, + }, + } + }, + wantErrors: []string{"one of before or after must be specified"}, + }, + { + desc: "ProxyFilterOrder-with-before", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + FilterOrder: []egv1a1.FilterPosition{ + { + Name: egv1a1.EnvoyFilterRateLimit, + Before: ptr.To(egv1a1.EnvoyFilterCORS), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "ProxyFilterOrder-with-after", + mutate: func(envoy *egv1a1.EnvoyProxy) { + envoy.Spec = egv1a1.EnvoyProxySpec{ + FilterOrder: []egv1a1.FilterPosition{ + { + Name: egv1a1.EnvoyFilterRateLimit, + After: ptr.To(egv1a1.EnvoyFilterBasicAuthn), + }, + }, + } + }, + wantErrors: []string{}, + }, } for _, tc := range cases { @@ -1237,7 +1293,7 @@ func TestEnvoyProxyProvider(t *testing.T) { } if (len(tc.wantErrors) != 0) != (err != nil) { - t.Fatalf("Unexpected response while creating EnvoyProxy; got err=\n%v\n;want error=%v", err, tc.wantErrors != nil) + t.Fatalf("Unexpected response while creating EnvoyProxy; got err=\n%v\n;want error=%v", err, tc.wantErrors) } var missingErrorStrings []string