From ce964f8ea5b564cf9286a7c7fa89af6676456c12 Mon Sep 17 00:00:00 2001 From: freddygv Date: Wed, 17 Mar 2021 13:40:49 -0600 Subject: [PATCH] Update xds for transparent proxy --- agent/xds/clusters.go | 82 +- agent/xds/clusters_test.go | 16 + agent/xds/endpoints.go | 81 +- agent/xds/endpoints_test.go | 9 + agent/xds/listeners.go | 371 ++++++- agent/xds/listeners_test.go | 910 +++++++++--------- agent/xds/routes.go | 42 +- agent/xds/server.go | 9 + .../transparent-proxy.envoy-1-17-x.golden | 139 +++ ...sparent-proxy.v2compat.envoy-1-17-x.golden | 139 +++ .../transparent-proxy.envoy-1-17-x.golden | 169 ++++ ...sparent-proxy.v2compat.envoy-1-17-x.golden | 169 ++++ 12 files changed, 1568 insertions(+), 568 deletions(-) create mode 100644 agent/xds/testdata/clusters/transparent-proxy.envoy-1-17-x.golden create mode 100644 agent/xds/testdata/clusters/transparent-proxy.v2compat.envoy-1-17-x.golden create mode 100644 agent/xds/testdata/listeners/transparent-proxy.envoy-1-17-x.golden create mode 100644 agent/xds/testdata/listeners/transparent-proxy.v2compat.envoy-1-17-x.golden diff --git a/agent/xds/clusters.go b/agent/xds/clusters.go index bfafb4d6dd..1899ec485b 100644 --- a/agent/xds/clusters.go +++ b/agent/xds/clusters.go @@ -46,44 +46,57 @@ func (s *Server) clustersFromSnapshot(_ connectionInfo, cfgSnap *proxycfg.Config // clustersFromSnapshot returns the xDS API representation of the "clusters" // (upstreams) in the snapshot. func (s *Server) clustersFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { - // TODO(rb): this sizing is a low bound. - clusters := make([]proto.Message, 0, len(cfgSnap.Proxy.Upstreams)+1) + // This sizing is a lower bound. + clusters := make([]proto.Message, 0, len(cfgSnap.ConnectProxy.DiscoveryChain)+1) // Include the "app" cluster for the public listener appCluster, err := s.makeAppCluster(cfgSnap, LocalAppClusterName, "", cfgSnap.Proxy.LocalServicePort) if err != nil { return nil, err } - clusters = append(clusters, appCluster) - for _, u := range cfgSnap.Proxy.Upstreams { - id := u.Identifier() + // In TransparentProxy mode there needs to be a passthrough cluster for traffic going to destinations + // that aren't in Consul's catalog. + // TODO (freddy): Add cluster-wide setting that can disable this cluster and restrict traffic to catalog destinations. + if cfgSnap.Proxy.TransparentProxy { + clusters = append(clusters, &envoy_cluster_v3.Cluster{ + Name: OriginalDestinationClusterName, + ClusterDiscoveryType: &envoy_cluster_v3.Cluster_Type{ + Type: envoy_cluster_v3.Cluster_ORIGINAL_DST, + }, + LbPolicy: envoy_cluster_v3.Cluster_CLUSTER_PROVIDED, + ConnectTimeout: ptypes.DurationProto(5 * time.Second), + }) + } - if u.DestinationType == structs.UpstreamDestTypePreparedQuery { - upstreamCluster, err := s.makeUpstreamClusterForPreparedQuery(u, cfgSnap) - if err != nil { - return nil, err - } - clusters = append(clusters, upstreamCluster) - - } else { - chain := cfgSnap.ConnectProxy.DiscoveryChain[id] - chainEndpoints, ok := cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[id] - if !ok { - // this should not happen - return nil, fmt.Errorf("no endpoint map for upstream %q", id) - } - - upstreamClusters, err := s.makeUpstreamClustersForDiscoveryChain(u, chain, chainEndpoints, cfgSnap) - if err != nil { - return nil, err - } - - for _, cluster := range upstreamClusters { - clusters = append(clusters, cluster) - } + for id, chain := range cfgSnap.ConnectProxy.DiscoveryChain { + chainEndpoints, ok := cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[id] + if !ok { + // this should not happen + return nil, fmt.Errorf("no endpoint map for upstream %q", id) } + + upstreamClusters, err := s.makeUpstreamClustersForDiscoveryChain(id, cfgSnap.ConnectProxy.UpstreamConfig[id], chain, chainEndpoints, cfgSnap) + if err != nil { + return nil, err + } + + for _, cluster := range upstreamClusters { + clusters = append(clusters, cluster) + } + } + + for _, u := range cfgSnap.Proxy.Upstreams { + if u.DestinationType != structs.UpstreamDestTypePreparedQuery { + continue + } + + upstreamCluster, err := s.makeUpstreamClusterForPreparedQuery(u, cfgSnap) + if err != nil { + return nil, err + } + clusters = append(clusters, upstreamCluster) } cfgSnap.Proxy.Expose.Finalize() @@ -316,7 +329,7 @@ func (s *Server) clustersFromSnapshotIngressGateway(cfgSnap *proxycfg.ConfigSnap return nil, fmt.Errorf("no endpoint map for upstream %q", id) } - upstreamClusters, err := s.makeUpstreamClustersForDiscoveryChain(u, chain, chainEndpoints, cfgSnap) + upstreamClusters, err := s.makeUpstreamClustersForDiscoveryChain(id, &u, chain, chainEndpoints, cfgSnap) if err != nil { return nil, err } @@ -439,16 +452,21 @@ func (s *Server) makeUpstreamClusterForPreparedQuery(upstream structs.Upstream, } func (s *Server) makeUpstreamClustersForDiscoveryChain( - upstream structs.Upstream, + id string, + upstream *structs.Upstream, chain *structs.CompiledDiscoveryChain, chainEndpoints map[string]structs.CheckServiceNodes, cfgSnap *proxycfg.ConfigSnapshot, ) ([]*envoy_cluster_v3.Cluster, error) { if chain == nil { - return nil, fmt.Errorf("cannot create upstream cluster without discovery chain for %s", upstream.Identifier()) + return nil, fmt.Errorf("cannot create upstream cluster without discovery chain for %s", id) } - cfg, err := structs.ParseUpstreamConfigNoDefaults(upstream.Config) + configMap := make(map[string]interface{}) + if upstream != nil { + configMap = upstream.Config + } + cfg, err := structs.ParseUpstreamConfigNoDefaults(configMap) if err != nil { // Don't hard fail on a config typo, just warn. The parse func returns // default config if there is an error so it's safe to continue. diff --git a/agent/xds/clusters_test.go b/agent/xds/clusters_test.go index 751b2327f9..e3a7e4230f 100644 --- a/agent/xds/clusters_test.go +++ b/agent/xds/clusters_test.go @@ -67,6 +67,15 @@ func TestClustersFromSnapshot(t *testing.T) { customAppClusterJSON(t, customClusterJSONOptions{ Name: "myservice", }) + snap.ConnectProxy.UpstreamConfig = map[string]*structs.Upstream{ + "db": { + Config: map[string]interface{}{ + "envoy_cluster_json": customAppClusterJSON(t, customClusterJSONOptions{ + Name: "myservice", + }), + }, + }, + } }, }, { @@ -616,6 +625,13 @@ func TestClustersFromSnapshot(t *testing.T) { create: proxycfg.TestConfigSnapshotIngress_MultipleListenersDuplicateService, setup: nil, }, + { + name: "transparent-proxy", + create: proxycfg.TestConfigSnapshot, + setup: func(snap *proxycfg.ConfigSnapshot) { + snap.Proxy.TransparentProxy = true + }, + }, } latestEnvoyVersion := proxysupport.EnvoyVersions[0] diff --git a/agent/xds/endpoints.go b/agent/xds/endpoints.go index 64b2d49e2a..ed1c4b5170 100644 --- a/agent/xds/endpoints.go +++ b/agent/xds/endpoints.go @@ -47,45 +47,46 @@ func (s *Server) endpointsFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnaps resources := make([]proto.Message, 0, len(cfgSnap.ConnectProxy.PreparedQueryEndpoints)+len(cfgSnap.ConnectProxy.WatchedUpstreamEndpoints)) - for _, u := range cfgSnap.Proxy.Upstreams { - id := u.Identifier() - - var chain *structs.CompiledDiscoveryChain - if u.DestinationType != structs.UpstreamDestTypePreparedQuery { - chain = cfgSnap.ConnectProxy.DiscoveryChain[id] + for id, chain := range cfgSnap.ConnectProxy.DiscoveryChain { + // Prepared queries get handled in the second loop below + if conf, ok := cfgSnap.ConnectProxy.UpstreamConfig[id]; ok && conf.DestinationType == structs.UpstreamDestTypePreparedQuery { + continue } - if chain == nil { - // We ONLY want this branch for prepared queries. + es := s.endpointsFromDiscoveryChain( + id, + chain, + cfgSnap.Datacenter, + cfgSnap.ConnectProxy.UpstreamConfig[id], + cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[id], + cfgSnap.ConnectProxy.WatchedGatewayEndpoints[id], + ) + resources = append(resources, es...) + } - dc := u.Datacenter - if dc == "" { - dc = cfgSnap.Datacenter - } - clusterName := connect.UpstreamSNI(&u, "", dc, cfgSnap.Roots.TrustDomain) + // Looping over explicit upstreams is only needed for prepared queries and escape hatch clusters + for _, u := range cfgSnap.Proxy.Upstreams { + if u.DestinationType != structs.UpstreamDestTypePreparedQuery || u.Config["envoy_cluster_json"] == "" { + continue + } + id := u.Identifier() - endpoints, ok := cfgSnap.ConnectProxy.PreparedQueryEndpoints[id] - if ok { - la := makeLoadAssignment( - clusterName, - []loadAssignmentEndpointGroup{ - {Endpoints: endpoints}, - }, - cfgSnap.Datacenter, - ) - resources = append(resources, la) - } + dc := u.Datacenter + if dc == "" { + dc = cfgSnap.Datacenter + } + clusterName := connect.UpstreamSNI(&u, "", dc, cfgSnap.Roots.TrustDomain) - } else { - // Newfangled discovery chain plumbing. - es := s.endpointsFromDiscoveryChain( - u, - chain, + endpoints, ok := cfgSnap.ConnectProxy.PreparedQueryEndpoints[id] + if ok { + la := makeLoadAssignment( + clusterName, + []loadAssignmentEndpointGroup{ + {Endpoints: endpoints}, + }, cfgSnap.Datacenter, - cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[id], - cfgSnap.ConnectProxy.WatchedGatewayEndpoints[id], ) - resources = append(resources, es...) + resources = append(resources, la) } } @@ -277,9 +278,10 @@ func (s *Server) endpointsFromSnapshotIngressGateway(cfgSnap *proxycfg.ConfigSna } es := s.endpointsFromDiscoveryChain( - u, + id, cfgSnap.IngressGateway.DiscoveryChain[id], cfgSnap.Datacenter, + &u, cfgSnap.IngressGateway.WatchedUpstreamEndpoints[id], cfgSnap.IngressGateway.WatchedGatewayEndpoints[id], ) @@ -301,9 +303,10 @@ func makeEndpoint(host string, port int) *envoy_endpoint_v3.LbEndpoint { } func (s *Server) endpointsFromDiscoveryChain( - upstream structs.Upstream, + id string, chain *structs.CompiledDiscoveryChain, datacenter string, + upstream *structs.Upstream, upstreamEndpoints, gatewayEndpoints map[string]structs.CheckServiceNodes, ) []proto.Message { var resources []proto.Message @@ -312,11 +315,15 @@ func (s *Server) endpointsFromDiscoveryChain( return resources } - cfg, err := structs.ParseUpstreamConfigNoDefaults(upstream.Config) + configMap := make(map[string]interface{}) + if upstream != nil { + configMap = upstream.Config + } + cfg, err := structs.ParseUpstreamConfigNoDefaults(configMap) if err != nil { // Don't hard fail on a config typo, just warn. The parse func returns // default config if there is an error so it's safe to continue. - s.Logger.Warn("failed to parse", "upstream", upstream.Identifier(), + s.Logger.Warn("failed to parse", "upstream", id, "error", err) } @@ -331,7 +338,7 @@ func (s *Server) endpointsFromDiscoveryChain( } } else { s.Logger.Warn("ignoring escape hatch setting, because a discovery chain is configued for", - "discovery chain", chain.ServiceName, "upstream", upstream.Identifier(), + "discovery chain", chain.ServiceName, "upstream", id, "envoy_cluster_json", chain.ServiceName) } } diff --git a/agent/xds/endpoints_test.go b/agent/xds/endpoints_test.go index b1df337266..d85eb3aac3 100644 --- a/agent/xds/endpoints_test.go +++ b/agent/xds/endpoints_test.go @@ -317,6 +317,15 @@ func TestEndpointsFromSnapshot(t *testing.T) { customAppClusterJSON(t, customClusterJSONOptions{ Name: "myservice", }) + snap.ConnectProxy.UpstreamConfig = map[string]*structs.Upstream{ + "db": { + Config: map[string]interface{}{ + "envoy_cluster_json": customAppClusterJSON(t, customClusterJSONOptions{ + Name: "myservice", + }), + }, + }, + } }, }, { diff --git a/agent/xds/listeners.go b/agent/xds/listeners.go index 3230fee004..5b223306bb 100644 --- a/agent/xds/listeners.go +++ b/agent/xds/listeners.go @@ -32,6 +32,10 @@ import ( "github.com/hashicorp/consul/logging" ) +const ( + TProxyOutboundPort = 15001 +) + // listenersFromSnapshot returns the xDS API representation of the "listeners" in the snapshot. func (s *Server) listenersFromSnapshot(cInfo connectionInfo, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { if cfgSnap == nil { @@ -54,8 +58,7 @@ func (s *Server) listenersFromSnapshot(cInfo connectionInfo, cfgSnap *proxycfg.C // listenersFromSnapshotConnectProxy returns the "listeners" for a connect proxy service func (s *Server) listenersFromSnapshotConnectProxy(cInfo connectionInfo, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { - // One listener for each upstream plus the public one - resources := make([]proto.Message, len(cfgSnap.Proxy.Upstreams)+1) + resources := make([]proto.Message, 1) var err error @@ -64,18 +67,77 @@ func (s *Server) listenersFromSnapshotConnectProxy(cInfo connectionInfo, cfgSnap if err != nil { return nil, err } - for i, u := range cfgSnap.Proxy.Upstreams { - id := u.Identifier() - var chain *structs.CompiledDiscoveryChain - if u.DestinationType != structs.UpstreamDestTypePreparedQuery { - chain = cfgSnap.ConnectProxy.DiscoveryChain[id] + // This outboundListener is exclusively used when TransparentProxy mode is active. + // In that situation there is a single listener where we are redirecting outbound traffic, + // and each upstream gets a filter chain attached to that listener. + var outboundListener *envoy_listener_v3.Listener + + if cfgSnap.Proxy.TransparentProxy { + outboundListener = makeListener(OutboundListenerName, "127.0.0.1", TProxyOutboundPort, envoy_core_v3.TrafficDirection_OUTBOUND) + outboundListener.FilterChains = make([]*envoy_listener_v3.FilterChain, 0) + outboundListener.ListenerFilters = []*envoy_listener_v3.ListenerFilter{ + { + // TODO (freddy): Hard-coded until we upgrade the go-control-plane library + Name: "envoy.filters.listener.original_dst", + }, + } + } + + var hasChains bool + + for id, chain := range cfgSnap.ConnectProxy.DiscoveryChain { + upstreamCfg := cfgSnap.ConnectProxy.UpstreamConfig[id] + if upstreamCfg != nil && upstreamCfg.DestinationType == structs.UpstreamDestTypePreparedQuery { + continue + } + cfg := getAndModifyUpstreamConfigForListener(s.Logger, id, upstreamCfg, chain) + + // If escape hatch is present, create a listener from it and move on to the next + if cfg.ListenerJSON != "" { + upstreamListener, err := makeListenerFromUserConfig(cfg.ListenerJSON) + if err != nil { + return nil, err + } + resources = append(resources, upstreamListener) + continue } - var upstreamListener proto.Message - upstreamListener, err = s.makeUpstreamListenerForDiscoveryChain( - &u, - u.LocalBindAddress, + // Generate the upstream listeners for when they are explicitly set with a local bind port + if outboundListener == nil || (upstreamCfg != nil && upstreamCfg.LocalBindPort != 0) { + address := "127.0.0.1" + if upstreamCfg.LocalBindAddress != "" { + address = upstreamCfg.LocalBindAddress + } + + filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain( + id, + "", + cfg.Protocol, + upstreamCfg, + chain, + cfgSnap, + nil, + ) + if err != nil { + return nil, err + } + + upstreamListener := makeListener(id, address, upstreamCfg.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND) + upstreamListener.FilterChains = []*envoy_listener_v3.FilterChain{ + filterChain, + } + resources = append(resources, upstreamListener) + + // Avoid creating filter chains below for upstreams that have dedicated listeners + continue + } + + filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain( + id, + "", + cfg.Protocol, + upstreamCfg, chain, cfgSnap, nil, @@ -83,7 +145,141 @@ func (s *Server) listenersFromSnapshotConnectProxy(cInfo connectionInfo, cfgSnap if err != nil { return nil, err } - resources[i+1] = upstreamListener + + // For filter chains used by the transparent proxy, we need to match on multiple destination addresses. + // These might be: the ClusterIP in k8s, or any of the service instance addresses. + endpoints := cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[id] + uniqueAddrs := make(map[string]struct{}) + + for _, t := range chain.Targets { + var k8sNamespace string + + // Store all the IP addresses per unique port + for _, e := range endpoints[t.ID] { + addr, _ := e.BestAddress(false) + + if _, ok := uniqueAddrs[addr]; !ok { + uniqueAddrs[addr] = struct{}{} + } + + // The k8s namespace should be the same for all instances, so pick any + if ns, ok := e.Service.Meta["k8s-namespace"]; ok { + k8sNamespace = ns + } + } + + // TODO (freddy) hack to remove for beta: for every potential discovery chain target, resolve the k8s ClusterIP + // since it's not stored in Consul's catalog (yet) + if k8sNamespace != "" { + host := fmt.Sprintf("%s.%s.svc.cluster.local", t.Service, k8sNamespace) + resolved, err := net.LookupHost(host) + if err != nil { + // We still have the Pod ips in the catalog, so don't hard-fail on errors + s.Logger.Warn("failed to resolve", "host", host, "error", err) + continue + } + for _, addr := range resolved { + if _, ok := uniqueAddrs[addr]; !ok { + uniqueAddrs[addr] = struct{}{} + } + } + } + } + + // For every potential address we collected, create the appropriate address prefix to match on. + // In this case we are matching on exact addresses, so the prefix is the address itself, + // and the prefix length is based on whether it's IPv4 or IPv6. + ranges := make([]*envoy_core_v3.CidrRange, 0) + + for addr := range uniqueAddrs { + ip := net.ParseIP(addr) + if ip == nil { + continue + } + + pfxLen := uint32(32) + if ip.To4() == nil { + pfxLen = 128 + } + ranges = append(ranges, &envoy_core_v3.CidrRange{ + AddressPrefix: addr, + PrefixLen: &wrappers.UInt32Value{Value: pfxLen}, + }) + } + filterChain.FilterChainMatch = &envoy_listener_v3.FilterChainMatch{ + PrefixRanges: ranges, + } + + // Only attach the filter chain if there are addresses to match on + if len(ranges) > 0 { + outboundListener.FilterChains = append(outboundListener.FilterChains, filterChain) + } + hasChains = true + } + + // Only create the outbound listener when there are upstreams and filter chains are present + if outboundListener != nil && hasChains { + // Filter chains are stable sorted to avoid draining if the list is provided out of order + sort.SliceStable(outboundListener.FilterChains, func(i, j int) bool { + return outboundListener.FilterChains[i].Name < outboundListener.FilterChains[j].Name + }) + + // Add a catch-all filter chain that acts as a TCP proxy to non-catalog destinations + filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain( + "passthrough", + OriginalDestinationClusterName, + "tcp", + nil, + nil, + cfgSnap, + nil, + ) + if err != nil { + return nil, err + } + outboundListener.FilterChains = append(outboundListener.FilterChains, filterChain) + + resources = append(resources, outboundListener) + } + + for id, u := range cfgSnap.ConnectProxy.UpstreamConfig { + if _, ok := cfgSnap.ConnectProxy.DiscoveryChain[id]; ok && u.DestinationType != structs.UpstreamDestTypePreparedQuery { + // This upstream is already covered above + continue + } + + cfg, err := structs.ParseUpstreamConfig(u.Config) + if err != nil { + // Don't hard fail on a config typo, just warn. The parse func returns + // default config if there is an error so it's safe to continue. + s.Logger.Warn("failed to parse", "upstream", u.Identifier(), "error", err) + } + address := "127.0.0.1" + if u.LocalBindAddress != "" { + address = u.LocalBindAddress + } + // This is the case where upstream config is centralized but no port was specified + if u.LocalBindPort == 0 { + continue + } + upstreamListener := makeListener(id, address, u.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND) + + filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain( + id, + "", + cfg.Protocol, + u, + nil, + cfgSnap, + nil, + ) + if err != nil { + return nil, err + } + upstreamListener.FilterChains = []*envoy_listener_v3.FilterChain{ + filterChain, + } + resources = append(resources, upstreamListener) } cfgSnap.Proxy.Expose.Finalize() @@ -532,25 +728,27 @@ func (s *Server) makeInboundListener(cInfo connectionInfo, cfgSnap *proxycfg.Con return l, nil } - // No user config, use default listener + // No user config, use default listener address + // Default to listening on all addresses, but override with bind address if one is set. addr := cfgSnap.Address - - // Override with bind address if one is set, otherwise default - // to 0.0.0.0 + if addr == "" { + addr = "0.0.0.0" + } if cfg.BindAddress != "" { addr = cfg.BindAddress - } else if addr == "" { - addr = "0.0.0.0" } // Override with bind port if one is set, otherwise default to // proxy service's address port := cfgSnap.Port + // if cfgSnap.Proxy.TransparentProxy { + // port = TProxyInboundPort + // } if cfg.BindPort != 0 { port = cfg.BindPort } - l = makeListener(PublicListenerName, addr, port, envoy_core_v3.TrafficDirection_INBOUND) + l = makeListener(name, addr, port, envoy_core_v3.TrafficDirection_INBOUND) filterOpts := listenerFilterOpts{ protocol: cfg.Protocol, @@ -994,6 +1192,109 @@ func (s *Server) makeMeshGatewayListener(name, addr string, port int, cfgSnap *p return l, nil } +func (s *Server) makeUpstreamFilterChainForDiscoveryChain( + id string, + overrideCluster string, + protocol string, + u *structs.Upstream, + chain *structs.CompiledDiscoveryChain, + cfgSnap *proxycfg.ConfigSnapshot, + tlsContext *envoy_tls_v3.DownstreamTlsContext, +) (*envoy_listener_v3.FilterChain, error) { + // TODO (freddy) Make this actually legible + useRDS := true + + var ( + clusterName string + destination, datacenter, namespace string + ) + + if chain != nil { + destination, datacenter, namespace = chain.ServiceName, chain.Datacenter, chain.Namespace + } + if (chain == nil || chain.IsDefault()) && u != nil { + useRDS = false + + if datacenter == "" { + datacenter = u.Datacenter + } + if datacenter == "" { + datacenter = cfgSnap.Datacenter + } + if destination == "" { + destination = u.DestinationName + } + if namespace == "" { + namespace = u.DestinationNamespace + } + + sni := connect.UpstreamSNI(u, "", datacenter, cfgSnap.Roots.TrustDomain) + clusterName = CustomizeClusterName(sni, chain) + + } else { + if protocol == "tcp" && chain != nil { + useRDS = false + + startNode := chain.Nodes[chain.StartNode] + if startNode == nil { + return nil, fmt.Errorf("missing first node in compiled discovery chain for: %s", chain.ServiceName) + } + if startNode.Type != structs.DiscoveryGraphNodeTypeResolver { + return nil, fmt.Errorf("unexpected first node in discovery chain using protocol=%q: %s", protocol, startNode.Type) + } + targetID := startNode.Resolver.Target + target := chain.Targets[targetID] + + clusterName = CustomizeClusterName(target.Name, chain) + } + } + + // Default the namespace to match how SNIs are generated + if namespace == "" { + namespace = structs.IntentionDefaultNamespace + } + + filterName := fmt.Sprintf("%s.%s.%s", destination, namespace, datacenter) + if u != nil && u.DestinationType == structs.UpstreamDestTypePreparedQuery { + // Avoid encoding dc and namespace for prepared queries. + // Those are defined in the query itself and are not available here. + filterName = id + } + if overrideCluster != "" { + useRDS = false + clusterName = overrideCluster + filterName = overrideCluster + } + + opts := listenerFilterOpts{ + useRDS: useRDS, + protocol: protocol, + filterName: filterName, + routeName: id, + cluster: clusterName, + statPrefix: "upstream.", + routePath: "", + ingressGateway: false, + httpAuthzFilter: nil, + } + filter, err := makeListenerFilter(opts) + if err != nil { + return nil, err + } + transportSocket, err := makeDownstreamTLSTransportSocket(tlsContext) + if err != nil { + return nil, err + } + + return &envoy_listener_v3.FilterChain{ + Filters: []*envoy_listener_v3.Filter{ + filter, + }, + TransportSocket: transportSocket, + }, nil +} + +// TODO(freddy) Replace in favor of new function above. Currently in use for ingress gateways. func (s *Server) makeUpstreamListenerForDiscoveryChain( u *structs.Upstream, address string, @@ -1007,7 +1308,7 @@ func (s *Server) makeUpstreamListenerForDiscoveryChain( upstreamID := u.Identifier() l := makeListener(upstreamID, address, u.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND) - cfg := getAndModifyUpstreamConfigForListener(s.Logger, u, chain) + cfg := getAndModifyUpstreamConfigForListener(s.Logger, upstreamID, u, chain) if cfg.ListenerJSON != "" { return makeListenerFromUserConfig(cfg.ListenerJSON) } @@ -1092,48 +1393,51 @@ func (s *Server) makeUpstreamListenerForDiscoveryChain( return l, nil } -func getAndModifyUpstreamConfigForListener(logger hclog.Logger, u *structs.Upstream, chain *structs.CompiledDiscoveryChain) structs.UpstreamConfig { +func getAndModifyUpstreamConfigForListener(logger hclog.Logger, id string, u *structs.Upstream, chain *structs.CompiledDiscoveryChain) structs.UpstreamConfig { var ( cfg structs.UpstreamConfig err error ) + configMap := make(map[string]interface{}) + if u != nil { + configMap = u.Config + } if chain == nil || chain.IsDefault() { - cfg, err = structs.ParseUpstreamConfig(u.Config) + cfg, err = structs.ParseUpstreamConfig(configMap) if err != nil { // Don't hard fail on a config typo, just warn. The parse func returns // default config if there is an error so it's safe to continue. - logger.Warn("failed to parse", "upstream", u.Identifier(), "error", err) + logger.Warn("failed to parse", "upstream", id, "error", err) } } else { // Use NoDefaults here so that we can set the protocol to the chain // protocol if necessary - cfg, err = structs.ParseUpstreamConfigNoDefaults(u.Config) + cfg, err = structs.ParseUpstreamConfigNoDefaults(configMap) if err != nil { // Don't hard fail on a config typo, just warn. The parse func returns // default config if there is an error so it's safe to continue. - logger.Warn("failed to parse", "upstream", u.Identifier(), "error", err) + logger.Warn("failed to parse", "upstream", id, "error", err) } if cfg.ListenerJSON != "" { logger.Warn("ignoring escape hatch setting because already configured for", - "discovery chain", chain.ServiceName, "upstream", u.Identifier(), "config", "envoy_listener_json") + "discovery chain", chain.ServiceName, "upstream", id, "config", "envoy_listener_json") // Remove from config struct so we don't use it later on cfg.ListenerJSON = "" } - proto := cfg.Protocol - if proto == "" { - proto = chain.Protocol + protocol := cfg.Protocol + if protocol == "" { + protocol = chain.Protocol } - - if proto == "" { - proto = "tcp" + if protocol == "" { + protocol = "tcp" } // set back on the config so that we can use it from return value - cfg.Protocol = proto + cfg.Protocol = protocol } return cfg @@ -1148,6 +1452,7 @@ type listenerFilterOpts struct { statPrefix string routePath string requestTimeoutMs *int + ingressGateway bool httpAuthzFilter *envoy_http_v3.HttpFilter } diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index d68a9074ea..23517d15d4 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -2,22 +2,19 @@ package xds import ( "bytes" - "path/filepath" - "sort" - "testing" - "text/template" - "time" - envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" - - testinf "github.com/mitchellh/go-testing-interface" - "github.com/stretchr/testify/require" - + "github.com/hashicorp/consul/agent/connect" + "github.com/hashicorp/consul/agent/consul/discoverychain" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/xds/proxysupport" "github.com/hashicorp/consul/sdk/testutil" - "github.com/hashicorp/consul/types" + testinf "github.com/mitchellh/go-testing-interface" + "github.com/stretchr/testify/require" + "path/filepath" + "sort" + "testing" + "text/template" ) func TestListenersFromSnapshot(t *testing.T) { @@ -35,447 +32,478 @@ func TestListenersFromSnapshot(t *testing.T) { overrideGoldenName string serverSetup func(*Server) }{ + // { + // name: "defaults", + // create: proxycfg.TestConfigSnapshot, + // setup: nil, // Default snapshot + // }, + // { + // name: "listener-bind-address", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["bind_address"] = "127.0.0.2" + // }, + // }, + // { + // name: "listener-bind-port", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["bind_port"] = 8888 + // }, + // }, + // { + // name: "listener-bind-address-port", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["bind_address"] = "127.0.0.2" + // snap.Proxy.Config["bind_port"] = 8888 + // }, + // }, + // { + // name: "http-public-listener", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["protocol"] = "http" + // }, + // }, + // { + // name: "http-listener-with-timeouts", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["protocol"] = "http" + // snap.Proxy.Config["local_connect_timeout_ms"] = 1234 + // snap.Proxy.Config["local_request_timeout_ms"] = 2345 + // }, + // }, + // { + // name: "http-upstream", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Upstreams[0].Config["protocol"] = "http" + // }, + // }, + // { + // name: "custom-public-listener", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["envoy_public_listener_json"] = + // customListenerJSON(t, customListenerJSONOptions{ + // Name: "custom-public-listen", + // }) + // }, + // }, + // { + // name: "custom-public-listener-http", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["protocol"] = "http" + // snap.Proxy.Config["envoy_public_listener_json"] = + // customHTTPListenerJSON(t, customHTTPListenerJSONOptions{ + // Name: "custom-public-listen", + // }) + // }, + // }, + // { + // name: "custom-public-listener-http-2", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["protocol"] = "http" + // snap.Proxy.Config["envoy_public_listener_json"] = + // customHTTPListenerJSON(t, customHTTPListenerJSONOptions{ + // Name: "custom-public-listen", + // HTTPConnectionManagerName: httpConnectionManagerNewName, + // }) + // }, + // }, + // { + // name: "custom-public-listener-http-missing", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["protocol"] = "http" + // snap.Proxy.Config["envoy_public_listener_json"] = + // customListenerJSON(t, customListenerJSONOptions{ + // Name: "custom-public-listen", + // }) + // }, + // }, + // { + // name: "custom-public-listener-ignores-tls", + // create: proxycfg.TestConfigSnapshot, + // overrideGoldenName: "custom-public-listener", // should be the same + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config["envoy_public_listener_json"] = + // customListenerJSON(t, customListenerJSONOptions{ + // Name: "custom-public-listen", + // // Attempt to override the TLS context should be ignored + // TLSContext: `"allowRenegotiation": false`, + // }) + // }, + // }, + // { + // name: "custom-upstream", + // create: proxycfg.TestConfigSnapshot, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Upstreams[0].Config["envoy_listener_json"] = + // customListenerJSON(t, customListenerJSONOptions{ + // Name: "custom-upstream", + // }) + // }, + // }, + // { + // name: "custom-upstream-ignored-with-disco-chain", + // create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Upstreams[0].Config["envoy_listener_json"] = + // customListenerJSON(t, customListenerJSONOptions{ + // Name: "custom-upstream", + // }) + // }, + // }, + // { + // name: "splitter-with-resolver-redirect", + // create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, + // setup: nil, + // }, + // { + // name: "connect-proxy-with-tcp-chain", + // create: proxycfg.TestConfigSnapshotDiscoveryChain, + // setup: nil, + // }, + // { + // name: "connect-proxy-with-http-chain", + // create: func(t testinf.T) *proxycfg.ConfigSnapshot { + // return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + // &structs.ProxyConfigEntry{ + // Kind: structs.ProxyDefaults, + // Name: structs.ProxyConfigGlobal, + // Config: map[string]interface{}{ + // "protocol": "http", + // }, + // }, + // ) + // }, + // setup: nil, + // }, + // { + // name: "connect-proxy-with-http2-chain", + // create: func(t testinf.T) *proxycfg.ConfigSnapshot { + // return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + // &structs.ProxyConfigEntry{ + // Kind: structs.ProxyDefaults, + // Name: structs.ProxyConfigGlobal, + // Config: map[string]interface{}{ + // "protocol": "http2", + // }, + // }, + // ) + // }, + // setup: nil, + // }, + // { + // name: "connect-proxy-with-grpc-chain", + // create: func(t testinf.T) *proxycfg.ConfigSnapshot { + // return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, + // &structs.ProxyConfigEntry{ + // Kind: structs.ProxyDefaults, + // Name: structs.ProxyConfigGlobal, + // Config: map[string]interface{}{ + // "protocol": "grpc", + // }, + // }, + // ) + // }, + // setup: nil, + // }, + // { + // name: "connect-proxy-with-chain-external-sni", + // create: proxycfg.TestConfigSnapshotDiscoveryChainExternalSNI, + // setup: nil, + // }, + // { + // name: "connect-proxy-with-chain-and-overrides", + // create: proxycfg.TestConfigSnapshotDiscoveryChainWithOverrides, + // setup: nil, + // }, + // { + // name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway", + // create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGateway, + // setup: nil, + // }, + // { + // name: "connect-proxy-with-tcp-chain-failover-through-local-gateway", + // create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGateway, + // setup: nil, + // }, + // { + // name: "expose-paths-local-app-paths", + // create: proxycfg.TestConfigSnapshotExposeConfig, + // }, + // { + // name: "expose-paths-new-cluster-http2", + // create: proxycfg.TestConfigSnapshotExposeConfig, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Expose.Paths[1] = structs.ExposePath{ + // LocalPathPort: 9090, + // Path: "/grpc.health.v1.Health/Check", + // ListenerPort: 21501, + // Protocol: "http2", + // } + // }, + // }, + // { + // // NOTE: if IPv6 is not supported in the kernel per + // // kernelSupportsIPv6() then this test will fail because the golden + // // files were generated assuming ipv6 support was present + // name: "expose-checks", + // create: proxycfg.TestConfigSnapshotExposeConfig, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Expose = structs.ExposeConfig{ + // Checks: true, + // } + // }, + // serverSetup: func(s *Server) { + // s.CfgFetcher = configFetcherFunc(func() string { + // return "192.0.2.1" + // }) + // + // s.CheckFetcher = httpCheckFetcherFunc(func(sid structs.ServiceID) []structs.CheckType { + // if sid != structs.NewServiceID("web", nil) { + // return nil + // } + // return []structs.CheckType{{ + // CheckID: types.CheckID("http"), + // Name: "http", + // HTTP: "http://127.0.0.1:8181/debug", + // ProxyHTTP: "http://:21500/debug", + // Method: "GET", + // Interval: 10 * time.Second, + // Timeout: 1 * time.Second, + // }} + // }) + // }, + // }, + // { + // name: "mesh-gateway", + // create: proxycfg.TestConfigSnapshotMeshGateway, + // }, + // { + // name: "mesh-gateway-using-federation-states", + // create: proxycfg.TestConfigSnapshotMeshGatewayUsingFederationStates, + // }, + // { + // name: "mesh-gateway-no-services", + // create: proxycfg.TestConfigSnapshotMeshGatewayNoServices, + // }, + // { + // name: "mesh-gateway-tagged-addresses", + // create: proxycfg.TestConfigSnapshotMeshGateway, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config = map[string]interface{}{ + // "envoy_mesh_gateway_no_default_bind": true, + // "envoy_mesh_gateway_bind_tagged_addresses": true, + // } + // }, + // }, + // { + // name: "mesh-gateway-custom-addresses", + // create: proxycfg.TestConfigSnapshotMeshGateway, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config = map[string]interface{}{ + // "envoy_mesh_gateway_bind_addresses": map[string]structs.ServiceAddress{ + // "foo": { + // Address: "198.17.2.3", + // Port: 8080, + // }, + // "bar": { + // Address: "2001:db8::ff", + // Port: 9999, + // }, + // "baz": { + // Address: "127.0.0.1", + // Port: 8765, + // }, + // }, + // } + // }, + // }, + // { + // name: "ingress-gateway", + // create: proxycfg.TestConfigSnapshotIngressGateway, + // setup: nil, + // }, + // { + // name: "ingress-gateway-bind-addrs", + // create: proxycfg.TestConfigSnapshotIngressGateway, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.TaggedAddresses = map[string]structs.ServiceAddress{ + // "lan": {Address: "10.0.0.1"}, + // "wan": {Address: "172.16.0.1"}, + // } + // snap.Proxy.Config = map[string]interface{}{ + // "envoy_gateway_no_default_bind": true, + // "envoy_gateway_bind_tagged_addresses": true, + // "envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{ + // "foo": {Address: "8.8.8.8"}, + // }, + // } + // }, + // }, + // { + // name: "ingress-gateway-no-services", + // create: proxycfg.TestConfigSnapshotIngressGatewayNoServices, + // setup: nil, + // }, + // { + // name: "ingress-with-chain-external-sni", + // create: proxycfg.TestConfigSnapshotIngressExternalSNI, + // setup: nil, + // }, + // { + // name: "ingress-with-chain-and-overrides", + // create: proxycfg.TestConfigSnapshotIngressWithOverrides, + // setup: nil, + // }, + // { + // name: "ingress-with-tcp-chain-failover-through-remote-gateway", + // create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughRemoteGateway, + // setup: nil, + // }, + // { + // name: "ingress-with-tcp-chain-failover-through-local-gateway", + // create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughLocalGateway, + // setup: nil, + // }, + // { + // name: "ingress-splitter-with-resolver-redirect", + // create: proxycfg.TestConfigSnapshotIngress_SplitterWithResolverRedirectMultiDC, + // setup: nil, + // }, + // { + // name: "terminating-gateway", + // create: proxycfg.TestConfigSnapshotTerminatingGateway, + // setup: nil, + // }, + // { + // name: "terminating-gateway-no-services", + // create: proxycfg.TestConfigSnapshotTerminatingGatewayNoServices, + // setup: nil, + // }, + // { + // name: "terminating-gateway-custom-and-tagged-addresses", + // create: proxycfg.TestConfigSnapshotTerminatingGateway, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.Proxy.Config = map[string]interface{}{ + // "envoy_gateway_no_default_bind": true, + // "envoy_gateway_bind_tagged_addresses": true, + // "envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{ + // // This bind address should not get a listener due to deduplication and it sorts to the end + // "z-duplicate-of-tagged-wan-addr": { + // Address: "198.18.0.1", + // Port: 443, + // }, + // "foo": { + // Address: "198.17.2.3", + // Port: 8080, + // }, + // }, + // } + // }, + // }, + // { + // name: "terminating-gateway-service-subsets", + // create: proxycfg.TestConfigSnapshotTerminatingGateway, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ + // structs.NewServiceName("web", nil): { + // Kind: structs.ServiceResolver, + // Name: "web", + // Subsets: map[string]structs.ServiceResolverSubset{ + // "v1": { + // Filter: "Service.Meta.version == 1", + // }, + // "v2": { + // Filter: "Service.Meta.version == 2", + // OnlyPassing: true, + // }, + // }, + // }, + // } + // snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("web", nil)] = &structs.ServiceConfigResponse{ + // ProxyConfig: map[string]interface{}{"protocol": "http"}, + // } + // }, + // }, + // { + // name: "ingress-http-multiple-services", + // create: proxycfg.TestConfigSnapshotIngress_HTTPMultipleServices, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ + // {Protocol: "http", Port: 8080}: { + // { + // DestinationName: "foo", + // LocalBindPort: 8080, + // }, + // { + // DestinationName: "bar", + // LocalBindPort: 8080, + // }, + // }, + // {Protocol: "http", Port: 443}: { + // { + // DestinationName: "baz", + // LocalBindPort: 443, + // }, + // { + // DestinationName: "qux", + // LocalBindPort: 443, + // }, + // }, + // } + // }, + // }, + // { + // name: "terminating-gateway-no-api-cert", + // create: proxycfg.TestConfigSnapshotTerminatingGateway, + // setup: func(snap *proxycfg.ConfigSnapshot) { + // snap.TerminatingGateway.ServiceLeaves[structs.NewServiceName("api", nil)] = nil + // }, + // }, + // { + // name: "ingress-with-tls-listener", + // create: proxycfg.TestConfigSnapshotIngressWithTLSListener, + // setup: nil, + // }, { - name: "defaults", - create: proxycfg.TestConfigSnapshot, - setup: nil, // Default snapshot - }, - { - name: "listener-bind-address", + name: "transparent-proxy", create: proxycfg.TestConfigSnapshot, setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["bind_address"] = "127.0.0.2" - }, - }, - { - name: "listener-bind-port", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["bind_port"] = 8888 - }, - }, - { - name: "listener-bind-address-port", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["bind_address"] = "127.0.0.2" - snap.Proxy.Config["bind_port"] = 8888 - }, - }, - { - name: "http-public-listener", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - }, - }, - { - name: "http-listener-with-timeouts", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - snap.Proxy.Config["local_connect_timeout_ms"] = 1234 - snap.Proxy.Config["local_request_timeout_ms"] = 2345 - }, - }, - { - name: "http-upstream", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].Config["protocol"] = "http" - }, - }, - { - name: "custom-public-listener", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["envoy_public_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: "custom-public-listen", - }) - }, - }, - { - name: "custom-public-listener-http", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - snap.Proxy.Config["envoy_public_listener_json"] = - customHTTPListenerJSON(t, customHTTPListenerJSONOptions{ - Name: "custom-public-listen", - }) - }, - }, - { - name: "custom-public-listener-http-2", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - snap.Proxy.Config["envoy_public_listener_json"] = - customHTTPListenerJSON(t, customHTTPListenerJSONOptions{ - Name: "custom-public-listen", - HTTPConnectionManagerName: httpConnectionManagerNewName, - }) - }, - }, - { - name: "custom-public-listener-http-missing", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["protocol"] = "http" - snap.Proxy.Config["envoy_public_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: "custom-public-listen", - }) - }, - }, - { - name: "custom-public-listener-ignores-tls", - create: proxycfg.TestConfigSnapshot, - overrideGoldenName: "custom-public-listener", // should be the same - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config["envoy_public_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: "custom-public-listen", - // Attempt to override the TLS context should be ignored - TLSContext: `"allowRenegotiation": false`, - }) - }, - }, - { - name: "custom-upstream", - create: proxycfg.TestConfigSnapshot, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].Config["envoy_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: "custom-upstream", - }) - }, - }, - { - name: "custom-upstream-ignored-with-disco-chain", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Upstreams[0].Config["envoy_listener_json"] = - customListenerJSON(t, customListenerJSONOptions{ - Name: "custom-upstream", - }) - }, - }, - { - name: "splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain", - create: proxycfg.TestConfigSnapshotDiscoveryChain, - setup: nil, - }, - { - name: "connect-proxy-with-http-chain", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http", - }, - }, - ) - }, - setup: nil, - }, - { - name: "connect-proxy-with-http2-chain", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "http2", - }, - }, - ) - }, - setup: nil, - }, - { - name: "connect-proxy-with-grpc-chain", - create: func(t testinf.T) *proxycfg.ConfigSnapshot { - return proxycfg.TestConfigSnapshotDiscoveryChainWithEntries(t, - &structs.ProxyConfigEntry{ - Kind: structs.ProxyDefaults, - Name: structs.ProxyConfigGlobal, - Config: map[string]interface{}{ - "protocol": "grpc", - }, - }, - ) - }, - setup: nil, - }, - { - name: "connect-proxy-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotDiscoveryChainExternalSNI, - setup: nil, - }, - { - name: "connect-proxy-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithOverrides, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughRemoteGateway, - setup: nil, - }, - { - name: "connect-proxy-with-tcp-chain-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailoverThroughLocalGateway, - setup: nil, - }, - { - name: "expose-paths-local-app-paths", - create: proxycfg.TestConfigSnapshotExposeConfig, - }, - { - name: "expose-paths-new-cluster-http2", - create: proxycfg.TestConfigSnapshotExposeConfig, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Expose.Paths[1] = structs.ExposePath{ - LocalPathPort: 9090, - Path: "/grpc.health.v1.Health/Check", - ListenerPort: 21501, - Protocol: "http2", - } - }, - }, - { - // NOTE: if IPv6 is not supported in the kernel per - // kernelSupportsIPv6() then this test will fail because the golden - // files were generated assuming ipv6 support was present - name: "expose-checks", - create: proxycfg.TestConfigSnapshotExposeConfig, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Expose = structs.ExposeConfig{ - Checks: true, - } - }, - serverSetup: func(s *Server) { - s.CfgFetcher = configFetcherFunc(func() string { - return "192.0.2.1" - }) + snap.Proxy.TransparentProxy = true - s.CheckFetcher = httpCheckFetcherFunc(func(sid structs.ServiceID) []structs.CheckType { - if sid != structs.NewServiceID("web", nil) { - return nil - } - return []structs.CheckType{{ - CheckID: types.CheckID("http"), - Name: "http", - HTTP: "http://127.0.0.1:8181/debug", - ProxyHTTP: "http://:21500/debug", - Method: "GET", - Interval: 10 * time.Second, - Timeout: 1 * time.Second, - }} - }) - }, - }, - { - name: "mesh-gateway", - create: proxycfg.TestConfigSnapshotMeshGateway, - }, - { - name: "mesh-gateway-using-federation-states", - create: proxycfg.TestConfigSnapshotMeshGatewayUsingFederationStates, - }, - { - name: "mesh-gateway-no-services", - create: proxycfg.TestConfigSnapshotMeshGatewayNoServices, - }, - { - name: "mesh-gateway-tagged-addresses", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config = map[string]interface{}{ - "envoy_mesh_gateway_no_default_bind": true, - "envoy_mesh_gateway_bind_tagged_addresses": true, - } - }, - }, - { - name: "mesh-gateway-custom-addresses", - create: proxycfg.TestConfigSnapshotMeshGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config = map[string]interface{}{ - "envoy_mesh_gateway_bind_addresses": map[string]structs.ServiceAddress{ - "foo": { - Address: "198.17.2.3", - Port: 8080, - }, - "bar": { - Address: "2001:db8::ff", - Port: 9999, - }, - "baz": { - Address: "127.0.0.1", - Port: 8765, - }, - }, - } - }, - }, - { - name: "ingress-gateway", - create: proxycfg.TestConfigSnapshotIngressGateway, - setup: nil, - }, - { - name: "ingress-gateway-bind-addrs", - create: proxycfg.TestConfigSnapshotIngressGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TaggedAddresses = map[string]structs.ServiceAddress{ - "lan": {Address: "10.0.0.1"}, - "wan": {Address: "172.16.0.1"}, - } - snap.Proxy.Config = map[string]interface{}{ - "envoy_gateway_no_default_bind": true, - "envoy_gateway_bind_tagged_addresses": true, - "envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{ - "foo": {Address: "8.8.8.8"}, - }, - } - }, - }, - { - name: "ingress-gateway-no-services", - create: proxycfg.TestConfigSnapshotIngressGatewayNoServices, - setup: nil, - }, - { - name: "ingress-with-chain-external-sni", - create: proxycfg.TestConfigSnapshotIngressExternalSNI, - setup: nil, - }, - { - name: "ingress-with-chain-and-overrides", - create: proxycfg.TestConfigSnapshotIngressWithOverrides, - setup: nil, - }, - { - name: "ingress-with-tcp-chain-failover-through-remote-gateway", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughRemoteGateway, - setup: nil, - }, - { - name: "ingress-with-tcp-chain-failover-through-local-gateway", - create: proxycfg.TestConfigSnapshotIngressWithFailoverThroughLocalGateway, - setup: nil, - }, - { - name: "ingress-splitter-with-resolver-redirect", - create: proxycfg.TestConfigSnapshotIngress_SplitterWithResolverRedirectMultiDC, - setup: nil, - }, - { - name: "terminating-gateway", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: nil, - }, - { - name: "terminating-gateway-no-services", - create: proxycfg.TestConfigSnapshotTerminatingGatewayNoServices, - setup: nil, - }, - { - name: "terminating-gateway-custom-and-tagged-addresses", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.Proxy.Config = map[string]interface{}{ - "envoy_gateway_no_default_bind": true, - "envoy_gateway_bind_tagged_addresses": true, - "envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{ - // This bind address should not get a listener due to deduplication and it sorts to the end - "z-duplicate-of-tagged-wan-addr": { - Address: "198.18.0.1", - Port: 443, - }, - "foo": { - Address: "198.17.2.3", - Port: 8080, - }, - }, - } - }, - }, - { - name: "terminating-gateway-service-subsets", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceResolvers = map[structs.ServiceName]*structs.ServiceResolverConfigEntry{ - structs.NewServiceName("web", nil): { - Kind: structs.ServiceResolver, - Name: "web", - Subsets: map[string]structs.ServiceResolverSubset{ - "v1": { - Filter: "Service.Meta.version == 1", + // DiscoveryChain without an UpstreamConfig should yield a filter chain when in TransparentProxy mode + snap.ConnectProxy.DiscoveryChain["google"] = discoverychain.TestCompileConfigEntries( + t, "google", "default", "dc1", + connect.TestClusterID+".consul", "dc1", nil) + snap.ConnectProxy.WatchedUpstreamEndpoints["google"] = map[string]structs.CheckServiceNodes{ + "google.default.dc1": { + structs.CheckServiceNode{ + Node: &structs.Node{ + Address: "8.8.8.8", + Datacenter: "dc1", }, - "v2": { - Filter: "Service.Meta.version == 2", - OnlyPassing: true, + Service: &structs.NodeService{ + Service: "google", + Port: 9090, }, }, }, } - snap.TerminatingGateway.ServiceConfigs[structs.NewServiceName("web", nil)] = &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{"protocol": "http"}, - } + + // DiscoveryChains without endpoints do not get a filter chain because there are no addresses to match on. + snap.ConnectProxy.DiscoveryChain["no-endpoints"] = discoverychain.TestCompileConfigEntries( + t, "no-endpoints", "default", "dc1", + connect.TestClusterID+".consul", "dc1", nil) }, }, - { - name: "ingress-http-multiple-services", - create: proxycfg.TestConfigSnapshotIngress_HTTPMultipleServices, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{ - {Protocol: "http", Port: 8080}: { - { - DestinationName: "foo", - LocalBindPort: 8080, - }, - { - DestinationName: "bar", - LocalBindPort: 8080, - }, - }, - {Protocol: "http", Port: 443}: { - { - DestinationName: "baz", - LocalBindPort: 443, - }, - { - DestinationName: "qux", - LocalBindPort: 443, - }, - }, - } - }, - }, - { - name: "terminating-gateway-no-api-cert", - create: proxycfg.TestConfigSnapshotTerminatingGateway, - setup: func(snap *proxycfg.ConfigSnapshot) { - snap.TerminatingGateway.ServiceLeaves[structs.NewServiceName("api", nil)] = nil - }, - }, - { - name: "ingress-with-tls-listener", - create: proxycfg.TestConfigSnapshotIngressWithTLSListener, - setup: nil, - }, } latestEnvoyVersion := proxysupport.EnvoyVersions[0] diff --git a/agent/xds/routes.go b/agent/xds/routes.go index acc71a4781..6530b9fb4f 100644 --- a/agent/xds/routes.go +++ b/agent/xds/routes.go @@ -28,7 +28,7 @@ func (s *Server) routesFromSnapshot(cInfo connectionInfo, cfgSnap *proxycfg.Conf switch cfgSnap.Kind { case structs.ServiceKindConnectProxy: - return routesForConnectProxy(cInfo, cfgSnap.Proxy.Upstreams, cfgSnap.ConnectProxy.DiscoveryChain) + return routesForConnectProxy(cInfo, cfgSnap.ConnectProxy.DiscoveryChain) case structs.ServiceKindIngressGateway: return routesForIngressGateway(cInfo, cfgSnap.IngressGateway.Upstreams, cfgSnap.IngressGateway.DiscoveryChain) case structs.ServiceKindTerminatingGateway: @@ -42,37 +42,29 @@ func (s *Server) routesFromSnapshot(cInfo connectionInfo, cfgSnap *proxycfg.Conf // "routes" in the snapshot. func routesForConnectProxy( cInfo connectionInfo, - upstreams structs.Upstreams, chains map[string]*structs.CompiledDiscoveryChain, ) ([]proto.Message, error) { var resources []proto.Message - for _, u := range upstreams { - upstreamID := u.Identifier() - - var chain *structs.CompiledDiscoveryChain - if u.DestinationType != structs.UpstreamDestTypePreparedQuery { - chain = chains[upstreamID] + for id, chain := range chains { + if chain.IsDefault() { + continue } - if chain == nil || chain.IsDefault() { - // TODO(rb): make this do the old school stuff too - } else { - virtualHost, err := makeUpstreamRouteForDiscoveryChain(cInfo, upstreamID, chain, []string{"*"}) - if err != nil { - return nil, err - } - - route := &envoy_route_v3.RouteConfiguration{ - Name: upstreamID, - VirtualHosts: []*envoy_route_v3.VirtualHost{virtualHost}, - // ValidateClusters defaults to true when defined statically and false - // when done via RDS. Re-set the sane value of true to prevent - // null-routing traffic. - ValidateClusters: makeBoolValue(true), - } - resources = append(resources, route) + virtualHost, err := makeUpstreamRouteForDiscoveryChain(cInfo, id, chain, []string{"*"}) + if err != nil { + return nil, err } + + route := &envoy_route_v3.RouteConfiguration{ + Name: id, + VirtualHosts: []*envoy_route_v3.VirtualHost{virtualHost}, + // ValidateClusters defaults to true when defined statically and false + // when done via RDS. Re-set the sane value of true to prevent + // null-routing traffic. + ValidateClusters: makeBoolValue(true), + } + resources = append(resources, route) } // TODO(rb): make sure we don't generate an empty result diff --git a/agent/xds/server.go b/agent/xds/server.go index 672e77b509..bce5d9df4a 100644 --- a/agent/xds/server.go +++ b/agent/xds/server.go @@ -52,6 +52,9 @@ const ( // PublicListenerName is the name we give the public listener in Envoy config. PublicListenerName = "public_listener" + // OutboundListenerName is the name we give the outbound Envoy listener when TransparentProxy mode is enabled. + OutboundListenerName = "outbound_listener" + // LocalAppClusterName is the name we give the local application "cluster" in // Envoy config. Note that all cluster names may collide with service names // since we want cluster names and service names to match to enable nice @@ -80,6 +83,12 @@ const ( // services named "local_agent" in the future. LocalAgentClusterName = "local_agent" + // OriginalDestinationClusterName is the name we give to the passthrough + // cluster which redirects transparently-proxied requests to their original + // destination. This cluster prevents Consul from blocking connections to + // destinations outside of the catalog when in TransparentProxy mode. + OriginalDestinationClusterName = "original-destination" + // DefaultAuthCheckFrequency is the default value for // Server.AuthCheckFrequency to use when the zero value is provided. DefaultAuthCheckFrequency = 5 * time.Minute diff --git a/agent/xds/testdata/clusters/transparent-proxy.envoy-1-17-x.golden b/agent/xds/testdata/clusters/transparent-proxy.envoy-1-17-x.golden new file mode 100644 index 0000000000..84e92ea308 --- /dev/null +++ b/agent/xds/testdata/clusters/transparent-proxy.envoy-1-17-x.golden @@ -0,0 +1,139 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "original-destination", + "type": "ORIGINAL_DST", + "connectTimeout": "5s", + "lbPolicy": "CLUSTER_PROVIDED" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/clusters/transparent-proxy.v2compat.envoy-1-17-x.golden b/agent/xds/testdata/clusters/transparent-proxy.v2compat.envoy-1-17-x.golden new file mode 100644 index 0000000000..110582676c --- /dev/null +++ b/agent/xds/testdata/clusters/transparent-proxy.v2compat.envoy-1-17-x.golden @@ -0,0 +1,139 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V2" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V2" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 8080 + } + } + } + } + ] + } + ] + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "original-destination", + "type": "ORIGINAL_DST", + "connectTimeout": "5s", + "lbPolicy": "CLUSTER_PROVIDED" + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/transparent-proxy.envoy-1-17-x.golden b/agent/xds/testdata/listeners/transparent-proxy.envoy-1-17-x.golden new file mode 100644 index 0000000000..15538eeb6d --- /dev/null +++ b/agent/xds/testdata/listeners/transparent-proxy.envoy-1-17-x.golden @@ -0,0 +1,169 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "outbound_listener:127.0.0.1:15001", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 15001 + } + }, + "filterChains": [ + { + "filterChainMatch": { + "prefixRanges": [ + { + "addressPrefix": "8.8.8.8", + "prefixLen": 32 + } + ] + }, + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.google.default.dc1", + "cluster": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + }, + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.original-destination", + "cluster": "original-destination" + } + } + ] + } + ], + "listenerFilters": [ + { + "name": "envoy.filters.listener.original_dst" + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC", + "rules": { + + }, + "statPrefix": "connect_authz" + } + }, + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "public_listener", + "cluster": "local_app" + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/transparent-proxy.v2compat.envoy-1-17-x.golden b/agent/xds/testdata/listeners/transparent-proxy.v2compat.envoy-1-17-x.golden new file mode 100644 index 0000000000..22c49ff164 --- /dev/null +++ b/agent/xds/testdata/listeners/transparent-proxy.v2compat.envoy-1-17-x.golden @@ -0,0 +1,169 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", + "statPrefix": "upstream.db.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "outbound_listener:127.0.0.1:15001", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 15001 + } + }, + "filterChains": [ + { + "filterChainMatch": { + "prefixRanges": [ + { + "addressPrefix": "8.8.8.8", + "prefixLen": 32 + } + ] + }, + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", + "statPrefix": "upstream.google.default.dc1", + "cluster": "google.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + }, + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", + "statPrefix": "upstream.original-destination", + "cluster": "original-destination" + } + } + ] + } + ], + "listenerFilters": [ + { + "name": "envoy.filters.listener.original_dst" + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.rbac.v2.RBAC", + "rules": { + + }, + "statPrefix": "connect_authz" + } + }, + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", + "statPrefix": "public_listener", + "cluster": "local_app" + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.api.v2.auth.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Listener", + "nonce": "00000001" +} \ No newline at end of file