From 3589fc0a9d574bb686cd93ec841c0223035df603 Mon Sep 17 00:00:00 2001 From: Saman Mahdanian <46444723+SamMHD@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:48:47 +0330 Subject: [PATCH] Apply Disable ExtAuth from GlobalExtAuth and Remove Auth from HTTP Upgrade (#6661) * fix #6617 and #6659 Changes: - use dagRoute's AuthContext and AuthDisabled in HTTPS-Upgrade to fix 6659 - Use globalExtAuth.AuthPolicy.Disabled to calculate dagRoute.AuthDisabled - Fix Tests Signed-off-by: Saman Mahdanian * add changelog Signed-off-by: Saman Mahdanian * fix indentation Signed-off-by: Saman Mahdanian * fix indentation in route.go Signed-off-by: Saman Mahdanian * disable ext_auth when upgrading to HTTPS Signed-off-by: Saman Mahdanian * fix tests for "disable ext_auth when upgrading to HTTPS" Signed-off-by: Saman Mahdanian * fix CHANGELOG for "disable ext_auth when upgrading to HTTPS" Signed-off-by: Saman Mahdanian * add tests for globalExtAuth.AuthPolicy.disabled proper behaviour Signed-off-by: Saman Mahdanian * fix gofumpt issue with global_authorization_test.go Signed-off-by: Saman Mahdanian * fix nil authorization extref issue while overwriting GlobalExtAuth Signed-off-by: Saman Mahdanian * fix linting issue Signed-off-by: Saman Mahdanian * Update changelogs/unreleased/6661-SamMHD-minor.md Signed-off-by: Saman Mahdanian --------- Signed-off-by: Saman Mahdanian --- apis/projectcontour/v1/helpers.go | 5 + changelogs/unreleased/6661-SamMHD-minor.md | 10 + internal/dag/httpproxy_processor.go | 25 +- internal/envoy/v3/route.go | 21 +- .../featuretests/v3/authorization_test.go | 40 ++- internal/featuretests/v3/envoy.go | 5 +- .../v3/global_authorization_test.go | 283 +++++++++++++++--- internal/featuretests/v3/headerpolicy_test.go | 22 +- internal/featuretests/v3/route_test.go | 70 +++-- internal/featuretests/v3/tcpproxy_test.go | 22 +- internal/xdscache/v3/route_test.go | 111 +++---- 11 files changed, 408 insertions(+), 206 deletions(-) create mode 100644 changelogs/unreleased/6661-SamMHD-minor.md diff --git a/apis/projectcontour/v1/helpers.go b/apis/projectcontour/v1/helpers.go index 21b17f02936..f64a593bdc0 100644 --- a/apis/projectcontour/v1/helpers.go +++ b/apis/projectcontour/v1/helpers.go @@ -40,6 +40,11 @@ func (v *VirtualHost) DisableAuthorization() bool { return false } +// IsConfigured returns whether service ref is configured +func (r *ExtensionServiceReference) IsConfigured() bool { + return r.Name != "" +} + // AuthorizationContext returns the authorization policy context (if present). func (v *VirtualHost) AuthorizationContext() map[string]string { if v.AuthorizationConfigured() { diff --git a/changelogs/unreleased/6661-SamMHD-minor.md b/changelogs/unreleased/6661-SamMHD-minor.md new file mode 100644 index 00000000000..c1d2fd05f7a --- /dev/null +++ b/changelogs/unreleased/6661-SamMHD-minor.md @@ -0,0 +1,10 @@ +## Disable ExtAuth by default if GlobalExtAuth.AuthPolicy.Disabled is set + +Global external authorization can now be disabled by default and enabled by overriding the vhost and route level auth policies. +This is achieved by setting the `globalExtAuth.authPolicy.disabled` in the configuration file or `ContourConfiguration` CRD to `true`, and setting the `authPolicy.disabled` to `false` in the vhost and route level auth policies. +The final authorization state is determined by the most specific policy applied at the route level. + +## Disable External Authorization in HTTPS Upgrade + +When external authorization is enabled, no authorization check will be performed for HTTP to HTTPS redirection. +Previously, external authorization was checked before redirection, which could result in a 401 Unauthorized error instead of a 301 Moved Permanently status code. diff --git a/internal/dag/httpproxy_processor.go b/internal/dag/httpproxy_processor.go index 6501d646e09..f1d0741ab49 100644 --- a/internal/dag/httpproxy_processor.go +++ b/internal/dag/httpproxy_processor.go @@ -852,16 +852,31 @@ func (p *HTTPProxyProcessor) computeRoutes( // enable it on the route and propagate defaults // downwards. if rootProxy.Spec.VirtualHost.AuthorizationConfigured() || p.GlobalExternalAuthorization != nil { + // Global external or vhost-level authorization is enabled by default + // unless an AuthPolicy explicitly disables it. By default, `disabled` + // is set to false, meaning authorization is active. This global setting + // can be overridden by vhost-level AuthPolicy, which can further be + // overridden by route-specific AuthPolicy. + // Therefore, the final authorization state is determined by the + // most specific policy applied at the route level. + disabled := false + + if p.GlobalExternalAuthorization != nil && p.GlobalExternalAuthorization.AuthPolicy != nil { + disabled = p.GlobalExternalAuthorization.AuthPolicy.Disabled + } + // When the ext_authz filter is added to a // vhost, it is in enabled state, but we can // disable it per route. We emulate disabling // it at the vhost layer by defaulting the state // from the root proxy. - disabled := rootProxy.Spec.VirtualHost.DisableAuthorization() + if rootProxy.Spec.VirtualHost.AuthorizationConfigured() { + disabled = rootProxy.Spec.VirtualHost.DisableAuthorization() + } // Take the default for enabling authorization - // from the virtual host. If this route has a - // policy, let that override. + // from the virtualhost/global-extauth. If this + // route has a policy, let that override. if route.AuthPolicy != nil { disabled = route.AuthPolicy.Disabled } @@ -1441,14 +1456,14 @@ func determineExternalAuthTimeout(responseTimeout string, validCond *contour_v1. } func (p *HTTPProxyProcessor) computeSecureVirtualHostAuthorization(validCond *contour_v1.DetailedCondition, httpproxy *contour_v1.HTTPProxy, svhost *SecureVirtualHost) bool { - if httpproxy.Spec.VirtualHost.AuthorizationConfigured() && !httpproxy.Spec.VirtualHost.DisableAuthorization() { + if httpproxy.Spec.VirtualHost.AuthorizationConfigured() && !httpproxy.Spec.VirtualHost.DisableAuthorization() && httpproxy.Spec.VirtualHost.Authorization.ExtensionServiceRef.IsConfigured() { authorization := p.computeVirtualHostAuthorization(httpproxy.Spec.VirtualHost.Authorization, validCond, httpproxy) if authorization == nil { return false } svhost.ExternalAuthorization = authorization - } else if p.GlobalExternalAuthorization != nil && !httpproxy.Spec.VirtualHost.DisableAuthorization() { + } else if p.GlobalExternalAuthorization != nil { globalAuthorization := p.computeVirtualHostAuthorization(p.GlobalExternalAuthorization, validCond, httpproxy) if globalAuthorization == nil { return false diff --git a/internal/envoy/v3/route.go b/internal/envoy/v3/route.go index 188260754ef..4cd4ea75425 100644 --- a/internal/envoy/v3/route.go +++ b/internal/envoy/v3/route.go @@ -121,14 +121,18 @@ func buildRoute(dagRoute *dag.Route, vhostName string, secure bool) *envoy_confi // envoy.RouteRoute. Currently the DAG processor adds any HTTP->HTTPS // redirect routes to *both* the insecure and secure vhosts. route.Action = UpgradeHTTPS() + + // Disable External Authorization it is being redirected to HTTPS route + route.TypedPerFilterConfig = map[string]*anypb.Any{} + route.TypedPerFilterConfig[ExtAuthzFilterName] = routeAuthzDisabled() case dagRoute.DirectResponse != nil: route.TypedPerFilterConfig = map[string]*anypb.Any{} // Apply per-route authorization policy modifications. if dagRoute.AuthDisabled { - route.TypedPerFilterConfig["envoy.filters.http.ext_authz"] = routeAuthzDisabled() + route.TypedPerFilterConfig[ExtAuthzFilterName] = routeAuthzDisabled() } else if len(dagRoute.AuthContext) > 0 { - route.TypedPerFilterConfig["envoy.filters.http.ext_authz"] = routeAuthzContext(dagRoute.AuthContext) + route.TypedPerFilterConfig[ExtAuthzFilterName] = routeAuthzContext(dagRoute.AuthContext) } route.Action = routeDirectResponse(dagRoute.DirectResponse) @@ -592,6 +596,19 @@ func UpgradeHTTPS() *envoy_config_route_v3.Route_Redirect { } } +// DisabledExtAuthConfig returns a route TypedPerFilterConfig that disables ExtAuth +func DisabledExtAuthConfig() map[string]*anypb.Any { + return map[string]*anypb.Any{ + ExtAuthzFilterName: protobuf.MustMarshalAny( + &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute{ + Override: &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{ + Disabled: true, + }, + }, + ), + } +} + // headerValueList creates a list of Envoy HeaderValueOptions from the provided map. func headerValueList(hvm map[string]string, app bool) []*envoy_config_core_v3.HeaderValueOption { var hvs []*envoy_config_core_v3.HeaderValueOption diff --git a/internal/featuretests/v3/authorization_test.go b/internal/featuretests/v3/authorization_test.go index 2e5458ef6bb..db9e76e3236 100644 --- a/internal/featuretests/v3/authorization_test.go +++ b/internal/featuretests/v3/authorization_test.go @@ -267,13 +267,6 @@ func authzOverrideDisabled(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont // same authorization enablement as the root proxy, and // the other path should have the opposite enablement. - disabledConfig := withFilterConfig(envoy_v3.ExtAuthzFilterName, - &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute{ - Override: &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{ - Disabled: true, - }, - }) - c.Request(routeType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ TypeUrl: routeType, Resources: resources(t, @@ -287,7 +280,7 @@ func authzOverrideDisabled(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont &envoy_config_route_v3.Route{ Match: routePrefix("/default"), Action: routeCluster("default/app-server/80/da39a3ee5e"), - TypedPerFilterConfig: disabledConfig, + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -297,7 +290,7 @@ func authzOverrideDisabled(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont &envoy_config_route_v3.Route{ Match: routePrefix("/disabled"), Action: routeCluster("default/app-server/80/da39a3ee5e"), - TypedPerFilterConfig: disabledConfig, + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, &envoy_config_route_v3.Route{ Match: routePrefix("/default"), @@ -309,22 +302,26 @@ func authzOverrideDisabled(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont "ingress_http", envoy_v3.VirtualHost(disabled, &envoy_config_route_v3.Route{ - Match: routePrefix("/enabled"), - Action: withRedirect(), + Match: routePrefix("/enabled"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/default"), - Action: withRedirect(), + Match: routePrefix("/default"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), envoy_v3.VirtualHost(enabled, &envoy_config_route_v3.Route{ - Match: routePrefix("/disabled"), - Action: withRedirect(), + Match: routePrefix("/disabled"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/default"), - Action: withRedirect(), + Match: routePrefix("/default"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -406,8 +403,9 @@ func authzMergeRouteContext(t *testing.T, rh ResourceEventHandlerWrapper, c *Con "ingress_http", envoy_v3.VirtualHost(fqdn, &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: withRedirect(), + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -433,8 +431,8 @@ func authzInvalidReference(t *testing.T, rh ResourceEventHandlerWrapper, c *Cont invalid.Spec.VirtualHost.Authorization.ExtensionServiceRef = contour_v1.ExtensionServiceReference{ APIVersion: "foo/bar", - Namespace: "", - Name: "", + Namespace: "missing", + Name: "extension", } rh.OnDelete(invalid) diff --git a/internal/featuretests/v3/envoy.go b/internal/featuretests/v3/envoy.go index 748d776b521..7b4d1ca8501 100644 --- a/internal/featuretests/v3/envoy.go +++ b/internal/featuretests/v3/envoy.go @@ -166,8 +166,9 @@ func routeHostRewriteHeader(cluster, hostnameHeader string) *envoy_config_route_ func upgradeHTTPS(match *envoy_config_route_v3.RouteMatch) *envoy_config_route_v3.Route { return &envoy_config_route_v3.Route{ - Match: match, - Action: envoy_v3.UpgradeHTTPS(), + Match: match, + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), } } diff --git a/internal/featuretests/v3/global_authorization_test.go b/internal/featuretests/v3/global_authorization_test.go index 7bb0e0e4aaa..b5ad014d377 100644 --- a/internal/featuretests/v3/global_authorization_test.go +++ b/internal/featuretests/v3/global_authorization_test.go @@ -41,6 +41,39 @@ import ( xdscache_v3 "github.com/projectcontour/contour/internal/xdscache/v3" ) +var ( + normalGlobalExtAuthConfig contour_v1.AuthorizationServer = contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Name: "extension", + Namespace: "auth", + }, + FailOpen: false, + ResponseTimeout: defaultResponseTimeout.String(), + AuthPolicy: &contour_v1.AuthorizationPolicy{ + Context: map[string]string{ + "header_type": "root_config", + "header_1": "message_1", + }, + }, + } + + disabledGlobalExtAuthConfig contour_v1.AuthorizationServer = contour_v1.AuthorizationServer{ + ExtensionServiceRef: contour_v1.ExtensionServiceReference{ + Name: "extension", + Namespace: "auth", + }, + FailOpen: false, + ResponseTimeout: defaultResponseTimeout.String(), + AuthPolicy: &contour_v1.AuthorizationPolicy{ + Disabled: true, + Context: map[string]string{ + "header_type": "root_config", + "header_1": "message_1", + }, + }, + } +) + func globalExternalAuthorizationFilterExists(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { p := &contour_v1.HTTPProxy{ ObjectMeta: meta_v1.ObjectMeta{ @@ -188,7 +221,18 @@ func globalExternalAuthorizationWithTLSGlobalAuthDisabled(t *testing.T, rh Resou FilterChains: []*envoy_config_listener_v3.FilterChain{ filterchaintls("foo.com", featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), - httpsFilterFor("foo.com"), + authzFilterFor( + "foo.com", + &envoy_filter_http_ext_authz_v3.ExtAuthz{ + Services: grpcCluster("extension/auth/extension"), + ClearRouteCache: true, + 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(), @@ -277,6 +321,138 @@ func globalExternalAuthorizationWithMergedAuthPolicy(t *testing.T, rh ResourceEv }) } +func globalExternalAuthorizationDisabledByDefault(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + p := &contour_v1.HTTPProxy{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "default", + Name: "proxy1", + }, + Spec: contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "foo.com", + }, + Routes: []contour_v1.Route{ + { + Services: []contour_v1.Service{ + { + Name: "s1", + Port: 80, + }, + }, + }, + }, + }, + } + rh.OnAdd(p) + + httpListener := defaultHTTPListener() + + // replace the default filter chains with an HCM that includes the global + // extAuthz filter. + httpListener.FilterChains = envoy_v3.FilterChains(getGlobalExtAuthHCM()) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + httpListener, + statsListener()), + }).Status(p).IsValid() + + c.Request(routeType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: routeType, + Resources: resources(t, + envoy_v3.RouteConfiguration( + "ingress_http", + envoy_v3.VirtualHost("foo.com", + &envoy_config_route_v3.Route{ + Match: routePrefix("/"), + Action: routeCluster("default/s1/80/da39a3ee5e"), + TypedPerFilterConfig: map[string]*anypb.Any{ + envoy_v3.ExtAuthzFilterName: protobuf.MustMarshalAny( + &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute{ + Override: &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{ + Disabled: true, + }, + }, + ), + }, + }, + ), + ), + ), + }) +} + +func GlobalExternalAuthorizationDisabledByDefaultAndEnabledOnRoute(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + p := &contour_v1.HTTPProxy{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "default", + Name: "proxy1", + }, + Spec: contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "foo.com", + }, + Routes: []contour_v1.Route{ + { + Services: []contour_v1.Service{ + { + Name: "s1", + Port: 80, + }, + }, + AuthPolicy: &contour_v1.AuthorizationPolicy{ + Disabled: false, + }, + }, + }, + }, + } + rh.OnAdd(p) + + httpListener := defaultHTTPListener() + + // replace the default filter chains with an HCM that includes the global + // extAuthz filter. + httpListener.FilterChains = envoy_v3.FilterChains(getGlobalExtAuthHCM()) + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + httpListener, + statsListener()), + }).Status(p).IsValid() + + c.Request(routeType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: routeType, + Resources: resources(t, + envoy_v3.RouteConfiguration( + "ingress_http", + envoy_v3.VirtualHost("foo.com", + &envoy_config_route_v3.Route{ + Match: routePrefix("/"), + Action: routeCluster("default/s1/80/da39a3ee5e"), + TypedPerFilterConfig: map[string]*anypb.Any{ + envoy_v3.ExtAuthzFilterName: protobuf.MustMarshalAny( + &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute{ + Override: &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute_CheckSettings{ + CheckSettings: &envoy_filter_http_ext_authz_v3.CheckSettings{ + ContextExtensions: map[string]string{ + "header_type": "root_config", + "header_1": "message_1", + }, + }, + }, + }, + ), + }, + }, + ), + ), + ), + }) +} + func globalExternalAuthorizationWithMergedAuthPolicyTLS(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { p := fixture.NewProxy("TLSProxy"). WithFQDN("foo.com"). @@ -347,6 +523,22 @@ func globalExternalAuthorizationWithMergedAuthPolicyTLS(t *testing.T, rh Resourc statsListener()), }).Status(p).IsValid() + expectedAuthContext := map[string]*anypb.Any{ + envoy_v3.ExtAuthzFilterName: protobuf.MustMarshalAny( + &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute{ + Override: &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute_CheckSettings{ + CheckSettings: &envoy_filter_http_ext_authz_v3.CheckSettings{ + ContextExtensions: map[string]string{ + "header_type": "proxy_config", + "header_1": "message_1", + "header_2": "message_2", + }, + }, + }, + }, + ), + } + c.Request(routeType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ TypeUrl: routeType, Resources: resources(t, @@ -354,23 +546,9 @@ func globalExternalAuthorizationWithMergedAuthPolicyTLS(t *testing.T, rh Resourc "https/foo.com", envoy_v3.VirtualHost("foo.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: routeCluster("default/s1/80/da39a3ee5e"), - TypedPerFilterConfig: map[string]*anypb.Any{ - envoy_v3.ExtAuthzFilterName: protobuf.MustMarshalAny( - &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute{ - Override: &envoy_filter_http_ext_authz_v3.ExtAuthzPerRoute_CheckSettings{ - CheckSettings: &envoy_filter_http_ext_authz_v3.CheckSettings{ - ContextExtensions: map[string]string{ - "header_type": "proxy_config", - "header_1": "message_1", - "header_2": "message_2", - }, - }, - }, - }, - ), - }, + Match: routePrefix("/"), + Action: routeCluster("default/s1/80/da39a3ee5e"), + TypedPerFilterConfig: expectedAuthContext, }, ), ), @@ -378,8 +556,9 @@ func globalExternalAuthorizationWithMergedAuthPolicyTLS(t *testing.T, rh Resourc "ingress_http", envoy_v3.VirtualHost("foo.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: withRedirect(), + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -565,25 +744,54 @@ func globalExternalAuthorizationFilterTLSWithFallbackCertificate(t *testing.T, r } func TestGlobalAuthorization(t *testing.T) { - subtests := map[string]func(*testing.T, ResourceEventHandlerWrapper, *Contour){ + subtests := map[string]struct { + globalExtAuthConfig *contour_v1.AuthorizationServer + testFunction func(*testing.T, ResourceEventHandlerWrapper, *Contour) + }{ // Default extAuthz on non TLS host. - "GlobalExternalAuthorizationFilterExists": globalExternalAuthorizationFilterExists, + "GlobalExternalAuthorizationFilterExists": { + &normalGlobalExtAuthConfig, globalExternalAuthorizationFilterExists, + }, // Default extAuthz on non TLS and TLS hosts. - "GlobalExternalAuthorizationFilterExistsTLS": globalExternalAuthorizationFilterExistsTLS, + "GlobalExternalAuthorizationFilterExistsTLS": { + &normalGlobalExtAuthConfig, globalExternalAuthorizationFilterExistsTLS, + }, // extAuthz disabled on TLS host. - "GlobalExternalAuthorizationWithTLSGlobalAuthDisabled": globalExternalAuthorizationWithTLSGlobalAuthDisabled, + "GlobalExternalAuthorizationWithTLSGlobalAuthDisabled": { + &normalGlobalExtAuthConfig, globalExternalAuthorizationWithTLSGlobalAuthDisabled, + }, // extAuthz override on TLS host. - "GlobalExternalAuthorizationWithTLSAuthOverride": globalExternalAuthorizationWithTLSAuthOverride, + "GlobalExternalAuthorizationWithTLSAuthOverride": { + &normalGlobalExtAuthConfig, globalExternalAuthorizationWithTLSAuthOverride, + }, // extAuthz authpolicy merge for non TLS hosts. - "GlobalExternalAuthorizationWithMergedAuthPolicy": globalExternalAuthorizationWithMergedAuthPolicy, + "GlobalExternalAuthorizationWithMergedAuthPolicy": { + &normalGlobalExtAuthConfig, globalExternalAuthorizationWithMergedAuthPolicy, + }, // extAuthz authpolicy merge for TLS hosts. - "GlobalExternalAuthorizationWithMergedAuthPolicyTLS": globalExternalAuthorizationWithMergedAuthPolicyTLS, + "GlobalExternalAuthorizationWithMergedAuthPolicyTLS": { + &normalGlobalExtAuthConfig, globalExternalAuthorizationWithMergedAuthPolicyTLS, + }, // extAuthz on TLS host with Fallback Certificate enabled. - "GlobalExternalAuthorizationFilterTLSWithFallbackCertificate": globalExternalAuthorizationFilterTLSWithFallbackCertificate, + "GlobalExternalAuthorizationFilterTLSWithFallbackCertificate": { + &normalGlobalExtAuthConfig, globalExternalAuthorizationFilterTLSWithFallbackCertificate, + }, + // extAuthz authPolicy.disabled propagation + "GlobalExternalAuthorizationDisabledByDefault": { + &disabledGlobalExtAuthConfig, globalExternalAuthorizationDisabledByDefault, + }, + // extAuthz non-empty vhost authPolicy enables authorization + "GlobalExternalAuthorizationDisabledByDefaultAndEnabledOnRoute": { + &disabledGlobalExtAuthConfig, GlobalExternalAuthorizationDisabledByDefaultAndEnabledOnRoute, + }, + // extAuthz authPolicy.disabled propagation + "GlobalExternalAuthorizationDisabledByDefaultMergeAuthPolicy": { + &disabledGlobalExtAuthConfig, globalExternalAuthorizationWithMergedAuthPolicy, + }, } - for n, f := range subtests { - f := f + for n, env := range subtests { + f := env.testFunction t.Run(n, func(t *testing.T) { rh, c, done := setup(t, func(cfg *xdscache_v3.ListenerConfig) { @@ -602,20 +810,7 @@ func TestGlobalAuthorization(t *testing.T) { func(b *dag.Builder) { for _, processor := range b.Processors { if httpProxyProcessor, ok := processor.(*dag.HTTPProxyProcessor); ok { - httpProxyProcessor.GlobalExternalAuthorization = &contour_v1.AuthorizationServer{ - ExtensionServiceRef: contour_v1.ExtensionServiceReference{ - Name: "extension", - Namespace: "auth", - }, - FailOpen: false, - ResponseTimeout: defaultResponseTimeout.String(), - AuthPolicy: &contour_v1.AuthorizationPolicy{ - Context: map[string]string{ - "header_type": "root_config", - "header_1": "message_1", - }, - }, - } + httpProxyProcessor.GlobalExternalAuthorization = env.globalExtAuthConfig httpProxyProcessor.FallbackCertificate = &types.NamespacedName{ Namespace: "admin", Name: "fallbacksecret", diff --git a/internal/featuretests/v3/headerpolicy_test.go b/internal/featuretests/v3/headerpolicy_test.go index f47d32c4852..e3762e9aafc 100644 --- a/internal/featuretests/v3/headerpolicy_test.go +++ b/internal/featuretests/v3/headerpolicy_test.go @@ -184,14 +184,9 @@ func TestHeaderPolicy_ReplaceHeader_HTTProxy(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("hello.world", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }), ), envoy_v3.RouteConfiguration("https/hello.world", @@ -297,14 +292,9 @@ func TestHeaderPolicy_ReplaceHostHeader_HTTProxy(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("hello.world", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }), ), envoy_v3.RouteConfiguration("https/hello.world", diff --git a/internal/featuretests/v3/route_test.go b/internal/featuretests/v3/route_test.go index 719737dbae9..b62e1e82563 100644 --- a/internal/featuretests/v3/route_test.go +++ b/internal/featuretests/v3/route_test.go @@ -310,12 +310,14 @@ func TestEditIngressInPlace(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("hello.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/whoop"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/whoop"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -364,12 +366,14 @@ func TestEditIngressInPlace(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("hello.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/whoop"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/whoop"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -462,8 +466,9 @@ func TestSSLRedirectOverlay(t *testing.T) { Action: routecluster("nginx-ingress/challenge-service/8009/da39a3ee5e"), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/"), // match all - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/"), // match all + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), virtualhosts( @@ -707,8 +712,9 @@ func TestRDSFilter(t *testing.T) { Action: routecluster("nginx-ingress/challenge-service/8009/da39a3ee5e"), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/"), // match all - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/"), // match all + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -1126,8 +1132,9 @@ func TestRouteWithTLS(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("test2.test.com", &envoy_config_route_v3.Route{ - Action: envoy_v3.UpgradeHTTPS(), - Match: routePrefix("/a"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), + Match: routePrefix("/a"), }, ), ), @@ -1203,8 +1210,9 @@ func TestRouteWithTLS_InsecurePaths(t *testing.T) { Action: routecluster("default/kuard/80/da39a3ee5e"), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/secure"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/secure"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -1289,12 +1297,14 @@ func TestRouteWithTLS_InsecurePaths_DisablePermitInsecureTrue(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("test2.test.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/insecure"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/insecure"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/secure"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/secure"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -1482,8 +1492,9 @@ func TestHTTPProxyRouteWithTLS(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("test2.test.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/a"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/a"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -1555,8 +1566,9 @@ func TestHTTPProxyRouteWithTLS_InsecurePaths(t *testing.T) { Action: routecluster("default/kuard/80/da39a3ee5e"), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/secure"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/secure"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -1637,12 +1649,14 @@ func TestHTTPProxyRouteWithTLS_InsecurePaths_DisablePermitInsecureTrue(t *testin envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("test2.test.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/insecure"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/insecure"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, &envoy_config_route_v3.Route{ - Match: routePrefix("/secure"), - Action: envoy_v3.UpgradeHTTPS(), + Match: routePrefix("/secure"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), diff --git a/internal/featuretests/v3/tcpproxy_test.go b/internal/featuretests/v3/tcpproxy_test.go index 56c83cf3c10..cd556ee24be 100644 --- a/internal/featuretests/v3/tcpproxy_test.go +++ b/internal/featuretests/v3/tcpproxy_test.go @@ -103,14 +103,9 @@ func TestTCPProxy(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("kuard-tcp.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -276,14 +271,9 @@ func TestTCPProxyTLSPassthrough(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("kuard-tcp.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: envoy_v3.UpgradeHTTPS(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), diff --git a/internal/xdscache/v3/route_test.go b/internal/xdscache/v3/route_test.go index b043c4a5a98..fa6498e6d33 100644 --- a/internal/xdscache/v3/route_test.go +++ b/internal/xdscache/v3/route_test.go @@ -451,14 +451,9 @@ func TestRouteVisit(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("www.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -598,14 +593,9 @@ func TestRouteVisit(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("www.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -1627,14 +1617,9 @@ func TestRouteVisit(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("www.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -1895,14 +1880,9 @@ func TestRouteVisit(t *testing.T) { AllowMethods: "GET, PUT, POST", }, &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -2787,14 +2767,9 @@ func TestRouteVisit(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("www.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -2943,14 +2918,9 @@ func TestRouteVisit(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("projectcontour.io", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), envoy_v3.VirtualHost("www.example.com", @@ -2963,6 +2933,7 @@ func TestRouteVisit(t *testing.T) { }, }, }, + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -3137,18 +3108,14 @@ func TestRouteVisit(t *testing.T) { }, }, }, + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), envoy_v3.VirtualHost("www.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -3382,14 +3349,9 @@ func TestRouteVisit(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("www.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -3654,14 +3616,9 @@ func TestRouteVisit_GlobalExternalAuthorization(t *testing.T) { envoy_v3.RouteConfiguration("ingress_http", envoy_v3.VirtualHost("www.example.com", &envoy_config_route_v3.Route{ - Match: routePrefix("/"), - Action: &envoy_config_route_v3.Route_Redirect{ - Redirect: &envoy_config_route_v3.RedirectAction{ - SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ - HttpsRedirect: true, - }, - }, - }, + Match: routePrefix("/"), + Action: withRedirect(), + TypedPerFilterConfig: envoy_v3.DisabledExtAuthConfig(), }, ), ), @@ -4096,6 +4053,16 @@ func withMirrorPolicy(route *envoy_config_route_v3.Route_Route, mirror string) * return route } +func withRedirect() *envoy_config_route_v3.Route_Redirect { + return &envoy_config_route_v3.Route_Redirect{ + Redirect: &envoy_config_route_v3.RedirectAction{ + SchemeRewriteSpecifier: &envoy_config_route_v3.RedirectAction_HttpsRedirect{ + HttpsRedirect: true, + }, + }, + } +} + // buildDAGGlobalExtAuth produces a dag.DAG from the supplied objects with global external authorization configured. func buildDAGGlobalExtAuth(t *testing.T, fallbackCertificate *types.NamespacedName, objs ...any) *dag.DAG { builder := dag.Builder{