diff --git a/apis/projectcontour/v1/helpers.go b/apis/projectcontour/v1/helpers.go index 9220593d696..b0d5119a3b7 100644 --- a/apis/projectcontour/v1/helpers.go +++ b/apis/projectcontour/v1/helpers.go @@ -52,7 +52,7 @@ func (v *VirtualHost) AuthorizationContext() map[string]string { } // DisableGlobalAuthorization returns true if this virtual host disables -// global authorization. If a global authorization config present, the default +// global authorization. If a global authorization config is present, the default // policy is to not disable. func (v *VirtualHost) DisableGlobalAuthorization() bool { return v.Authorization != nil && v.Authorization.GlobalExternalAuthorizationDisabled diff --git a/apis/projectcontour/v1/httpproxy.go b/apis/projectcontour/v1/httpproxy.go index 8fd5bf15718..2934740b8e4 100644 --- a/apis/projectcontour/v1/httpproxy.go +++ b/apis/projectcontour/v1/httpproxy.go @@ -193,9 +193,10 @@ type ExtensionServiceReference struct { // external authorization GRPC protocol (https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto). type AuthorizationServer struct { // ExtensionServiceRef specifies the extension resource that will authorize client requests. + // One of globalExtAuthDisabled or extensionRef must be set. // - // +required - ExtensionServiceRef ExtensionServiceReference `json:"extensionRef,,omitempty"` + // +optional + ExtensionServiceRef ExtensionServiceReference `json:"extensionRef,omitempty"` // AuthPolicy sets a default authorization policy for client requests. // This policy will be used unless overridden by individual routes. @@ -225,6 +226,9 @@ type AuthorizationServer struct { WithRequestBody *AuthorizationServerBufferSettings `json:"withRequestBody,omitempty"` // GlobalExternalAuthorizationDisabled optionally disables the global external authorization on the virtual host. + // One of globalExtAuthDisabled or extensionRef must be set. + // + // +optional GlobalExternalAuthorizationDisabled bool `json:"globalExtAuthDisabled,omitempty"` } diff --git a/apis/projectcontour/v1alpha1/contourconfig.go b/apis/projectcontour/v1alpha1/contourconfig.go index 3e5ef114e10..9ae283541cf 100644 --- a/apis/projectcontour/v1alpha1/contourconfig.go +++ b/apis/projectcontour/v1alpha1/contourconfig.go @@ -63,7 +63,7 @@ type ContourConfigurationSpec struct { EnableExternalNameService *bool `json:"enableExternalNameService,omitempty"` // GlobalExternalAuthorization allows envoys external authorization filter - // to be enabled for all HTTP requests. + // to be enabled for all virtual hosts. // +optional GlobalExternalAuthorization *GlobalExternalAuthorizationConfig `json:"globalExtAuth,omitempty"` @@ -628,7 +628,7 @@ type NetworkParameters struct { EnvoyAdminPort *int `json:"adminPort,omitempty"` } -// GlobalExternalAuthorizationConfig defines properties of global HTTP external authorization. +// GlobalExternalAuthorizationConfig defines properties of global external authorization. type GlobalExternalAuthorizationConfig struct { // ExtensionService identifies the extension service responsible for the authorization. // formatted as /. diff --git a/changelogs/unreleased/4994-clayton-gonsalves-minor.md b/changelogs/unreleased/4994-clayton-gonsalves-minor.md new file mode 100644 index 00000000000..567e5cbcd32 --- /dev/null +++ b/changelogs/unreleased/4994-clayton-gonsalves-minor.md @@ -0,0 +1 @@ +Add support for Global External Authorization for HTTPProxy. \ No newline at end of file diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go index 84ed7eea422..1f396f2cbb8 100644 --- a/cmd/contour/servecontext.go +++ b/cmd/contour/servecontext.go @@ -406,6 +406,7 @@ func (ctx *serveContext) convertToContourConfigurationSpec() contour_api_v1alpha if ctx.Config.GlobalExternalAuthorization.WithRequestBody != nil { globalExtAuth.WithRequestBody = &contour_api_v1alpha1.GlobalAuthorizationServerBufferSettings{ + MaxRequestBytes: ctx.Config.GlobalExternalAuthorization.WithRequestBody.MaxRequestBytes, AllowPartialMessage: ref.To(ctx.Config.GlobalExternalAuthorization.WithRequestBody.AllowPartialMessage), PackAsBytes: ref.To(ctx.Config.GlobalExternalAuthorization.WithRequestBody.PackAsBytes), } diff --git a/cmd/contour/servecontext_test.go b/cmd/contour/servecontext_test.go index 4d5583caa2b..d709061df63 100644 --- a/cmd/contour/servecontext_test.go +++ b/cmd/contour/servecontext_test.go @@ -481,8 +481,9 @@ func TestConvertServeContext(t *testing.T) { DisablePermitInsecure: ref.To(false), FallbackCertificate: nil, }, - EnableExternalNameService: ref.To(false), - RateLimitService: nil, + EnableExternalNameService: ref.To(false), + RateLimitService: nil, + GlobalExternalAuthorization: nil, Policy: &contour_api_v1alpha1.PolicyConfig{ RequestHeadersPolicy: &contour_api_v1alpha1.HeadersPolicy{}, ResponseHeadersPolicy: &contour_api_v1alpha1.HeadersPolicy{}, @@ -699,6 +700,43 @@ func TestConvertServeContext(t *testing.T) { return cfg }, }, + "global external authorization": { + getServeContext: func(ctx *serveContext) *serveContext { + ctx.Config.GlobalExternalAuthorization = config.GlobalExternalAuthorization{ + ExtensionService: "extauthns/extauthtext", + FailOpen: true, + AuthPolicy: &config.GlobalAuthorizationPolicy{ + Context: map[string]string{ + "foo": "bar", + }, + }, + WithRequestBody: &config.GlobalAuthorizationServerBufferSettings{ + MaxRequestBytes: 512, + PackAsBytes: true, + AllowPartialMessage: true, + }, + } + return ctx + }, + getContourConfiguration: func(cfg contour_api_v1alpha1.ContourConfigurationSpec) contour_api_v1alpha1.ContourConfigurationSpec { + cfg.GlobalExternalAuthorization = &contour_api_v1alpha1.GlobalExternalAuthorizationConfig{ + ExtensionService: "extauthns/extauthtext", + FailOpen: ref.To(true), + AuthPolicy: &contour_api_v1alpha1.GlobalAuthorizationPolicy{ + Context: map[string]string{ + "foo": "bar", + }, + Disabled: ref.To(false), + }, + WithRequestBody: &contour_api_v1alpha1.GlobalAuthorizationServerBufferSettings{ + MaxRequestBytes: 512, + PackAsBytes: ref.To(true), + AllowPartialMessage: ref.To(true), + }, + } + return cfg + }, + }, } for name, tc := range cases { diff --git a/config.yaml b/config.yaml deleted file mode 100644 index 39c8dede14a..00000000000 --- a/config.yaml +++ /dev/null @@ -1,165 +0,0 @@ -# -# server: -# determine which XDS Server implementation to utilize in Contour. -# xds-server-type: contour -# -# Specify the Gateway API configuration. -# gateway: -# controllerName: projectcontour.io/projectcontour/contour -# -# should contour expect to be running inside a k8s cluster -# incluster: true -# -# path to kubeconfig (if not running inside a k8s cluster) -# kubeconfig: /path/to/.kube/config -# -# Disable RFC-compliant behavior to strip "Content-Length" header if -# "Tranfer-Encoding: chunked" is also set. -# disableAllowChunkedLength: false -# -# Disable Envoy's non-standard merge_slashes path transformation option -# that strips duplicate slashes from request URLs. -# disableMergeSlashes: false -# -# Disable HTTPProxy permitInsecure field -disablePermitInsecure: false -tls: -# minimum TLS version that Contour will negotiate -# minimum-protocol-version: "1.2" -# TLS ciphers to be supported by Envoy TLS listeners when negotiating -# TLS 1.2. -# cipher-suites: -# - '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305]' -# - '[ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]' -# - 'ECDHE-ECDSA-AES256-GCM-SHA384' -# - 'ECDHE-RSA-AES256-GCM-SHA384' -# Defines the Kubernetes name/namespace matching a secret to use -# as the fallback certificate when requests which don't match the -# SNI defined for a vhost. - fallback-certificate: -# name: fallback-secret-name -# namespace: projectcontour - envoy-client-certificate: -# name: envoy-client-cert-secret-name -# namespace: projectcontour -#### -# ExternalName Services are disabled by default due to CVE-2021-XXXXX -# You can re-enable them by setting this setting to `true`. -# This is not recommended without understanding the security implications. -# Please see the advisory at https://github.com/projectcontour/contour/security/advisories/GHSA-5ph6-qq5x-7jwc for the details. -# enableExternalNameService: false -## -# Address to be placed in status.loadbalancer field of Ingress objects. -# May be either a literal IP address or a host name. -# The value will be placed directly into the relevant field inside the status.loadBalancer struct. -# ingress-status-address: local.projectcontour.io -### Logging options -# Default setting -accesslog-format: envoy -# The default access log format is defined by Envoy but it can be customized by setting following variable. -# accesslog-format-string: "...\n" -# To enable JSON logging in Envoy -# accesslog-format: json -# accesslog-level: info -# The default fields that will be logged are specified below. -# To customise this list, just add or remove entries. -# The canonical list is available at -# https://godoc.org/github.com/projectcontour/contour/internal/envoy#JSONFields -# json-fields: -# - "@timestamp" -# - "authority" -# - "bytes_received" -# - "bytes_sent" -# - "downstream_local_address" -# - "downstream_remote_address" -# - "duration" -# - "method" -# - "path" -# - "protocol" -# - "request_id" -# - "requested_server_name" -# - "response_code" -# - "response_flags" -# - "uber_trace_id" -# - "upstream_cluster" -# - "upstream_host" -# - "upstream_local_address" -# - "upstream_service_time" -# - "user_agent" -# - "x_forwarded_for" -# - "grpc_status" -# - "grpc_status_number" -# -# default-http-versions: -# - "HTTP/2" -# - "HTTP/1.1" -# -# The following shows the default proxy timeout settings. -# timeouts: -# request-timeout: infinity -# connection-idle-timeout: 60s -# stream-idle-timeout: 5m -# max-connection-duration: infinity -# delayed-close-timeout: 1s -# connection-shutdown-grace-period: 5s -# connect-timeout: 2s -# -# Envoy cluster settings. -# cluster: -# configure the cluster dns lookup family -# valid options are: auto (default), v4, v6 -# dns-lookup-family: auto -# -# Envoy network settings. -# network: -# Configure the number of additional ingress proxy hops from the -# right side of the x-forwarded-for HTTP header to trust. -# num-trusted-hops: 0 -# Configure the port used to access the Envoy Admin interface. -# admin-port: 9001 -# -globalExtAuth: - # extensionService is the / - # of the ExtensionService we created in the - # previous step. - extensionService: projectcontour-auth/htpasswd - # failOpen is whether to allow requests through - # if there's an error. - failOpen: false - # Context is a set of key/value pairs that are sent to the authentication server in the check request. - authPolicy: - context: - header1: value1 - header2: value2 - routq: global - # ResponseTimeout configures maximum time to wait for a check response from the authorization server - responseTimeout: 1s - -# Global Policy settings. -# policy: -# # Default headers to set on all requests (unless set/removed on the HTTPProxy object itself) -# request-headers: -# set: -# # example: the hostname of the Envoy instance that proxied the request -# X-Envoy-Hostname: %HOSTNAME% -# # example: add a l5d-dst-override header to instruct Linkerd what service the request is destined for -# l5d-dst-override: %CONTOUR_SERVICE_NAME%.%CONTOUR_NAMESPACE%.svc.cluster.local:%CONTOUR_SERVICE_PORT% -# # default headers to set on all responses (unless set/removed on the HTTPProxy object itself) -# response-headers: -# set: -# # example: Envoy flags that provide additional details about the response or connection -# X-Envoy-Response-Flags: %RESPONSE_FLAGS% -# -# metrics: -# contour: -# address: 0.0.0.0 -# port: 8000 -# server-certificate-path: /path/to/server-cert.pem -# server-key-path: /path/to/server-private-key.pem -# ca-certificate-path: /path/to/root-ca-for-client-validation.pem -# envoy: -# address: 0.0.0.0 -# port: 8002 -# server-certificate-path: /path/to/server-cert.pem -# server-key-path: /path/to/server-private-key.pem -# ca-certificate-path: /path/to/root-ca-for-client-validation.pem diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index 3f0797448a6..93e48d8f11a 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -413,7 +413,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external authorization - filter to be enabled for all HTTP requests. + filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -3498,7 +3498,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external - authorization filter to be enabled for all HTTP requests. + authorization filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -5864,7 +5864,8 @@ spec: type: object extensionRef: description: ExtensionServiceRef specifies the extension resource - that will authorize client requests. + that will authorize client requests. One of globalExtAuthDisabled + or extensionRef must be set. properties: apiVersion: description: API version of the referent. If this field @@ -5893,7 +5894,8 @@ spec: globalExtAuthDisabled: description: GlobalExternalAuthorizationDisabled optionally disables the global external authorization on the virtual - host. + host. One of globalExtAuthDisabled or extensionRef must + be set. type: boolean responseTimeout: description: ResponseTimeout configures maximum time to wait diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index 226087ff122..acb84cbf443 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -626,7 +626,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external authorization - filter to be enabled for all HTTP requests. + filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -3711,7 +3711,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external - authorization filter to be enabled for all HTTP requests. + authorization filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -6077,7 +6077,8 @@ spec: type: object extensionRef: description: ExtensionServiceRef specifies the extension resource - that will authorize client requests. + that will authorize client requests. One of globalExtAuthDisabled + or extensionRef must be set. properties: apiVersion: description: API version of the referent. If this field @@ -6106,7 +6107,8 @@ spec: globalExtAuthDisabled: description: GlobalExternalAuthorizationDisabled optionally disables the global external authorization on the virtual - host. + host. One of globalExtAuthDisabled or extensionRef must + be set. type: boolean responseTimeout: description: ResponseTimeout configures maximum time to wait diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index e1d36c79f87..276fd72829b 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -427,7 +427,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external authorization - filter to be enabled for all HTTP requests. + filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -3512,7 +3512,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external - authorization filter to be enabled for all HTTP requests. + authorization filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -5878,7 +5878,8 @@ spec: type: object extensionRef: description: ExtensionServiceRef specifies the extension resource - that will authorize client requests. + that will authorize client requests. One of globalExtAuthDisabled + or extensionRef must be set. properties: apiVersion: description: API version of the referent. If this field @@ -5907,7 +5908,8 @@ spec: globalExtAuthDisabled: description: GlobalExternalAuthorizationDisabled optionally disables the global external authorization on the virtual - host. + host. One of globalExtAuthDisabled or extensionRef must + be set. type: boolean responseTimeout: description: ResponseTimeout configures maximum time to wait diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index 54ccdd444e0..c56b9d30198 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -632,7 +632,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external authorization - filter to be enabled for all HTTP requests. + filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -3717,7 +3717,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external - authorization filter to be enabled for all HTTP requests. + authorization filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -6083,7 +6083,8 @@ spec: type: object extensionRef: description: ExtensionServiceRef specifies the extension resource - that will authorize client requests. + that will authorize client requests. One of globalExtAuthDisabled + or extensionRef must be set. properties: apiVersion: description: API version of the referent. If this field @@ -6112,7 +6113,8 @@ spec: globalExtAuthDisabled: description: GlobalExternalAuthorizationDisabled optionally disables the global external authorization on the virtual - host. + host. One of globalExtAuthDisabled or extensionRef must + be set. type: boolean responseTimeout: description: ResponseTimeout configures maximum time to wait diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index d80cdd2f98f..7a1660f34ab 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -626,7 +626,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external authorization - filter to be enabled for all HTTP requests. + filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -3711,7 +3711,7 @@ spec: type: object globalExtAuth: description: GlobalExternalAuthorization allows envoys external - authorization filter to be enabled for all HTTP requests. + authorization filter to be enabled for all virtual hosts. properties: extensionService: description: ExtensionService identifies the extension service @@ -6077,7 +6077,8 @@ spec: type: object extensionRef: description: ExtensionServiceRef specifies the extension resource - that will authorize client requests. + that will authorize client requests. One of globalExtAuthDisabled + or extensionRef must be set. properties: apiVersion: description: API version of the referent. If this field @@ -6106,7 +6107,8 @@ spec: globalExtAuthDisabled: description: GlobalExternalAuthorizationDisabled optionally disables the global external authorization on the virtual - host. + host. One of globalExtAuthDisabled or extensionRef must + be set. type: boolean responseTimeout: description: ResponseTimeout configures maximum time to wait diff --git a/internal/dag/dag.go b/internal/dag/dag.go index c1157f5f078..7862085d6b0 100644 --- a/internal/dag/dag.go +++ b/internal/dag/dag.go @@ -657,6 +657,8 @@ type VirtualHost struct { // are rate limited. RateLimitPolicy *RateLimitPolicy + // GlobalExternalAuthorization defineshow requests for the virtual host + // are authorized. GlobalExternalAuthorization *GlobalExternalAuthorization Routes map[string]*Route diff --git a/internal/dag/httpproxy_processor.go b/internal/dag/httpproxy_processor.go index 269b9534bf4..a7ec266fbc6 100644 --- a/internal/dag/httpproxy_processor.go +++ b/internal/dag/httpproxy_processor.go @@ -92,7 +92,7 @@ type HTTPProxyProcessor struct { // Response headers that will be set on all routes (optional). ResponseHeadersPolicy *HeadersPolicy - // GlobalExternalAuthorization + // GlobalExternalAuthorization defines how requests will be authorized. GlobalExternalAuthorization *contour_api_v1alpha1.GlobalExternalAuthorizationConfig // ConnectTimeout defines how long the proxy should wait when establishing connection to upstream service. diff --git a/internal/envoy/v3/route.go b/internal/envoy/v3/route.go index 79eace91ecc..079de7dcd21 100644 --- a/internal/envoy/v3/route.go +++ b/internal/envoy/v3/route.go @@ -70,17 +70,6 @@ func VirtualHostAndRoutes(vh *dag.VirtualHost, dagRoutes []*dag.Route, secure bo // buildRoute converts a DAG route to an Envoy route. func buildRoute(dagRoute *dag.Route, vhostName string, secure bool, authService *dag.ExtensionCluster) *envoy_route_v3.Route { switch { - case dagRoute.HTTPSUpgrade && !secure && dagRoute.AuthDisabled: - rt := &envoy_route_v3.Route{ - Match: RouteMatch(dagRoute), - Action: UpgradeHTTPS(), - } - - if rt.TypedPerFilterConfig == nil { - rt.TypedPerFilterConfig = map[string]*anypb.Any{} - } - rt.TypedPerFilterConfig["envoy.filters.http.ext_authz"] = routeAuthzDisabled() - return rt case dagRoute.HTTPSUpgrade && !secure: // TODO(dfc) if we ensure the builder never returns a dag.Route connected // to a SecureVirtualHost that requires upgrade, this logic can move to diff --git a/internal/featuretests/v3/authorization_test.go b/internal/featuretests/v3/authorization_test.go index ff26b6b4bd8..031fe6635c8 100644 --- a/internal/featuretests/v3/authorization_test.go +++ b/internal/featuretests/v3/authorization_test.go @@ -30,8 +30,6 @@ import ( envoy_v3 "github.com/projectcontour/contour/internal/envoy/v3" "github.com/projectcontour/contour/internal/featuretests" "github.com/projectcontour/contour/internal/fixture" - "github.com/projectcontour/contour/internal/protobuf" - "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/tools/cache" @@ -327,30 +325,12 @@ func authzOverrideDisabled(t *testing.T, rh cache.ResourceEventHandler, c *Conto &envoy_route_v3.Route{ Match: routePrefix("/default"), Action: withRedirect(), - TypedPerFilterConfig: map[string]*anypb.Any{ - "envoy.filters.http.ext_authz": protobuf.MustMarshalAny( - &envoy_config_filter_http_ext_authz_v3.ExtAuthzPerRoute{ - Override: &envoy_config_filter_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{ - Disabled: true, - }, - }, - ), - }, }, ), envoy_v3.VirtualHost(enabled, &envoy_route_v3.Route{ Match: routePrefix("/disabled"), Action: withRedirect(), - TypedPerFilterConfig: map[string]*anypb.Any{ - "envoy.filters.http.ext_authz": protobuf.MustMarshalAny( - &envoy_config_filter_http_ext_authz_v3.ExtAuthzPerRoute{ - Override: &envoy_config_filter_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{ - Disabled: true, - }, - }, - ), - }, }, &envoy_route_v3.Route{ Match: routePrefix("/default"), diff --git a/internal/xdscache/v3/listener.go b/internal/xdscache/v3/listener.go index 7ab2b372a79..84cac3d0b5e 100644 --- a/internal/xdscache/v3/listener.go +++ b/internal/xdscache/v3/listener.go @@ -141,6 +141,8 @@ type ListenerConfig struct { // used. RateLimitConfig *RateLimitConfig + // GlobalExternalAuthConfig optionally configures the global external authorization Service to be + // used. GlobalExternalAuthConfig *GlobalExternalAuthConfig } diff --git a/internal/xdscache/v3/route.go b/internal/xdscache/v3/route.go index f4914291445..fb4f8364760 100644 --- a/internal/xdscache/v3/route.go +++ b/internal/xdscache/v3/route.go @@ -113,6 +113,7 @@ func (c *RouteCache) OnChange(root *dag.DAG) { } var auth *dag.ExtensionCluster + // TLS vhost overrides non TLS vhost. if vhost.GlobalExternalAuthorization != nil { auth = vhost.GlobalExternalAuthorization.AuthorizationService } else if vhost.AuthorizationService != nil { diff --git a/pkg/config/parameters.go b/pkg/config/parameters.go index fe627d4a93b..d117356fe6c 100644 --- a/pkg/config/parameters.go +++ b/pkg/config/parameters.go @@ -562,7 +562,7 @@ type Parameters struct { Metrics MetricsParameters `yaml:"metrics,omitempty"` } -// GlobalExternalAuthorizationConfig defines properties of global HTTP external authorization. +// GlobalExternalAuthorizationConfig defines properties of global external authorization. type GlobalExternalAuthorization struct { // ExtensionService identifies the extension service responsible for the authorization. // formatted as /. diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index 8953532a398..1f1988bf707 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -345,7 +345,7 @@

AuthorizationServer -extensionRef, +extensionRef
@@ -354,7 +354,9 @@

AuthorizationServer -

ExtensionServiceRef specifies the extension resource that will authorize client requests.

+(Optional) +

ExtensionServiceRef specifies the extension resource that will authorize client requests. +One of globalExtAuthDisabled or extensionRef must be set.

@@ -429,7 +431,9 @@

AuthorizationServer -

GlobalExternalAuthorizationDisabled optionally disables the global external authorization on the virtual host.

+(Optional) +

GlobalExternalAuthorizationDisabled optionally disables the global external authorization on the virtual host. +One of globalExtAuthDisabled or extensionRef must be set.

@@ -4648,7 +4652,7 @@

ContourConfiguration (Optional)

GlobalExternalAuthorization allows envoys external authorization filter -to be enabled for all HTTP requests.

+to be enabled for all virtual hosts.

@@ -5342,7 +5346,7 @@

ContourConfiguratio (Optional)

GlobalExternalAuthorization allows envoys external authorization filter -to be enabled for all HTTP requests.

+to be enabled for all virtual hosts.

@@ -6947,7 +6951,7 @@

GlobalExte ContourConfigurationSpec)

-

GlobalExternalAuthorizationConfig defines properties of global HTTP external authorization.

+

GlobalExternalAuthorizationConfig defines properties of global external authorization.

diff --git a/site/content/guides/external-authorization.md b/site/content/guides/external-authorization.md index 367b76b9043..f4339dc3944 100644 --- a/site/content/guides/external-authorization.md +++ b/site/content/guides/external-authorization.md @@ -361,13 +361,170 @@ $ curl -k --user user1:password1 https://local.projectcontour.io/test/$((RANDOM) {"TestId":"","Path":"/test/27132","Host":"local.projectcontour.io","Method":"GET","Proto":"HTTP/1.1","Headers":{"Accept":["*/*"],"Auth-Handler":["htpasswd"],"Auth-Realm":["default"],"Auth-Username":["user1"],"Authorization":["Basic dXNlcjE6cGFzc3dvcmQx"],"Content-Length":["0"],"User-Agent":["curl/7.64.1"],"X-Envoy-Expected-Rq-Timeout-Ms":["15000"],"X-Envoy-Internal":["true"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"X-Request-Id":["2c0ae102-4cf6-400e-a38f-5f0b844364cc"],"X-Request-Start":["t=1601601826.102"]}} ``` +## Global External Authorization + +Starting from version 1.25, Contour supports global external authorization. This allows you to setup a single external authorization configuration for all your virtual hosts (HTTP and HTTPS). + +To get started make sure you have `contour-authserver` and the `ExtensionService` deployed as described in above. + + +### Global Configuration + +Define the global external authorization configuration in your contour config. + +```yaml +globalExtAuth: + extensionService: projectcontour-auth/htpasswd + failOpen: false + authPolicy: + context: + header1: value1 + header2: value2 + responseTimeout: 1s +``` + +Setup a HTTPProxy without TLS +```yaml +apiVersion: projectcontour.io/v1 +kind: HTTPProxy +metadata: + name: echo +spec: + virtualhost: + fqdn: local.projectcontour.io + routes: + - services: + - name: ingress-conformance-echo + port: 80 +``` + +``` +$ kubectl apply -f echo-proxy.yaml +httpproxy.projectcontour.io/echo created +``` + +When we make a HTTP request without authentication details, we can see that the endpoint is secured and returns a 401. + +``` +$ curl -k -I http://local.projectcontour.io/test/$((RANDOM)) +HTTP/1.1 401 Unauthorized +www-authenticate: Basic realm="default", charset="UTF-8" +vary: Accept-Encoding +date: Mon, 20 Feb 2023 13:45:31 GMT +``` + +If you add the username and password to the same request you can verify that the request succeeds. +``` +$ curl -k --user user1:password1 http://local.projectcontour.io/test/$((RANDOM)) +{"TestId":"","Path":"/test/27748","Host":"local.projectcontour.io","Method":"GET","Proto":"HTTP/1.1","Headers":{"Accept":["*/*"],"Auth-Context-Header1":["value1"],"Auth-Context-Header2":["value2"],"Auth-Context-Routq":["global"],"Auth-Handler":["htpasswd"],"Auth-Realm":["default"],"Auth-Username":["user1"],"Authorization":["Basic dXNlcjE6cGFzc3dvcmQx"],"User-Agent":["curl/7.86.0"],"X-Envoy-Expected-Rq-Timeout-Ms":["15000"],"X-Envoy-Internal":["true"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["http"],"X-Request-Id":["b6bb7036-8408-4b03-9ce5-7011d89799b4"],"X-Request-Start":["t=1676900780.118"]}} +``` + +Global external authorization can also be configured with TLS virtual hosts. Update your HTTPProxy by adding `tls` and `secretName` to it. + +```yaml +apiVersion: projectcontour.io/v1 +kind: HTTPProxy +metadata: + name: echo +spec: + virtualhost: + fqdn: local.projectcontour.io + tls: + secretName: ingress-conformance-echo + routes: + - services: + - name: ingress-conformance-echo + port: 80 +``` + +``` +$ kubectl apply -f echo-proxy.yaml +httpproxy.projectcontour.io/echo configured +``` + +you can verify the HTTPS requests succeeds +``` +$ curl -k --user user1:password1 https://local.projectcontour.io/test/$((RANDOM)) +{"TestId":"","Path":"/test/13499","Host":"local.projectcontour.io","Method":"GET","Proto":"HTTP/1.1","Headers":{"Accept":["*/*"],"Auth-Context-Header1":["value1"],"Auth-Context-Header2":["value2"],"Auth-Context-Routq":["global"],"Auth-Handler":["htpasswd"],"Auth-Realm":["default"],"Auth-Username":["user1"],"Authorization":["Basic dXNlcjE6cGFzc3dvcmQx"],"User-Agent":["curl/7.86.0"],"X-Envoy-Expected-Rq-Timeout-Ms":["15000"],"X-Envoy-Internal":["true"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"X-Request-Id":["2b3edbed-3c68-44ef-a659-2e1245d7fe13"],"X-Request-Start":["t=1676901557.918"]}} +``` + +### excluding a virtual host from global external authorization + +You can also exclude a TLS virtual host from the global external autohorization policy by adding the `globalExtAuthDisabled` flag. + +```yaml +apiVersion: projectcontour.io/v1 +kind: HTTPProxy +metadata: + name: echo +spec: + virtualhost: + fqdn: local.projectcontour.io + tls: + secretName: ingress-conformance-echo + authorization: + globalExtAuthDisabled: true + routes: + - services: + - name: ingress-conformance-echo + port: 80 +``` + +``` +$ kubectl apply -f echo-proxy.yaml +httpproxy.projectcontour.io/echo configured +``` + +You can verify that an insecure request succeeds without being authorized. + +``` +$ curl -k https://local.projectcontour.io/test/$((RANDOM)) +{"TestId":"","Path":"/test/51","Host":"local.projectcontour.io","Method":"GET","Proto":"HTTP/1.1","Headers":{"Accept":["*/*"],"User-Agent":["curl/7.86.0"],"X-Envoy-Expected-Rq-Timeout-Ms":["15000"],"X-Envoy-Internal":["true"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"X-Request-Id":["18716e12-dcce-45ba-a3bb-bc26af3775d2"],"X-Request-Start":["t=1676901847.802"]}} +``` + +### Overridng global external authorization for a virtual host + +In some cases you may want a different configuration that what is defined globally. To override the global external autohorization add the `authorization` block to your HTTPProxy as shown below + +```yaml +apiVersion: projectcontour.io/v1 +kind: HTTPProxy +metadata: + name: echo +spec: + virtualhost: + fqdn: local.projectcontour.io + tls: + secretName: ingress-conformance-echo + authorization: + extensionRef: + name: htpasswd + namespace: projectcontour-auth + routes: + - services: + - name: ingress-conformance-echo + port: 80 +``` + +``` +$ kubectl apply -f echo-proxy.yaml +httpproxy.projectcontour.io/echo configured +``` + +You can verify that the endpoint has applied the overridden external authorization configuration. + +``` +$ curl -k --user user1:password1 https://local.projectcontour.io/test/$((RANDOM)) +{"TestId":"","Path":"/test/4514","Host":"local.projectcontour.io","Method":"GET","Proto":"HTTP/1.1","Headers":{"Accept":["*/*"],"Auth-Context-Overriden_message":["overriden_value"],"Auth-Handler":["htpasswd"],"Auth-Realm":["default"],"Auth-Username":["user1"],"Authorization":["Basic dXNlcjE6cGFzc3dvcmQx"],"User-Agent":["curl/7.86.0"],"X-Envoy-Expected-Rq-Timeout-Ms":["15000"],"X-Envoy-Internal":["true"],"X-Forwarded-For":["172.18.0.1"],"X-Forwarded-Proto":["https"],"X-Request-Id":["8a02d6ce-8be0-4e87-8ed8-cca7e239e986"],"X-Request-Start":["t=1676902237.111"]}} +``` + ## Caveats There are a few caveats to consider when deploying external authorization: 1. Only one external authorization server can be configured on a virtual host -1. Only HTTPS virtual hosts are supported +1. HTTP hosts are only supported with global external authorization. 1. External authorization cannot be used with the TLS fallback certificate (i.e. client SNI support is required) [1]: https://github.com/projectcontour/contour-authserver diff --git a/test/e2e/deployment.go b/test/e2e/deployment.go index 4bc94465da7..2de69f009e4 100644 --- a/test/e2e/deployment.go +++ b/test/e2e/deployment.go @@ -11,6 +11,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build e2e +// +build e2e + package e2e import ( diff --git a/test/e2e/httpproxy/global_external_auth_test.go b/test/e2e/httpproxy/global_external_auth_test.go index 92acbe8505f..5e6647ab944 100644 --- a/test/e2e/httpproxy/global_external_auth_test.go +++ b/test/e2e/httpproxy/global_external_auth_test.go @@ -11,6 +11,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build e2e +// +build e2e + package httpproxy import ( diff --git a/test/e2e/httpproxy/httpproxy_test.go b/test/e2e/httpproxy/httpproxy_test.go index f207aebe31c..e21a6241806 100644 --- a/test/e2e/httpproxy/httpproxy_test.go +++ b/test/e2e/httpproxy/httpproxy_test.go @@ -11,6 +11,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build e2e +// +build e2e + package httpproxy import (