diff --git a/changelogs/unreleased/6558-eflores-minor.md b/changelogs/unreleased/6558-eflores-minor.md new file mode 100644 index 00000000000..82b01f10282 --- /dev/null +++ b/changelogs/unreleased/6558-eflores-minor.md @@ -0,0 +1,3 @@ +## Fallback Certificate: Add Global Ext Auth support + +Applies Global Auth filters to Fallback certificate diff --git a/internal/featuretests/v3/envoy.go b/internal/featuretests/v3/envoy.go index 6d3187a66bd..b95c9074c0d 100644 --- a/internal/featuretests/v3/envoy.go +++ b/internal/featuretests/v3/envoy.go @@ -460,6 +460,34 @@ func filterchaintlsfallback(fallbackSecret *core_v1.Secret, peerValidationContex ) } +// filterchaintlsfallbackauthz does same thing as filterchaintlsfallback but inserts a +// `ext_authz` filter with the specified configuration into the filter chain. +func filterchaintlsfallbackauthz(fallbackSecret *core_v1.Secret, authz *envoy_filter_http_ext_authz_v3.ExtAuthz, peerValidationContext *dag.PeerValidationContext, alpn ...string) *envoy_config_listener_v3.FilterChain { + return envoy_v3.FilterChainTLSFallback( + envoy_v3.DownstreamTLSContext( + &dag.Secret{Object: fallbackSecret}, + envoy_transport_socket_tls_v3.TlsParameters_TLSv1_2, + envoy_transport_socket_tls_v3.TlsParameters_TLSv1_3, + nil, + peerValidationContext, + alpn...), + envoy_v3.Filters( + envoy_v3.HTTPConnectionManagerBuilder(). + DefaultFilters(). + AddFilter(&envoy_filter_network_http_connection_manager_v3.HttpFilter{ + Name: envoy_v3.ExtAuthzFilterName, + ConfigType: &envoy_filter_network_http_connection_manager_v3.HttpFilter_TypedConfig{ + TypedConfig: protobuf.MustMarshalAny(authz), + }, + }). + RouteConfigName(xdscache_v3.ENVOY_FALLBACK_ROUTECONFIG). + MetricsPrefix(xdscache_v3.ENVOY_HTTPS_LISTENER). + AccessLoggers(envoy_v3.FileAccessLogEnvoy("/dev/stdout", "", nil, contour_v1alpha1.LogLevelInfo)). + Get(), + ), + ) +} + func httpsFilterFor(vhost string) *envoy_config_listener_v3.Filter { return envoy_v3.HTTPConnectionManagerBuilder(). AddFilter(envoy_v3.FilterMisdirectedRequests(vhost)). diff --git a/internal/featuretests/v3/global_authorization_test.go b/internal/featuretests/v3/global_authorization_test.go index dbafac189e1..ae45402d44e 100644 --- a/internal/featuretests/v3/global_authorization_test.go +++ b/internal/featuretests/v3/global_authorization_test.go @@ -14,6 +14,7 @@ package v3 import ( + "k8s.io/apimachinery/pkg/types" "testing" envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" @@ -470,6 +471,99 @@ func globalExternalAuthorizationWithTLSAuthOverride(t *testing.T, rh ResourceEve }).Status(p).IsValid() } +func globalExternalAuthorizationFilterTLSWithFallbackCertificate(t *testing.T, rh ResourceEventHandlerWrapper, c *Contour) { + p := fixture.NewProxy("TLSProxy"). + WithFQDN("foo.com"). + WithSpec(contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "foo.com", + TLS: &contour_v1.TLS{ + SecretName: "certificate", + EnableFallbackCertificate: true, + }, + }, + Routes: []contour_v1.Route{ + { + Services: []contour_v1.Service{ + { + Name: "s1", + Port: 80, + }, + }, + }, + }, + }) + + rh.OnAdd(p) + + // Add Fallback Certificate Secret + fallbackSecret := featuretests.TLSSecret(t, "admin/fallbacksecret", &featuretests.ServerCertificate) + rh.OnAdd(fallbackSecret) + + // Add Fallback Cert Delegation + certDelegationAll := &contour_v1.TLSCertificateDelegation{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "fallbackcertdelegation", + Namespace: "admin", + }, + Spec: contour_v1.TLSCertificateDelegationSpec{ + Delegations: []contour_v1.CertificateDelegation{{ + SecretName: "fallbacksecret", + TargetNamespaces: []string{"*"}, + }}, + }, + } + + rh.OnAdd(certDelegationAll) + + httpListener := defaultHTTPListener() + httpListener.FilterChains = envoy_v3.FilterChains(getGlobalExtAuthHCM()) + + httpsListener := &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("foo.com", + featuretests.TLSSecret(t, "certificate", &featuretests.ServerCertificate), + 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"), + filterchaintlsfallbackauthz(fallbackSecret, + &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(), + } + + c.Request(listenerType).Equals(&envoy_service_discovery_v3.DiscoveryResponse{ + TypeUrl: listenerType, + Resources: resources(t, + httpListener, + httpsListener, + statsListener()), + }).Status(p).IsValid() +} + func TestGlobalAuthorization(t *testing.T) { subtests := map[string]func(*testing.T, ResourceEventHandlerWrapper, *Contour){ // Default extAuthz on non TLS host. @@ -484,6 +578,8 @@ func TestGlobalAuthorization(t *testing.T) { "GlobalExternalAuthorizationWithMergedAuthPolicy": globalExternalAuthorizationWithMergedAuthPolicy, // extAuthz authpolicy merge for TLS hosts. "GlobalExternalAuthorizationWithMergedAuthPolicyTLS": globalExternalAuthorizationWithMergedAuthPolicyTLS, + // extAuthz on TLS host with Fallback Certificate enabled. + "GlobalExternalAuthorizationFilterTLSWithFallbackCertificate": globalExternalAuthorizationFilterTLSWithFallbackCertificate, } for n, f := range subtests { @@ -520,6 +616,10 @@ func TestGlobalAuthorization(t *testing.T) { }, }, } + httpProxyProcessor.FallbackCertificate = &types.NamespacedName{ + Namespace: "admin", + Name: "fallbacksecret", + } } } }) diff --git a/internal/xdscache/v3/listener.go b/internal/xdscache/v3/listener.go index 975c0654743..6176fdcaad0 100644 --- a/internal/xdscache/v3/listener.go +++ b/internal/xdscache/v3/listener.go @@ -543,8 +543,14 @@ func (c *ListenerCache) OnChange(root *dag.DAG) { alpnProtos..., ) + var authzFilter *envoy_filter_network_http_connection_manager_v3.HttpFilter + if vh.ExternalAuthorization != nil { + authzFilter = envoy_v3.FilterExternalAuthz(vh.ExternalAuthorization) + } + cm := envoy_v3.HTTPConnectionManagerBuilder(). DefaultFilters(). + AddFilter(authzFilter). RouteConfigName(fallbackCertRouteConfigName(listener)). MetricsPrefix(listener.Name). AccessLoggers(cfg.newSecureAccessLog()).