diff --git a/apis/projectcontour/v1/httpproxy.go b/apis/projectcontour/v1/httpproxy.go index 8e407a02b7c..e0ca3f53b46 100644 --- a/apis/projectcontour/v1/httpproxy.go +++ b/apis/projectcontour/v1/httpproxy.go @@ -239,15 +239,39 @@ type ExtensionServiceReference struct { Name string `json:"name,omitempty" protobuf:"bytes,3,opt,name=name"` } +// AuthorizationServiceType indicates the protocol +// implemented by the external authorization server. +type AuthorizationServiceType string + +const ( + AuthorizationGRPCService AuthorizationServiceType = "grpc" + AuthorizationHTTPService AuthorizationServiceType = "http" +) + // AuthorizationServer configures an external server to authenticate // client requests. The external server must implement the v3 Envoy -// external authorization GRPC protocol (https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto). +// external authorization GRPC protocol (https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto) +// or the HTTP authorization server protocol. +// +kubebuilder:validation:XValidation:message="httpSettings can only be set when serviceType is 'http'",rule="!has(self.httpSettings) || self.serviceType == 'http'" type AuthorizationServer struct { // ExtensionServiceRef specifies the extension resource that will authorize client requests. // // +optional ExtensionServiceRef ExtensionServiceReference `json:"extensionRef,omitempty"` + // ServiceType sets the protocol used to communicate with + // the external authorization server. + // + // +optional + // +kubebuilder:validation:Enum=http;grpc + // +kubebuilder:default=grpc + ServiceType AuthorizationServiceType `json:"serviceType,omitempty"` + + // HTTPAuthorizationServerSettings defines configurations for interacting with an external HTTP authorization server. + // + // +optional + HTTPServerSettings *HTTPAuthorizationServerSettings `json:"httpSettings,omitempty"` + // AuthPolicy sets a default authorization policy for client requests. // This policy will be used unless overridden by individual routes. // @@ -276,6 +300,64 @@ type AuthorizationServer struct { WithRequestBody *AuthorizationServerBufferSettings `json:"withRequestBody,omitempty"` } +// HTTPAuthorizationServerSettings defines configurations for interacting with an external HTTP authorization server. +type HTTPAuthorizationServerSettings struct { + // PathPrefix Sets a prefix to the value of authorization request header Path. + // + // +optional + PathPrefix string `json:"pathPrefix,omitempty"` + + // AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + // Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + // + // +optional + AllowedAuthorizationHeaders []HTTPAuthorizationServerAllowedHeaders `json:"allowedAuthorizationHeaders,omitempty"` + + // AllowedUpstreamHeaders specifies response headers from the authorization server + // that may be added to the original client request before sending it to the upstream. + // + // +optional + AllowedUpstreamHeaders []HTTPAuthorizationServerAllowedHeaders `json:"allowedUpstreamHeaders,omitempty"` +} + +// HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers +// in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user +// experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. +// +kubebuilder:validation:XValidation:message="only one of prefix, suffix, exact, and contains should be set in the allowedHeader",rule="(has(self.exact) ? 1 : 0) + (has(self.prefix) ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) ? 1 : 0) == 1" +type HTTPAuthorizationServerAllowedHeaders struct { + // Exact specifies a string that the header name must be equal to. + // + // +optional + Exact string `json:"exact,omitempty"` + + // Prefix defines a prefix match for the header name. + // + // +optional + Prefix string `json:"prefix,omitempty"` + + // Suffix defines a suffix match for a header name. + // + // +optional + Suffix string `json:"suffix,omitempty"` + + // To streamline user experience and mitigate potential issues, we do not support regex. + // Additionally, it's essential to ensure that any regex patterns adhere to the configured runtime key, re2.max_program_size.error_level + // by verifying that the program size is smaller than the specified value. + // This necessitates thorough validation of user input. + // + // Regex string `json:"regex,omitempty"` + + // Contains specifies a substring that must be present in the header name. + // + // +optional + Contains string `json:"contains,omitempty"` + + // IgnoreCase specifies whether string matching should be case-insensitive. + // + // +optional + IgnoreCase bool `json:"ignoreCase,omitempty"` +} + // AuthorizationServerBufferSettings enables ExtAuthz filter to buffer client request data and send it as part of authorization request type AuthorizationServerBufferSettings struct { // MaxRequestBytes sets the maximum size of message body ExtAuthz filter will hold in-memory. diff --git a/apis/projectcontour/v1/zz_generated.deepcopy.go b/apis/projectcontour/v1/zz_generated.deepcopy.go index 30cd9f1cbea..67a09799161 100644 --- a/apis/projectcontour/v1/zz_generated.deepcopy.go +++ b/apis/projectcontour/v1/zz_generated.deepcopy.go @@ -50,6 +50,11 @@ func (in *AuthorizationPolicy) DeepCopy() *AuthorizationPolicy { func (in *AuthorizationServer) DeepCopyInto(out *AuthorizationServer) { *out = *in out.ExtensionServiceRef = in.ExtensionServiceRef + if in.HTTPServerSettings != nil { + in, out := &in.HTTPServerSettings, &out.HTTPServerSettings + *out = new(HTTPAuthorizationServerSettings) + (*in).DeepCopyInto(*out) + } if in.AuthPolicy != nil { in, out := &in.AuthPolicy, &out.AuthPolicy *out = new(AuthorizationPolicy) @@ -324,6 +329,46 @@ func (in *GlobalRateLimitPolicy) DeepCopy() *GlobalRateLimitPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPAuthorizationServerAllowedHeaders) DeepCopyInto(out *HTTPAuthorizationServerAllowedHeaders) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPAuthorizationServerAllowedHeaders. +func (in *HTTPAuthorizationServerAllowedHeaders) DeepCopy() *HTTPAuthorizationServerAllowedHeaders { + if in == nil { + return nil + } + out := new(HTTPAuthorizationServerAllowedHeaders) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPAuthorizationServerSettings) DeepCopyInto(out *HTTPAuthorizationServerSettings) { + *out = *in + if in.AllowedAuthorizationHeaders != nil { + in, out := &in.AllowedAuthorizationHeaders, &out.AllowedAuthorizationHeaders + *out = make([]HTTPAuthorizationServerAllowedHeaders, len(*in)) + copy(*out, *in) + } + if in.AllowedUpstreamHeaders != nil { + in, out := &in.AllowedUpstreamHeaders, &out.AllowedUpstreamHeaders + *out = make([]HTTPAuthorizationServerAllowedHeaders, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPAuthorizationServerSettings. +func (in *HTTPAuthorizationServerSettings) DeepCopy() *HTTPAuthorizationServerSettings { + if in == nil { + return nil + } + out := new(HTTPAuthorizationServerSettings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPDirectResponsePolicy) DeepCopyInto(out *HTTPDirectResponsePolicy) { *out = *in diff --git a/apis/projectcontour/v1alpha1/extensionservice.go b/apis/projectcontour/v1alpha1/extensionservice.go index c074de546e7..c936f98c786 100644 --- a/apis/projectcontour/v1alpha1/extensionservice.go +++ b/apis/projectcontour/v1alpha1/extensionservice.go @@ -62,7 +62,7 @@ type ExtensionServiceTarget struct { // ExtensionServiceSpec defines the desired state of an ExtensionService resource. type ExtensionServiceSpec struct { // Services specifies the set of Kubernetes Service resources that - // receive GRPC extension API requests. + // receive extension API requests. // If no weights are specified for any of the entries in // this array, traffic will be spread evenly across all the // services. @@ -78,15 +78,15 @@ type ExtensionServiceSpec struct { UpstreamValidation *contour_v1.UpstreamValidation `json:"validation,omitempty"` // Protocol may be used to specify (or override) the protocol used to reach this Service. - // Values may be h2 or h2c. If omitted, protocol-selection falls back on Service annotations. + // Values may be h2, h2c or http/1.1. If omitted, protocol-selection falls back on Service annotations. // // +optional - // +kubebuilder:validation:Enum=h2;h2c + // +kubebuilder:validation:Enum=http/1.1;h2;h2c Protocol *string `json:"protocol,omitempty"` - // The policy for load balancing GRPC service requests. Note that the + // The policy for load balancing service requests. Note that the // `Cookie` and `RequestHash` load balancing strategies cannot be used - // here. + // here for GRPC service requests. // // +optional LoadBalancerPolicy *contour_v1.LoadBalancerPolicy `json:"loadBalancerPolicy,omitempty"` diff --git a/changelogs/unreleased/7418-therealak12-minor.md b/changelogs/unreleased/7418-therealak12-minor.md new file mode 100644 index 00000000000..8b33df50489 --- /dev/null +++ b/changelogs/unreleased/7418-therealak12-minor.md @@ -0,0 +1,5 @@ +## Contour now supports HTTP external authorization services + +With this change, Contour supports HTTP external authorization services in addition to gRPC. +This expands compatibility with a broader range of authorization providers and +allows operators to choose the protocol that best fits their environment. diff --git a/cmd/contour/serve.go b/cmd/contour/serve.go index 16a00bdd283..1e449f0a9c1 100644 --- a/cmd/contour/serve.go +++ b/cmd/contour/serve.go @@ -830,20 +830,17 @@ func (s *Server) setupGlobalExternalAuthentication(contourConfiguration contour_ context = contourConfiguration.GlobalExternalAuthorization.AuthPolicy.Context } - globalExternalAuthConfig := &xdscache_v3.GlobalExternalAuthConfig{ - ExtensionServiceConfig: extensionSvcConfig, - FailOpen: contourConfiguration.GlobalExternalAuthorization.FailOpen, - Context: context, + var validCond contour_v1.DetailedCondition + extAuth := dag.NewExternalAuthorization(contourConfiguration.GlobalExternalAuthorization, &validCond) + if len(validCond.Errors) > 0 { + return nil, fmt.Errorf("%s", validCond.Errors[0].Message) } - if contourConfiguration.GlobalExternalAuthorization.WithRequestBody != nil { - globalExternalAuthConfig.WithRequestBody = &dag.AuthorizationServerBufferSettings{ - PackAsBytes: contourConfiguration.GlobalExternalAuthorization.WithRequestBody.PackAsBytes, - AllowPartialMessage: contourConfiguration.GlobalExternalAuthorization.WithRequestBody.AllowPartialMessage, - MaxRequestBytes: contourConfiguration.GlobalExternalAuthorization.WithRequestBody.MaxRequestBytes, - } - } - return globalExternalAuthConfig, nil + return &xdscache_v3.GlobalExternalAuthConfig{ + ExtensionServiceConfig: extensionSvcConfig, + ExternalAuthorization: *extAuth, + Context: context, + }, nil } func (s *Server) setupDebugService(debugConfig contour_v1alpha1.DebugConfig, builder *dag.Builder) error { diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go index 8c4ee27d73b..d4f09ce5b4a 100644 --- a/cmd/contour/servecontext.go +++ b/cmd/contour/servecontext.go @@ -457,10 +457,15 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_v1alpha1.Co Name: nsedName.Name, Namespace: nsedName.Namespace, }, + ServiceType: ctx.Config.GlobalExternalAuthorization.ServiceType, ResponseTimeout: ctx.Config.GlobalExternalAuthorization.ResponseTimeout, FailOpen: ctx.Config.GlobalExternalAuthorization.FailOpen, } + if ctx.Config.GlobalExternalAuthorization.HTTPServerSettings != nil { + globalExtAuth.HTTPServerSettings = ctx.Config.GlobalExternalAuthorization.HTTPServerSettings + } + if ctx.Config.GlobalExternalAuthorization.AuthPolicy != nil { globalExtAuth.AuthPolicy = &contour_v1.AuthorizationPolicy{ Disabled: ctx.Config.GlobalExternalAuthorization.AuthPolicy.Disabled, diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index bd251103c8b..c4c8f39fc9b 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -727,6 +727,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of authorization + request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -735,6 +820,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -756,6 +850,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -4675,6 +4772,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -4683,6 +4865,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -4704,6 +4895,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -5298,9 +5492,9 @@ spec: type: object loadBalancerPolicy: description: |- - The policy for load balancing GRPC service requests. Note that the + The policy for load balancing service requests. Note that the `Cookie` and `RequestHash` load balancing strategies cannot be used - here. + here for GRPC service requests. properties: requestHashPolicies: description: |- @@ -5374,8 +5568,9 @@ spec: protocol: description: |- Protocol may be used to specify (or override) the protocol used to reach this Service. - Values may be h2 or h2c. If omitted, protocol-selection falls back on Service annotations. + Values may be h2, h2c or http/1.1. If omitted, protocol-selection falls back on Service annotations. enum: + - http/1.1 - h2 - h2c type: string @@ -5391,7 +5586,7 @@ spec: services: description: |- Services specifies the set of Kubernetes Service resources that - receive GRPC extension API requests. + receive extension API requests. If no weights are specified for any of the entries in this array, traffic will be spread evenly across all the services. @@ -7650,6 +7845,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -7658,6 +7938,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -7679,6 +7968,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' corsPolicy: description: Specifies the cross-origin policy to apply to the VirtualHost. diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index d89221430ae..264be05fbe3 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -946,6 +946,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of authorization + request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -954,6 +1039,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -975,6 +1069,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -4894,6 +4991,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -4902,6 +5084,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -4923,6 +5114,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -5517,9 +5711,9 @@ spec: type: object loadBalancerPolicy: description: |- - The policy for load balancing GRPC service requests. Note that the + The policy for load balancing service requests. Note that the `Cookie` and `RequestHash` load balancing strategies cannot be used - here. + here for GRPC service requests. properties: requestHashPolicies: description: |- @@ -5593,8 +5787,9 @@ spec: protocol: description: |- Protocol may be used to specify (or override) the protocol used to reach this Service. - Values may be h2 or h2c. If omitted, protocol-selection falls back on Service annotations. + Values may be h2, h2c or http/1.1. If omitted, protocol-selection falls back on Service annotations. enum: + - http/1.1 - h2 - h2c type: string @@ -5610,7 +5805,7 @@ spec: services: description: |- Services specifies the set of Kubernetes Service resources that - receive GRPC extension API requests. + receive extension API requests. If no weights are specified for any of the entries in this array, traffic will be spread evenly across all the services. @@ -7869,6 +8064,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -7877,6 +8157,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -7898,6 +8187,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' corsPolicy: description: Specifies the cross-origin policy to apply to the VirtualHost. diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index 2496dac11ac..3a175d8ca03 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -738,6 +738,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of authorization + request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -746,6 +831,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -767,6 +861,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -4686,6 +4783,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -4694,6 +4876,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -4715,6 +4906,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -5309,9 +5503,9 @@ spec: type: object loadBalancerPolicy: description: |- - The policy for load balancing GRPC service requests. Note that the + The policy for load balancing service requests. Note that the `Cookie` and `RequestHash` load balancing strategies cannot be used - here. + here for GRPC service requests. properties: requestHashPolicies: description: |- @@ -5385,8 +5579,9 @@ spec: protocol: description: |- Protocol may be used to specify (or override) the protocol used to reach this Service. - Values may be h2 or h2c. If omitted, protocol-selection falls back on Service annotations. + Values may be h2, h2c or http/1.1. If omitted, protocol-selection falls back on Service annotations. enum: + - http/1.1 - h2 - h2c type: string @@ -5402,7 +5597,7 @@ spec: services: description: |- Services specifies the set of Kubernetes Service resources that - receive GRPC extension API requests. + receive extension API requests. If no weights are specified for any of the entries in this array, traffic will be spread evenly across all the services. @@ -7661,6 +7856,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -7669,6 +7949,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -7690,6 +7979,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' corsPolicy: description: Specifies the cross-origin policy to apply to the VirtualHost. diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index 8c125a49e2c..77142750f4c 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -763,6 +763,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of authorization + request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -771,6 +856,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -792,6 +886,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -4711,6 +4808,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -4719,6 +4901,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -4740,6 +4931,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -5334,9 +5528,9 @@ spec: type: object loadBalancerPolicy: description: |- - The policy for load balancing GRPC service requests. Note that the + The policy for load balancing service requests. Note that the `Cookie` and `RequestHash` load balancing strategies cannot be used - here. + here for GRPC service requests. properties: requestHashPolicies: description: |- @@ -5410,8 +5604,9 @@ spec: protocol: description: |- Protocol may be used to specify (or override) the protocol used to reach this Service. - Values may be h2 or h2c. If omitted, protocol-selection falls back on Service annotations. + Values may be h2, h2c or http/1.1. If omitted, protocol-selection falls back on Service annotations. enum: + - http/1.1 - h2 - h2c type: string @@ -5427,7 +5622,7 @@ spec: services: description: |- Services specifies the set of Kubernetes Service resources that - receive GRPC extension API requests. + receive extension API requests. If no weights are specified for any of the entries in this array, traffic will be spread evenly across all the services. @@ -7686,6 +7881,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -7694,6 +7974,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -7715,6 +8004,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' corsPolicy: description: Specifies the cross-origin policy to apply to the VirtualHost. diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index 837cf752cef..60521a03de8 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -946,6 +946,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that must + be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string matching + should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the header + name. + type: string + suffix: + description: Suffix defines a suffix match for a header + name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) ? + 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of authorization + request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -954,6 +1039,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -975,6 +1069,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -4894,6 +4991,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -4902,6 +5084,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -4923,6 +5114,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' health: description: |- Health defines the endpoints Contour uses to serve health checks. @@ -5517,9 +5711,9 @@ spec: type: object loadBalancerPolicy: description: |- - The policy for load balancing GRPC service requests. Note that the + The policy for load balancing service requests. Note that the `Cookie` and `RequestHash` load balancing strategies cannot be used - here. + here for GRPC service requests. properties: requestHashPolicies: description: |- @@ -5593,8 +5787,9 @@ spec: protocol: description: |- Protocol may be used to specify (or override) the protocol used to reach this Service. - Values may be h2 or h2c. If omitted, protocol-selection falls back on Service annotations. + Values may be h2, h2c or http/1.1. If omitted, protocol-selection falls back on Service annotations. enum: + - http/1.1 - h2 - h2c type: string @@ -5610,7 +5805,7 @@ spec: services: description: |- Services specifies the set of Kubernetes Service resources that - receive GRPC extension API requests. + receive extension API requests. If no weights are specified for any of the entries in this array, traffic will be spread evenly across all the services. @@ -7869,6 +8064,91 @@ spec: set in most cases. It is intended for use only while migrating applications from internal authorization to Contour external authorization. type: boolean + httpSettings: + description: HTTPAuthorizationServerSettings defines configurations + for interacting with an external HTTP authorization server. + properties: + allowedAuthorizationHeaders: + description: |- + AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. + Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + allowedUpstreamHeaders: + description: |- + AllowedUpstreamHeaders specifies response headers from the authorization server + that may be added to the original client request before sending it to the upstream. + items: + description: |- + HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers + in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user + experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided. + properties: + contains: + description: Contains specifies a substring that + must be present in the header name. + type: string + exact: + description: Exact specifies a string that the header + name must be equal to. + type: string + ignoreCase: + description: IgnoreCase specifies whether string + matching should be case-insensitive. + type: boolean + prefix: + description: Prefix defines a prefix match for the + header name. + type: string + suffix: + description: Suffix defines a suffix match for a + header name. + type: string + type: object + x-kubernetes-validations: + - message: only one of prefix, suffix, exact, and contains + should be set in the allowedHeader + rule: '(has(self.exact) ? 1 : 0) + (has(self.prefix) + ? 1 : 0) + (has(self.suffix) ? 1 : 0) + (has(self.contains) + ? 1 : 0) == 1' + type: array + pathPrefix: + description: PathPrefix Sets a prefix to the value of + authorization request header Path. + type: string + type: object responseTimeout: description: |- ResponseTimeout configures maximum time to wait for a check response from the authorization server. @@ -7877,6 +8157,15 @@ spec: The string "infinity" is also a valid input and specifies no timeout. pattern: ^(((\d*(\.\d*)?h)|(\d*(\.\d*)?m)|(\d*(\.\d*)?s)|(\d*(\.\d*)?ms)|(\d*(\.\d*)?us)|(\d*(\.\d*)?µs)|(\d*(\.\d*)?ns))+|infinity|infinite)$ type: string + serviceType: + default: grpc + description: |- + ServiceType sets the protocol used to communicate with + the external authorization server. + enum: + - http + - grpc + type: string withRequestBody: description: WithRequestBody specifies configuration for sending the client request's body to authorization server. @@ -7898,6 +8187,9 @@ spec: type: boolean type: object type: object + x-kubernetes-validations: + - message: httpSettings can only be set when serviceType is 'http' + rule: '!has(self.httpSettings) || self.serviceType == ''http''' corsPolicy: description: Specifies the cross-origin policy to apply to the VirtualHost. diff --git a/internal/dag/conditions.go b/internal/dag/conditions.go index 8b4f542ce9c..5fa207f0866 100644 --- a/internal/dag/conditions.go +++ b/internal/dag/conditions.go @@ -407,6 +407,44 @@ func queryParameterMatchConditionsValid(conditions []contour_v1.MatchCondition) return nil } +// ExternalAuthAllowedHeadersValid validates that the allowed header conditions within a +// slice of HTTPAuthorizationServerAllowedHeaders are valid. Specifically, it returns an error for +// any of the following scenarios: +// - no conditions are set +// - more than one condition is set in the same allowed header condition branch +// - invalid regular expression is specified for the Regex condition +func ExternalAuthAllowedHeadersValid(allowedHeaders []contour_v1.HTTPAuthorizationServerAllowedHeaders) error { + for _, allowedHeader := range allowedHeaders { + sum := 0 + + if allowedHeader.Exact != "" { + sum++ + } + + if allowedHeader.Prefix != "" { + sum++ + } + + if allowedHeader.Suffix != "" { + sum++ + } + + if allowedHeader.Contains != "" { + sum++ + } + + if sum == 0 { + return errors.New("one of prefix, suffix, exact or contains is required for each allowedHeader") + } + + if sum > 1 { + return errors.New("only one of prefix, suffix, exact, and contains should be set in the allowedHeader") + } + } + + return nil +} + // ValidateRegex returns an error if the supplied // RE2 regex syntax is invalid. func ValidateRegex(regex string) error { diff --git a/internal/dag/dag.go b/internal/dag/dag.go index 0e75755abfd..5b1fed32f93 100644 --- a/internal/dag/dag.go +++ b/internal/dag/dag.go @@ -162,6 +162,35 @@ func (hc *HeaderMatchCondition) String() string { return "header: " + details } +// AuthorizationServiceType defines whether the external authorization server +// uses HTTP or gRPC protocol. +type AuthorizationServiceType int + +const ( + // AuthorizationServiceGRPC indicates the server implements the gRPC ext_authz protocol. + AuthorizationServiceGRPC AuthorizationServiceType = iota + // AuthorizationServiceHTTP indicates the server implements the raw HTTP ext_authz protocol. + AuthorizationServiceHTTP +) + +const ( + // HeaderNameMatchTypeExact matches a header name exactly. + HeaderNameMatchTypeExact = "exact" + // HeaderNameMatchTypePrefix matches a header name by prefix. + HeaderNameMatchTypePrefix = "prefix" + // HeaderNameMatchTypeSuffix matches a header name by suffix. + HeaderNameMatchTypeSuffix = "suffix" + // HeaderNameMatchTypeContains matches a header name if it contains the provided value. + HeaderNameMatchTypeContains = "contains" +) + +// HeaderNameMatchCondition matches an HTTP header name by MatchType. +type HeaderNameMatchCondition struct { + MatchType string + Value string + IgnoreCase bool +} + const ( // QueryParamMatchTypeExact matches a querystring parameter value exactly. QueryParamMatchTypeExact = "exact" @@ -861,6 +890,28 @@ type IPFilterRule struct { // ExternalAuthorization contains the configuration for enabling // the ExtAuthz filter. type ExternalAuthorization struct { + // ServiceAPIType defines the external authorization service API type. + // It indicates the protocol implemented by the external server, specifying whether it's a raw HTTP authorization server + // or a gRPC authorization server. + ServiceAPIType AuthorizationServiceType + + // Note that in addition to the user’s supplied matchers, Host, Method, Path, Content-Length, and Authorization are additionally included in the list. + HTTPAllowedAuthorizationHeaders []HeaderNameMatchCondition + + // HTTPAllowedUpstreamHeaders specifies authorization response headers that will be added to the original client request. + // Note that coexistent headers will be overridden. + HTTPAllowedUpstreamHeaders []HeaderNameMatchCondition + + // HTTPPathPrefix Sets a prefix to the value of authorization request header Path. + HTTPPathPrefix string + + // Note: This field is not used by Envoy + // https://github.com/envoyproxy/envoy/issues/5357 + // + // HttpServerURI sets the URI of the external HTTP authorization server to which authorization requests must be sent. + // Only required for http services. + // HttpServerURI string + // AuthorizationService points to the extension that client // requests are forwarded to for authorization. If nil, no // authorization is enabled for this host. diff --git a/internal/dag/httpproxy_processor.go b/internal/dag/httpproxy_processor.go index 8a61bc5f132..2d9c95f0248 100644 --- a/internal/dag/httpproxy_processor.go +++ b/internal/dag/httpproxy_processor.go @@ -1392,29 +1392,18 @@ func (p *HTTPProxyProcessor) computeVirtualHostAuthorization(auth *contour_v1.Au return nil } - ok, respTimeout := determineExternalAuthTimeout(auth.ResponseTimeout, validCond, ext) - if !ok { + extAuth := NewExternalAuthorization(auth, validCond) + if extAuth == nil { return nil } - globalExternalAuthorization := &ExternalAuthorization{ - AuthorizationService: ext, - AuthorizationFailOpen: auth.FailOpen, - AuthorizationResponseTimeout: *respTimeout, + // If no explicit timeout was configured, fall back to the extension service's own timeout. + if extAuth.AuthorizationResponseTimeout.UseDefault() { + extAuth.AuthorizationResponseTimeout = ext.RouteTimeoutPolicy.ResponseTimeout } - if auth.WithRequestBody != nil { - maxRequestBytes := defaultMaxRequestBytes - if auth.WithRequestBody.MaxRequestBytes != 0 { - maxRequestBytes = auth.WithRequestBody.MaxRequestBytes - } - globalExternalAuthorization.AuthorizationServerWithRequestBody = &AuthorizationServerBufferSettings{ - MaxRequestBytes: maxRequestBytes, - AllowPartialMessage: auth.WithRequestBody.AllowPartialMessage, - PackAsBytes: auth.WithRequestBody.PackAsBytes, - } - } - return globalExternalAuthorization + extAuth.AuthorizationService = ext + return extAuth } func validateExternalAuthExtensionService(ref contour_v1.ExtensionServiceReference, validCond *contour_v1.DetailedCondition, httpproxy *contour_v1.HTTPProxy, getExtensionCluster func(name string) *ExtensionCluster) (bool, *ExtensionCluster) { diff --git a/internal/envoy/v3/cluster.go b/internal/envoy/v3/cluster.go index 4717b966f8b..c6b876a32b5 100644 --- a/internal/envoy/v3/cluster.go +++ b/internal/envoy/v3/cluster.go @@ -162,10 +162,10 @@ func (e *EnvoyGen) ExtensionCluster(ext *dag.ExtensionCluster) *envoy_config_clu // TODO(jpeach): Externalname service support in https://github.com/projectcontour/contour/issues/2875 - http2Version := HTTPVersionAuto + httpVersion := HTTPVersionAuto switch ext.Protocol { case "h2": - http2Version = HTTPVersion2 + httpVersion = HTTPVersion2 cluster.TransportSocket = UpstreamTLSTransportSocket( e.UpstreamTLSContext( ext.UpstreamValidation, @@ -176,13 +176,15 @@ func (e *EnvoyGen) ExtensionCluster(ext *dag.ExtensionCluster) *envoy_config_clu ), ) case "h2c": - http2Version = HTTPVersion2 + httpVersion = HTTPVersion2 + case "http/1.1": + httpVersion = HTTPVersion1 } if ext.ClusterTimeoutPolicy.ConnectTimeout > time.Duration(0) { cluster.ConnectTimeout = durationpb.New(ext.ClusterTimeoutPolicy.ConnectTimeout) } - cluster.TypedExtensionProtocolOptions = protocolOptions(http2Version, ext.ClusterTimeoutPolicy.IdleConnectionTimeout, nil) + cluster.TypedExtensionProtocolOptions = protocolOptions(httpVersion, ext.ClusterTimeoutPolicy.IdleConnectionTimeout, nil) applyCircuitBreakers(cluster, ext.CircuitBreakers) diff --git a/internal/envoy/v3/listener.go b/internal/envoy/v3/listener.go index 51a8a53bebe..ca3e83b0d83 100644 --- a/internal/envoy/v3/listener.go +++ b/internal/envoy/v3/listener.go @@ -41,6 +41,7 @@ import ( envoy_filter_network_http_connection_manager_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" envoy_filter_network_tcp_proxy_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" envoy_transport_socket_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" "github.com/envoyproxy/go-control-plane/pkg/wellknown" "google.golang.org/protobuf/proto" @@ -64,7 +65,7 @@ const ( HTTPVersion3 HTTPVersionType = envoy_filter_network_http_connection_manager_v3.HttpConnectionManager_HTTP3 ) -// ProtoNamesForVersions returns the slice of ALPN protocol names for the give HTTP versions. +// ProtoNamesForVersions returns the slice of ALPN protocol names for the given HTTP versions. func ProtoNamesForVersions(versions ...HTTPVersionType) []string { protocols := map[HTTPVersionType]string{ HTTPVersion1: "http/1.1", @@ -837,13 +838,50 @@ end } } +// ExternalAuthzAllowedHeaders returns the slice of StringMatcher for a given slice of HeaderNameMatchCondition. +func ExternalAuthzAllowedHeaders(allowedHeaders []dag.HeaderNameMatchCondition) []*envoy_matcher_v3.StringMatcher { + var allowedHeaderPatterns []*envoy_matcher_v3.StringMatcher + + for _, allowedHeader := range allowedHeaders { + switch allowedHeader.MatchType { + case dag.HeaderNameMatchTypeExact: + allowedHeaderPatterns = append(allowedHeaderPatterns, &envoy_matcher_v3.StringMatcher{ + MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{ + Exact: allowedHeader.Value, + }, + IgnoreCase: allowedHeader.IgnoreCase, + }) + case dag.HeaderNameMatchTypePrefix: + allowedHeaderPatterns = append(allowedHeaderPatterns, &envoy_matcher_v3.StringMatcher{ + MatchPattern: &envoy_matcher_v3.StringMatcher_Prefix{ + Prefix: allowedHeader.Value, + }, + IgnoreCase: allowedHeader.IgnoreCase, + }) + case dag.HeaderNameMatchTypeSuffix: + allowedHeaderPatterns = append(allowedHeaderPatterns, &envoy_matcher_v3.StringMatcher{ + MatchPattern: &envoy_matcher_v3.StringMatcher_Suffix{ + Suffix: allowedHeader.Value, + }, + IgnoreCase: allowedHeader.IgnoreCase, + }) + case dag.HeaderNameMatchTypeContains: + allowedHeaderPatterns = append(allowedHeaderPatterns, &envoy_matcher_v3.StringMatcher{ + MatchPattern: &envoy_matcher_v3.StringMatcher_Contains{ + Contains: allowedHeader.Value, + }, + IgnoreCase: allowedHeader.IgnoreCase, + }) + } + } + + return allowedHeaderPatterns +} + // FilterExternalAuthz returns an `ext_authz` filter configured with the // requested parameters. func FilterExternalAuthz(externalAuthorization *dag.ExternalAuthorization) *envoy_filter_network_http_connection_manager_v3.HttpFilter { authConfig := envoy_filter_http_ext_authz_v3.ExtAuthz{ - Services: &envoy_filter_http_ext_authz_v3.ExtAuthz_GrpcService{ - GrpcService: grpcService(externalAuthorization.AuthorizationService.Name, externalAuthorization.AuthorizationService.SNI, externalAuthorization.AuthorizationResponseTimeout), - }, // Pretty sure we always want this. Why have an // external auth service if it is not going to affect // routing decisions? @@ -852,11 +890,53 @@ func FilterExternalAuthz(externalAuthorization *dag.ExternalAuthorization) *envo StatusOnError: &envoy_type_v3.HttpStatus{ Code: envoy_type_v3.StatusCode_Forbidden, }, - MetadataContextNamespaces: []string{}, - IncludePeerCertificate: true, // TODO(jpeach): When we move to the Envoy v4 API, propagate the // `transport_api_version` from ExtensionServiceSpec ProtocolVersion. - TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + IncludePeerCertificate: true, + } + + switch externalAuthorization.ServiceAPIType { + case dag.AuthorizationServiceGRPC: + authConfig.Services = &envoy_filter_http_ext_authz_v3.ExtAuthz_GrpcService{ + GrpcService: grpcService(externalAuthorization.AuthorizationService.Name, externalAuthorization.AuthorizationService.SNI, externalAuthorization.AuthorizationResponseTimeout), + } + authConfig.MetadataContextNamespaces = []string{} + + case dag.AuthorizationServiceHTTP: + extAuthzService := &envoy_filter_http_ext_authz_v3.ExtAuthz_HttpService{ + HttpService: &envoy_filter_http_ext_authz_v3.HttpService{ + ServerUri: &envoy_config_core_v3.HttpUri{ + // Uri is required by the Envoy API but routing is determined by the Cluster field, + // so we use a dummy value here. + Uri: "http://dummy/", + HttpUpstreamType: &envoy_config_core_v3.HttpUri_Cluster{ + Cluster: externalAuthorization.AuthorizationService.Name, + }, + Timeout: httpURITimeout(externalAuthorization.AuthorizationResponseTimeout), + }, + }, + } + + if pathPrefix := externalAuthorization.HTTPPathPrefix; pathPrefix != "" { + extAuthzService.HttpService.PathPrefix = pathPrefix + } + + if len(externalAuthorization.HTTPAllowedAuthorizationHeaders) > 0 { + authConfig.AllowedHeaders = &envoy_matcher_v3.ListStringMatcher{ + Patterns: ExternalAuthzAllowedHeaders(externalAuthorization.HTTPAllowedAuthorizationHeaders), + } + } + + if len(externalAuthorization.HTTPAllowedUpstreamHeaders) > 0 { + extAuthzService.HttpService.AuthorizationResponse = &envoy_filter_http_ext_authz_v3.AuthorizationResponse{ + AllowedUpstreamHeaders: &envoy_matcher_v3.ListStringMatcher{ + Patterns: ExternalAuthzAllowedHeaders(externalAuthorization.HTTPAllowedUpstreamHeaders), + }, + } + } + + authConfig.Services = extAuthzService } if externalAuthorization.AuthorizationServerWithRequestBody != nil { @@ -975,6 +1055,15 @@ func FilterChainTLSFallback(downstream *envoy_transport_socket_tls_v3.Downstream return fc } +// httpURITimeout returns a duration for the HttpUri.Timeout field. +// It returns 0 (infinite) if the timeout is not set. +func httpURITimeout(d timeout.Setting) *durationpb.Duration { + if t := envoy.Timeout(d); t != nil { + return t + } + return durationpb.New(0) +} + // grpcService returns a envoy_config_core_v3.GrpcService for the given parameters. func grpcService(clusterName, sni string, timeout timeout.Setting) *envoy_config_core_v3.GrpcService { authority := strings.ReplaceAll(clusterName, "/", ".") diff --git a/internal/featuretests/v3/authorization_test.go b/internal/featuretests/v3/authorization_test.go index efbb9ae21e0..4c98a3b121c 100644 --- a/internal/featuretests/v3/authorization_test.go +++ b/internal/featuretests/v3/authorization_test.go @@ -24,6 +24,7 @@ import ( envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_filter_http_ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3" envoy_service_discovery_v3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" + envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" "google.golang.org/protobuf/types/known/durationpb" core_v1 "k8s.io/api/core/v1" @@ -51,6 +52,20 @@ func grpcCluster(name string) *envoy_filter_http_ext_authz_v3.ExtAuthz_GrpcServi } } +func httpCluster(name string) *envoy_filter_http_ext_authz_v3.ExtAuthz_HttpService { + return &envoy_filter_http_ext_authz_v3.ExtAuthz_HttpService{ + HttpService: &envoy_filter_http_ext_authz_v3.HttpService{ + ServerUri: &envoy_config_core_v3.HttpUri{ + Uri: "http://dummy/", + HttpUpstreamType: &envoy_config_core_v3.HttpUri_Cluster{ + Cluster: name, + }, + Timeout: durationpb.New(defaultResponseTimeout), + }, + }, + } +} + func authzResponseTimeout(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { const fqdn = "failopen.projectcontour.io" @@ -62,6 +77,7 @@ func authzResponseTimeout(t *testing.T, rh ResourceEventHandlerWrapper, c *Conto Namespace: "auth", Name: "extension", }, + ServiceType: contour_v1.AuthorizationGRPCService, ResponseTimeout: "10m", }). WithSpec(contour_v1.HTTPProxySpec{ @@ -120,6 +136,7 @@ func authzInvalidResponseTimeout(t *testing.T, rh ResourceEventHandlerWrapper, c Namespace: "auth", Name: "extension", }, + ServiceType: contour_v1.AuthorizationGRPCService, ResponseTimeout: "invalid-timeout", }). WithSpec(contour_v1.HTTPProxySpec{ @@ -147,7 +164,8 @@ func authzFailOpen(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { Namespace: "auth", Name: "extension", }, - FailOpen: true, + ServiceType: contour_v1.AuthorizationGRPCService, + FailOpen: true, }). WithSpec(contour_v1.HTTPProxySpec{ Routes: []contour_v1.Route{{ @@ -199,6 +217,7 @@ func authzFallbackIncompat(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont Namespace: "auth", Name: "extension", }, + ServiceType: contour_v1.AuthorizationGRPCService, }). WithSpec(contour_v1.HTTPProxySpec{ Routes: []contour_v1.Route{{ @@ -230,6 +249,7 @@ func authzOverrideDisabled(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont WithCertificate("certificate"). WithAuthServer(contour_v1.AuthorizationServer{ ExtensionServiceRef: extensionRef, + ServiceType: contour_v1.AuthorizationGRPCService, AuthPolicy: &contour_v1.AuthorizationPolicy{Disabled: false}, }). WithSpec(contour_v1.HTTPProxySpec{ @@ -340,6 +360,7 @@ func authzMergeRouteContext(t *testing.T, rh ResourceEventHandlerWrapper, c *Con Namespace: "auth", Name: "extension", }, + ServiceType: contour_v1.AuthorizationGRPCService, AuthPolicy: &contour_v1.AuthorizationPolicy{ Context: map[string]string{ "root-element": "root", @@ -419,7 +440,9 @@ func authzInvalidReference(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont invalid := fixture.NewProxy("proxy"). WithFQDN(fqdn). WithCertificate("certificate"). - WithAuthServer(contour_v1.AuthorizationServer{}). + WithAuthServer(contour_v1.AuthorizationServer{ + ServiceType: contour_v1.AuthorizationGRPCService, + }). WithSpec(contour_v1.HTTPProxySpec{ Routes: []contour_v1.Route{{ Services: []contour_v1.Service{{ @@ -509,7 +532,8 @@ func authzWithRequestBodyBufferSettings(t *testing.T, rh ResourceEventHandlerWra Namespace: "auth", Name: "extension", }, - FailOpen: true, + ServiceType: contour_v1.AuthorizationGRPCService, + FailOpen: true, WithRequestBody: &contour_v1.AuthorizationServerBufferSettings{ MaxRequestBytes: 100, AllowPartialMessage: true, @@ -562,16 +586,509 @@ func authzWithRequestBodyBufferSettings(t *testing.T, rh ResourceEventHandlerWra }).Status(p).IsValid() } +func AuthzTypeGRPC(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + const fqdn = "typegrpc.projectcontour.io" + + p := fixture.NewProxy("proxy"). + WithFQDN(fqdn). + WithCertificate("certificate"). + WithAuthServer(contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Namespace: "auth", + Name: "extension", + }, + ServiceType: contour_v1.AuthorizationGRPCService, + }). + WithSpec(contour_v1.HTTPProxySpec{ + Routes: []contour_v1.Route{{ + Services: []contour_v1.Service{{ + Name: "app-server", + Port: 80, + }}, + }}, + }) + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + defaultHTTPListener(), + &envoy_config_listener_v3.Listener{ + Name: "ingress_https", + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{ + filterchaintls(fqdn, + featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), + authzFilterFor( + fqdn, + &envoy_filter_http_ext_authz_v3.ExtAuthz{ + Services: grpcCluster("extension/auth/extension"), + ClearRouteCache: true, + FailureModeAllow: false, + IncludePeerCertificate: true, + StatusOnError: &envoy_type_v3.HttpStatus{ + Code: envoy_type_v3.StatusCode_Forbidden, + }, + TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + }, + ), + nil, "h2", "http/1.1"), + }, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, + statsListener()), + }).Status(p).IsValid() +} + +func authzTypeHTTP(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + const fqdn = "typehttp.projectcontour.io" + + p := fixture.NewProxy("proxy"). + WithFQDN(fqdn). + WithCertificate("certificate"). + WithAuthServer(contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Namespace: "auth", + Name: "extension", + }, + ServiceType: contour_v1.AuthorizationHTTPService, + }). + WithSpec(contour_v1.HTTPProxySpec{ + Routes: []contour_v1.Route{{ + Services: []contour_v1.Service{{ + Name: "app-server", + Port: 80, + }}, + }}, + }) + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + defaultHTTPListener(), + &envoy_config_listener_v3.Listener{ + Name: "ingress_https", + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{ + filterchaintls(fqdn, + featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), + authzFilterFor( + fqdn, + &envoy_filter_http_ext_authz_v3.ExtAuthz{ + Services: httpCluster("extension/auth/extension"), + ClearRouteCache: true, + FailureModeAllow: false, + IncludePeerCertificate: true, + StatusOnError: &envoy_type_v3.HttpStatus{ + Code: envoy_type_v3.StatusCode_Forbidden, + }, + TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + }, + ), + nil, "h2", "http/1.1"), + }, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, + statsListener()), + }).Status(p).IsValid() +} + +func authzTypeHTTPWithPathPrefix(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + const fqdn = "typehttp.projectcontour.io" + + p := fixture.NewProxy("proxy"). + WithFQDN(fqdn). + WithCertificate("certificate"). + WithAuthServer(contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Namespace: "auth", + Name: "extension", + }, + ServiceType: contour_v1.AuthorizationHTTPService, + HTTPServerSettings: &contour_v1.HTTPAuthorizationServerSettings{ + PathPrefix: "/auth?", + }, + }). + WithSpec(contour_v1.HTTPProxySpec{ + Routes: []contour_v1.Route{{ + Services: []contour_v1.Service{{ + Name: "app-server", + Port: 80, + }}, + }}, + }) + + rh.OnDelete(p) + rh.OnAdd(p) + + cluster := httpCluster("extension/auth/extension") + cluster.HttpService.PathPrefix = "/auth?" + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + defaultHTTPListener(), + &envoy_config_listener_v3.Listener{ + Name: "ingress_https", + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{ + filterchaintls(fqdn, + featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), + authzFilterFor( + fqdn, + &envoy_filter_http_ext_authz_v3.ExtAuthz{ + Services: cluster, + ClearRouteCache: true, + FailureModeAllow: false, + IncludePeerCertificate: true, + StatusOnError: &envoy_type_v3.HttpStatus{ + Code: envoy_type_v3.StatusCode_Forbidden, + }, + TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + }, + ), + nil, "h2", "http/1.1"), + }, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, + statsListener()), + }).Status(p).IsValid() +} + +func authzTypeHTTPWithAllowedAuthorizationHeaders(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + const fqdn = "typehttp.projectcontour.io" + + p := fixture.NewProxy("proxy"). + WithFQDN(fqdn). + WithCertificate("certificate"). + WithAuthServer(contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Namespace: "auth", + Name: "extension", + }, + ServiceType: contour_v1.AuthorizationHTTPService, + }). + WithSpec(contour_v1.HTTPProxySpec{ + Routes: []contour_v1.Route{{ + Services: []contour_v1.Service{{ + Name: "app-server", + Port: 80, + }}, + }}, + }) + + p.Spec.VirtualHost.Authorization.HTTPServerSettings = &contour_v1.HTTPAuthorizationServerSettings{ + AllowedAuthorizationHeaders: []contour_v1.HTTPAuthorizationServerAllowedHeaders{ + {IgnoreCase: false}, + }, + } + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, statsListener()), + }).Status(p).HasError(contour_v1.ConditionTypeAuthError, "AuthBadAllowedHeader", `Spec.Virtualhost.Authorization.HTTPServerSettings.AllowedAuthorizationHeaders is invalid: one of prefix, suffix, exact or contains is required for each allowedHeader`) + + p.Spec.VirtualHost.Authorization.HTTPServerSettings = &contour_v1.HTTPAuthorizationServerSettings{ + AllowedAuthorizationHeaders: []contour_v1.HTTPAuthorizationServerAllowedHeaders{ + {Exact: "test", Prefix: "test", IgnoreCase: false}, + }, + } + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, statsListener()), + }).Status(p).HasError(contour_v1.ConditionTypeAuthError, "AuthBadAllowedHeader", `Spec.Virtualhost.Authorization.HTTPServerSettings.AllowedAuthorizationHeaders is invalid: only one of prefix, suffix, exact, and contains should be set in the allowedHeader`) + + p.Spec.VirtualHost.Authorization.HTTPServerSettings = &contour_v1.HTTPAuthorizationServerSettings{ + AllowedAuthorizationHeaders: []contour_v1.HTTPAuthorizationServerAllowedHeaders{ + {Prefix: "test1", IgnoreCase: false}, + {Exact: "test2", IgnoreCase: true}, + }, + } + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + defaultHTTPListener(), + &envoy_config_listener_v3.Listener{ + Name: "ingress_https", + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{ + filterchaintls(fqdn, + featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), + authzFilterFor( + fqdn, + &envoy_filter_http_ext_authz_v3.ExtAuthz{ + Services: httpCluster("extension/auth/extension"), + AllowedHeaders: &envoy_matcher_v3.ListStringMatcher{ + Patterns: []*envoy_matcher_v3.StringMatcher{ + {MatchPattern: &envoy_matcher_v3.StringMatcher_Prefix{Prefix: "test1"}, IgnoreCase: false}, + {MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{Exact: "test2"}, IgnoreCase: true}, + }, + }, + ClearRouteCache: true, + FailureModeAllow: false, + IncludePeerCertificate: true, + StatusOnError: &envoy_type_v3.HttpStatus{ + Code: envoy_type_v3.StatusCode_Forbidden, + }, + TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + }, + ), + nil, "h2", "http/1.1"), + }, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, + statsListener()), + }).Status(p).IsValid() +} + +func authzTypeHTTPWithAllowedUpstreamHeaders(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + const fqdn = "typehttp.projectcontour.io" + + p := fixture.NewProxy("proxy"). + WithFQDN(fqdn). + WithCertificate("certificate"). + WithAuthServer(contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Namespace: "auth", + Name: "extension", + }, + ServiceType: contour_v1.AuthorizationHTTPService, + }). + WithSpec(contour_v1.HTTPProxySpec{ + Routes: []contour_v1.Route{{ + Services: []contour_v1.Service{{ + Name: "app-server", + Port: 80, + }}, + }}, + }) + + p.Spec.VirtualHost.Authorization.HTTPServerSettings = &contour_v1.HTTPAuthorizationServerSettings{ + AllowedUpstreamHeaders: []contour_v1.HTTPAuthorizationServerAllowedHeaders{ + {IgnoreCase: false}, + }, + } + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, statsListener()), + }).Status(p).HasError(contour_v1.ConditionTypeAuthError, "AuthBadAllowedHeader", `Spec.Virtualhost.Authorization.HTTPServerSettings.AllowedUpstreamHeaders is invalid: one of prefix, suffix, exact or contains is required for each allowedHeader`) + + p.Spec.VirtualHost.Authorization.HTTPServerSettings = &contour_v1.HTTPAuthorizationServerSettings{ + AllowedUpstreamHeaders: []contour_v1.HTTPAuthorizationServerAllowedHeaders{ + {Exact: "test", Prefix: "test", IgnoreCase: false}, + }, + } + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, statsListener()), + }).Status(p).HasError(contour_v1.ConditionTypeAuthError, "AuthBadAllowedHeader", `Spec.Virtualhost.Authorization.HTTPServerSettings.AllowedUpstreamHeaders is invalid: only one of prefix, suffix, exact, and contains should be set in the allowedHeader`) + + p.Spec.VirtualHost.Authorization.HTTPServerSettings = &contour_v1.HTTPAuthorizationServerSettings{ + AllowedUpstreamHeaders: []contour_v1.HTTPAuthorizationServerAllowedHeaders{ + {Prefix: "test1", IgnoreCase: false}, + {Exact: "test2", IgnoreCase: true}, + }, + } + + rh.OnDelete(p) + rh.OnAdd(p) + + cluster := httpCluster("extension/auth/extension") + cluster.HttpService.AuthorizationResponse = &envoy_filter_http_ext_authz_v3.AuthorizationResponse{ + AllowedUpstreamHeaders: &envoy_matcher_v3.ListStringMatcher{ + Patterns: []*envoy_matcher_v3.StringMatcher{ + {MatchPattern: &envoy_matcher_v3.StringMatcher_Prefix{Prefix: "test1"}, IgnoreCase: false}, + {MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{Exact: "test2"}, IgnoreCase: true}, + }, + }, + } + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + defaultHTTPListener(), + &envoy_config_listener_v3.Listener{ + Name: "ingress_https", + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{ + filterchaintls(fqdn, + featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), + authzFilterFor( + fqdn, + &envoy_filter_http_ext_authz_v3.ExtAuthz{ + Services: cluster, + ClearRouteCache: true, + FailureModeAllow: false, + IncludePeerCertificate: true, + StatusOnError: &envoy_type_v3.HttpStatus{ + Code: envoy_type_v3.StatusCode_Forbidden, + }, + TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + }, + ), + nil, "h2", "http/1.1"), + }, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, + statsListener()), + }).Status(p).IsValid() +} + +func authzTypeHTTPWithContext(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + const fqdn = "typehttp.projectcontour.io" + + p := fixture.NewProxy("proxy"). + WithFQDN(fqdn). + WithCertificate("certificate"). + WithAuthServer(contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Namespace: "auth", + Name: "extension", + }, + ServiceType: contour_v1.AuthorizationHTTPService, + AuthPolicy: &contour_v1.AuthorizationPolicy{ + Context: map[string]string{ + "k1": "v1", + "k2": "v2", + }, + }, + }). + WithSpec(contour_v1.HTTPProxySpec{ + Routes: []contour_v1.Route{{ + Services: []contour_v1.Service{{ + Name: "app-server", + Port: 80, + }}, + }}, + }) + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, statsListener()), + }).Status(p).HasError(contour_v1.ConditionTypeAuthError, "AuthContextForHTTP", `Spec.Virtualhost.Authorization.AuthPolicy.Context are only applied to grpc service type`) +} + +// authzTypeUnset tests backwards compatibility: an HTTPProxy with no +// serviceAPIType set (field absent in older stored objects, Go zero-value "") +// must be treated as gRPC. +func authzTypeUnset(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + const fqdn = "typegrpc.projectcontour.io" + + p := fixture.NewProxy("proxy"). + WithFQDN(fqdn). + WithCertificate("certificate"). + WithAuthServer(contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Namespace: "auth", + Name: "extension", + }, + // ServiceAPIType intentionally absent to simulate upgrade from older version where this field didn't exist. + }). + WithSpec(contour_v1.HTTPProxySpec{ + Routes: []contour_v1.Route{{ + Services: []contour_v1.Service{{ + Name: "app-server", + Port: 80, + }}, + }}, + }) + + rh.OnDelete(p) + rh.OnAdd(p) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + defaultHTTPListener(), + &envoy_config_listener_v3.Listener{ + Name: "ingress_https", + Address: envoy_v3.SocketAddress("0.0.0.0", 8443), + ListenerFilters: envoy_v3.ListenerFilters( + envoy_v3.TLSInspector(), + ), + FilterChains: []*envoy_config_listener_v3.FilterChain{ + filterchaintls(fqdn, + featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), + authzFilterFor( + fqdn, + &envoy_filter_http_ext_authz_v3.ExtAuthz{ + Services: grpcCluster("extension/auth/extension"), + ClearRouteCache: true, + FailureModeAllow: false, + IncludePeerCertificate: true, + StatusOnError: &envoy_type_v3.HttpStatus{ + Code: envoy_type_v3.StatusCode_Forbidden, + }, + TransportApiVersion: envoy_config_core_v3.ApiVersion_V3, + }, + ), + nil, "h2", "http/1.1"), + }, + SocketOptions: envoy_v3.NewSocketOptions().TCPKeepalive().Build(), + }, + statsListener()), + }).Status(p).IsValid() +} + func TestAuthorization(t *testing.T) { subtests := map[string]func(*testing.T, ResourceEventHandlerWrapper, *Contour){ - "MissingExtension": authzInvalidReference, - "MergeRouteContext": authzMergeRouteContext, - "OverrideDisabled": authzOverrideDisabled, - "FallbackIncompat": authzFallbackIncompat, - "FailOpen": authzFailOpen, - "ResponseTimeout": authzResponseTimeout, - "InvalidResponseTimeout": authzInvalidResponseTimeout, - "AuthzWithRequestBodyBufferSettings": authzWithRequestBodyBufferSettings, + "MissingExtension": authzInvalidReference, + "MergeRouteContext": authzMergeRouteContext, + "OverrideDisabled": authzOverrideDisabled, + "FallbackIncompat": authzFallbackIncompat, + "FailOpen": authzFailOpen, + "ResponseTimeout": authzResponseTimeout, + "InvalidResponseTimeout": authzInvalidResponseTimeout, + "AuthzWithRequestBodyBufferSettings": authzWithRequestBodyBufferSettings, + "AuthzTypeGRPC": AuthzTypeGRPC, + "AuthzTypeHTTP": authzTypeHTTP, + "AuthzTypeHTTPWithPathPrefix": authzTypeHTTPWithPathPrefix, + "AuthzTypeHTTPWithAllowedAuthorizationHeaders": authzTypeHTTPWithAllowedAuthorizationHeaders, + "AuthzTypeHTTPWithAllowedUpstreamHeaders": authzTypeHTTPWithAllowedUpstreamHeaders, + "AuthzTypeHTTPWithContext": authzTypeHTTPWithContext, + "AuthzTypeUnset": authzTypeUnset, } for n, f := range subtests { diff --git a/internal/featuretests/v3/envoy.go b/internal/featuretests/v3/envoy.go index e9b8a5995e1..d434381f923 100644 --- a/internal/featuretests/v3/envoy.go +++ b/internal/featuretests/v3/envoy.go @@ -249,6 +249,20 @@ func h2cCluster(c *envoy_config_cluster_v3.Cluster) *envoy_config_cluster_v3.Clu return c } +func http1Cluster(c *envoy_config_cluster_v3.Cluster) *envoy_config_cluster_v3.Cluster { + c.TypedExtensionProtocolOptions = map[string]*anypb.Any{ + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": protobuf.MustMarshalAny( + &envoy_upstream_http_v3.HttpProtocolOptions{ + UpstreamProtocolOptions: &envoy_upstream_http_v3.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &envoy_upstream_http_v3.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &envoy_upstream_http_v3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{}, + }, + }, + }), + } + return c +} + func withConnectionTimeout(c *envoy_config_cluster_v3.Cluster, timeout time.Duration, httpVersion envoy_v3.HTTPVersionType) *envoy_config_cluster_v3.Cluster { var config *envoy_upstream_http_v3.HttpProtocolOptions_ExplicitHttpConfig diff --git a/internal/featuretests/v3/extensionservice_test.go b/internal/featuretests/v3/extensionservice_test.go index a82f1fd5726..94e3f4be64c 100644 --- a/internal/featuretests/v3/extensionservice_test.go +++ b/internal/featuretests/v3/extensionservice_test.go @@ -78,7 +78,7 @@ func extBasic(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { } func extCleartext(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { - rh.OnAdd(&contour_v1alpha1.ExtensionService{ + es := &contour_v1alpha1.ExtensionService{ ObjectMeta: fixture.ObjectMeta("ns/ext"), Spec: contour_v1alpha1.ExtensionServiceSpec{ Protocol: ptr.To("h2c"), @@ -87,7 +87,9 @@ func extCleartext(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { {Name: "svc2", Port: 8082}, }, }, - }) + } + + rh.OnAdd(es) c.Request(clusterType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ TypeUrl: clusterType, @@ -97,6 +99,20 @@ func extCleartext(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { ), ), }) + + es.Spec.Protocol = ptr.To("http/1.1") + + rh.OnDelete(es) + rh.OnAdd(es) + + c.Request(clusterType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: clusterType, + Resources: resources(t, + DefaultCluster( + http1Cluster(cluster("extension/ns/ext", "extension/ns/ext", "extension_ns_ext")), + ), + ), + }) } func extUpstreamValidation(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { @@ -313,7 +329,7 @@ func extInvalidTimeout(_ *testing.T, rh ResourceEventHandlerWrapper, c *Contour) } func extInconsistentProto(_ *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { - rh.OnAdd(&contour_v1alpha1.ExtensionService{ + es := &contour_v1alpha1.ExtensionService{ ObjectMeta: fixture.ObjectMeta("ns/ext"), Spec: contour_v1alpha1.ExtensionServiceSpec{ Services: []contour_v1alpha1.ExtensionServiceTarget{ @@ -325,8 +341,20 @@ func extInconsistentProto(_ *testing.T, rh ResourceEventHandlerWrapper, c *Conto SubjectName: "ext.projectcontour.io", }, }, + } + + rh.OnAdd(es) + + // Should have no clusters because Protocol and UpstreamValidation is inconsistent. + c.Request(clusterType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: clusterType, }) + es.Spec.Protocol = ptr.To("h1") + + rh.OnDelete(es) + rh.OnAdd(es) + // Should have no clusters because Protocol and UpstreamValidation is inconsistent. c.Request(clusterType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ TypeUrl: clusterType, diff --git a/internal/featuretests/v3/global_authorization_test.go b/internal/featuretests/v3/global_authorization_test.go index c2b35f58cae..1b1f6a16789 100644 --- a/internal/featuretests/v3/global_authorization_test.go +++ b/internal/featuretests/v3/global_authorization_test.go @@ -47,6 +47,7 @@ var ( Name: "extension", Namespace: "auth", }, + ServiceType: contour_v1.AuthorizationGRPCService, FailOpen: false, ResponseTimeout: defaultResponseTimeout.String(), AuthPolicy: &contour_v1.AuthorizationPolicy{ @@ -62,6 +63,7 @@ var ( Name: "extension", Namespace: "auth", }, + ServiceType: contour_v1.AuthorizationGRPCService, FailOpen: false, ResponseTimeout: defaultResponseTimeout.String(), AuthPolicy: &contour_v1.AuthorizationPolicy{ @@ -580,6 +582,7 @@ func globalExternalAuthorizationWithTLSAuthOverride(t *testing.T, rh ResourceEve Namespace: "auth", Name: "extension", }, + ServiceType: contour_v1.AuthorizationGRPCService, ResponseTimeout: defaultResponseTimeout.String(), FailOpen: true, WithRequestBody: &contour_v1.AuthorizationServerBufferSettings{ @@ -800,12 +803,13 @@ func TestGlobalAuthorization(t *testing.T) { ExtensionService: k8s.NamespacedNameFrom("auth/extension"), Timeout: timeout.DurationSetting(defaultResponseTimeout), }, - FailOpen: false, Context: map[string]string{ "header_type": "root_config", "header_1": "message_1", }, } + cfg.GlobalExternalAuthConfig.ServiceAPIType = dag.AuthorizationServiceGRPC + cfg.GlobalExternalAuthConfig.AuthorizationFailOpen = false }, func(b *dag.Builder) { for _, processor := range b.Processors { diff --git a/internal/xdscache/v3/listener.go b/internal/xdscache/v3/listener.go index a0e62004d80..b1cf42294c3 100644 --- a/internal/xdscache/v3/listener.go +++ b/internal/xdscache/v3/listener.go @@ -202,9 +202,8 @@ type RateLimitConfig struct { type GlobalExternalAuthConfig struct { ExtensionServiceConfig - FailOpen bool - Context map[string]string - WithRequestBody *dag.AuthorizationServerBufferSettings + dag.ExternalAuthorization + Context map[string]string } // httpAccessLog returns the access log for the HTTP (non TLS) @@ -604,9 +603,13 @@ func httpGlobalExternalAuthConfig(config *GlobalExternalAuthConfig) *envoy_filte Name: dag.ExtensionClusterName(config.ExtensionService), SNI: config.SNI, }, - AuthorizationFailOpen: config.FailOpen, + ServiceAPIType: config.ServiceAPIType, + HTTPAllowedAuthorizationHeaders: config.HTTPAllowedAuthorizationHeaders, + HTTPAllowedUpstreamHeaders: config.HTTPAllowedUpstreamHeaders, + HTTPPathPrefix: config.HTTPPathPrefix, + AuthorizationFailOpen: config.AuthorizationFailOpen, AuthorizationResponseTimeout: config.Timeout, - AuthorizationServerWithRequestBody: config.WithRequestBody, + AuthorizationServerWithRequestBody: config.AuthorizationServerWithRequestBody, }) } diff --git a/pkg/config/parameters.go b/pkg/config/parameters.go index 1e4c397d513..645b33cf053 100644 --- a/pkg/config/parameters.go +++ b/pkg/config/parameters.go @@ -761,6 +761,16 @@ type GlobalExternalAuthorization struct { // ExtensionService identifies the extension service defining the RLS, // formatted as /. ExtensionService string `yaml:"extensionService,omitempty"` + // ServiceType defines the external authorization service API type. + // It indicates the protocol implemented by the external server, specifying whether it's a raw HTTP authorization server + // or a gRPC authorization server. + // + // +optional + ServiceType contour_v1.AuthorizationServiceType `yaml:"serviceType,omitempty"` + // HttpAuthorizationServerSettings defines configurations for interacting with an external HTTP authorization server. + // + // +optional + HTTPServerSettings *contour_v1.HTTPAuthorizationServerSettings `yaml:"httpSettings,omitempty"` // AuthPolicy sets a default authorization policy for client requests. // This policy will be used unless overridden by individual routes. // diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index f985ae33037..7cc7b7a6c97 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -334,7 +334,8 @@

AuthorizationServer

AuthorizationServer configures an external server to authenticate client requests. The external server must implement the v3 Envoy -external authorization GRPC protocol (https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto).

+external authorization GRPC protocol (https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto) +or the HTTP authorization server protocol.

@@ -361,6 +362,37 @@

AuthorizationServer

+ + + + + + + +
+serviceType +
+ + +AuthorizationServiceType + + +
+(Optional) +

ServiceType sets the protocol used to communicate with +the external authorization server.

+
+httpSettings +
+ + +HTTPAuthorizationServerSettings + + +
+(Optional) +

HTTPAuthorizationServerSettings defines configurations for interacting with an external HTTP authorization server.

+
authPolicy
@@ -482,6 +514,29 @@

AuthorizationSer

+

AuthorizationServiceType +(string alias)

+

+(Appears on: +AuthorizationServer) +

+

+

AuthorizationServiceType indicates the protocol +implemented by the external authorization server.

+

+ + + + + + + + + + + + +
ValueDescription

"grpc"

"http"

CORSHeaderValue (string alias)

@@ -1281,6 +1336,156 @@

GlobalRateLimitPolicy +

HTTPAuthorizationServerAllowedHeaders +

+

+(Appears on: +HTTPAuthorizationServerSettings) +

+

+

HTTPAuthorizationServerAllowedHeaders specifies how to conditionally match against allowed headers +in the context of HTTP authorization. Regex support is intentionally excluded to simplify the user +experience and prevent potential issues. Only one of Prefix, Exact, Suffix or Contains must be provided.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+exact +
+ +string + +
+(Optional) +

Exact specifies a string that the header name must be equal to.

+
+prefix +
+ +string + +
+(Optional) +

Prefix defines a prefix match for the header name.

+
+suffix +
+ +string + +
+(Optional) +

Suffix defines a suffix match for a header name.

+
+contains +
+ +string + +
+(Optional) +

Contains specifies a substring that must be present in the header name.

+
+ignoreCase +
+ +bool + +
+(Optional) +

IgnoreCase specifies whether string matching should be case-insensitive.

+
+

HTTPAuthorizationServerSettings +

+

+(Appears on: +AuthorizationServer) +

+

+

HTTPAuthorizationServerSettings defines configurations for interacting with an external HTTP authorization server.

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+pathPrefix +
+ +string + +
+(Optional) +

PathPrefix Sets a prefix to the value of authorization request header Path.

+
+allowedAuthorizationHeaders +
+ + +[]HTTPAuthorizationServerAllowedHeaders + + +
+(Optional) +

AllowedAuthorizationHeaders specifies client request headers that will be sent to the authorization server. +Host, Method, Path, Content-Length, and Authorization headers are additionally included in the list.

+
+allowedUpstreamHeaders +
+ + +[]HTTPAuthorizationServerAllowedHeaders + + +
+(Optional) +

AllowedUpstreamHeaders specifies response headers from the authorization server +that may be added to the original client request before sending it to the upstream.

+

HTTPDirectResponsePolicy

@@ -5437,7 +5642,7 @@

ExtensionService

Services specifies the set of Kubernetes Service resources that -receive GRPC extension API requests. +receive extension API requests. If no weights are specified for any of the entries in this array, traffic will be spread evenly across all the services. @@ -5471,7 +5676,7 @@

ExtensionService (Optional)

Protocol may be used to specify (or override) the protocol used to reach this Service. -Values may be h2 or h2c. If omitted, protocol-selection falls back on Service annotations.

+Values may be h2, h2c or http/1.1. If omitted, protocol-selection falls back on Service annotations.

@@ -5486,9 +5691,9 @@

ExtensionService (Optional) -

The policy for load balancing GRPC service requests. Note that the +

The policy for load balancing service requests. Note that the Cookie and RequestHash load balancing strategies cannot be used -here.

+here for GRPC service requests.

@@ -7662,7 +7867,7 @@

ExtensionServiceSpec

Services specifies the set of Kubernetes Service resources that -receive GRPC extension API requests. +receive extension API requests. If no weights are specified for any of the entries in this array, traffic will be spread evenly across all the services. @@ -7696,7 +7901,7 @@

ExtensionServiceSpec (Optional)

Protocol may be used to specify (or override) the protocol used to reach this Service. -Values may be h2 or h2c. If omitted, protocol-selection falls back on Service annotations.

+Values may be h2, h2c or http/1.1. If omitted, protocol-selection falls back on Service annotations.

@@ -7711,9 +7916,9 @@

ExtensionServiceSpec (Optional) -

The policy for load balancing GRPC service requests. Note that the +

The policy for load balancing service requests. Note that the Cookie and RequestHash load balancing strategies cannot be used -here.

+here for GRPC service requests.