From e943f7a697ddb12c23d48d1f1b43ab559a0674b6 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Thu, 25 Aug 2022 15:49:04 -0400 Subject: [PATCH 1/4] Omit non-IP defined endpoints from clusters --- cli/cmd/proxy/read/config.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/cli/cmd/proxy/read/config.go b/cli/cmd/proxy/read/config.go index 3c1a105c0e..5ff3db8c40 100644 --- a/cli/cmd/proxy/read/config.go +++ b/cli/cmd/proxy/read/config.go @@ -6,11 +6,17 @@ import ( "fmt" "io" "net/http" + "regexp" "strings" "github.com/hashicorp/consul-k8s/cli/common" ) +const ( + ipv6RegEx = `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` + ipv4RegEx = `^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})` +) + // EnvoyConfig represents the configuration retrieved from a config dump at the // admin endpoint. It wraps the Envoy ConfigDump struct to give us convenient // access to the different sections of the config. @@ -198,8 +204,15 @@ func parseClusters(rawCfg map[string]interface{}, clusterMapping map[string][]st endpoints := make([]string, 0) for _, endpoint := range cluster.Cluster.LoadAssignment.Endpoints { for _, lbEndpoint := range endpoint.LBEndpoints { - endpoints = append(endpoints, fmt.Sprintf("%s:%d", lbEndpoint.Endpoint.Address.SocketAddress.Address, - int(lbEndpoint.Endpoint.Address.SocketAddress.PortValue))) + // Only add endpoints defined by IP addresses. + addr := lbEndpoint.Endpoint.Address.SocketAddress.Address + match, err := regexp.MatchString(ipv4RegEx+"|"+ipv6RegEx, addr) + if err != nil { + return clusters, err + } + if match { + endpoints = append(endpoints, fmt.Sprintf("%s:%d", addr, int(lbEndpoint.Endpoint.Address.SocketAddress.PortValue))) + } } } From 75e6612ac02c51d5c886f599239925c3bf2e086a Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Thu, 25 Aug 2022 17:14:09 -0400 Subject: [PATCH 2/4] Improve perf with regex --- cli/cmd/proxy/read/config.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cli/cmd/proxy/read/config.go b/cli/cmd/proxy/read/config.go index 5ff3db8c40..da1944e1ba 100644 --- a/cli/cmd/proxy/read/config.go +++ b/cli/cmd/proxy/read/config.go @@ -17,6 +17,8 @@ const ( ipv4RegEx = `^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})` ) +var isIP = regexp.MustCompile(ipv4RegEx + "|" + ipv6RegEx) + // EnvoyConfig represents the configuration retrieved from a config dump at the // admin endpoint. It wraps the Envoy ConfigDump struct to give us convenient // access to the different sections of the config. @@ -205,12 +207,7 @@ func parseClusters(rawCfg map[string]interface{}, clusterMapping map[string][]st for _, endpoint := range cluster.Cluster.LoadAssignment.Endpoints { for _, lbEndpoint := range endpoint.LBEndpoints { // Only add endpoints defined by IP addresses. - addr := lbEndpoint.Endpoint.Address.SocketAddress.Address - match, err := regexp.MatchString(ipv4RegEx+"|"+ipv6RegEx, addr) - if err != nil { - return clusters, err - } - if match { + if addr := lbEndpoint.Endpoint.Address.SocketAddress.Address; isIP.MatchString(addr) { endpoints = append(endpoints, fmt.Sprintf("%s:%d", addr, int(lbEndpoint.Endpoint.Address.SocketAddress.PortValue))) } } From f3d52bc60d181e3ff337b127f9eaf16001cf9ee5 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Fri, 26 Aug 2022 12:51:24 -0400 Subject: [PATCH 3/4] Use ParseIP instead of RegEx --- cli/cmd/proxy/read/config.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/cli/cmd/proxy/read/config.go b/cli/cmd/proxy/read/config.go index da1944e1ba..e7e6bcad34 100644 --- a/cli/cmd/proxy/read/config.go +++ b/cli/cmd/proxy/read/config.go @@ -5,20 +5,13 @@ import ( "encoding/json" "fmt" "io" + "net" "net/http" - "regexp" "strings" "github.com/hashicorp/consul-k8s/cli/common" ) -const ( - ipv6RegEx = `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` - ipv4RegEx = `^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})` -) - -var isIP = regexp.MustCompile(ipv4RegEx + "|" + ipv6RegEx) - // EnvoyConfig represents the configuration retrieved from a config dump at the // admin endpoint. It wraps the Envoy ConfigDump struct to give us convenient // access to the different sections of the config. @@ -207,7 +200,7 @@ func parseClusters(rawCfg map[string]interface{}, clusterMapping map[string][]st for _, endpoint := range cluster.Cluster.LoadAssignment.Endpoints { for _, lbEndpoint := range endpoint.LBEndpoints { // Only add endpoints defined by IP addresses. - if addr := lbEndpoint.Endpoint.Address.SocketAddress.Address; isIP.MatchString(addr) { + if addr := lbEndpoint.Endpoint.Address.SocketAddress.Address; net.ParseIP(addr) != nil { endpoints = append(endpoints, fmt.Sprintf("%s:%d", addr, int(lbEndpoint.Endpoint.Address.SocketAddress.PortValue))) } } From 932f53b666d0c6e4c01dde39f4800c8504c15301 Mon Sep 17 00:00:00 2001 From: Thomas Eckert Date: Tue, 30 Aug 2022 16:48:03 -0400 Subject: [PATCH 4/4] Add test for parseClusters --- cli/cmd/proxy/read/config_test.go | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/cli/cmd/proxy/read/config_test.go b/cli/cmd/proxy/read/config_test.go index 243523698f..6b0e425794 100644 --- a/cli/cmd/proxy/read/config_test.go +++ b/cli/cmd/proxy/read/config_test.go @@ -372,6 +372,80 @@ func TestFormatFilters(t *testing.T) { } } +// TestClusterParsingEndpoints checks that the parseClusters function correctly +// maps endpoints from the config dump and cluster mapping into a config object. +// This includes omitting endpoints that are defined by domain names as seen in +// Logical DNS clusters. +func TestClusterParsingEndpoints(t *testing.T) { + expected := []Cluster{ + { + Name: "logical_dns_cluster", + FullyQualifiedDomainName: "logical_dns_cluster.service.host", + Endpoints: []string{"192.168.18.110:20000", "192.168.52.101:20000", "192.168.65.131:20000"}, + Type: "LOGICAL_DNS", + LastUpdated: "2022-08-30T12:31:03.354Z", + }, + } + + rawCfg := map[string]interface{}{ + "dynamic_active_clusters": []map[string]interface{}{ + { + "cluster": map[string]interface{}{ + "name": "logical_dns_cluster.service.host", + "type": "LOGICAL_DNS", + "load_assignment": map[string]interface{}{ + "endpoints": []map[string]interface{}{ + { + "lb_endpoints": []map[string]interface{}{ + { + "endpoint": map[string]interface{}{ + "address": map[string]interface{}{ + "socket_address": map[string]interface{}{ + "address": "192.168.18.110", + "port_value": 20000, + }, + }, + }, + }, + { + "endpoints": map[string]interface{}{ + "address": map[string]interface{}{ + "socket_address": map[string]interface{}{ + "address": "192.168.52.101", + "port_value": 20000, + }, + }, + }, + }, + { + "endpoints": map[string]interface{}{ + "address": map[string]interface{}{ + "socket_address": map[string]interface{}{ + "address": "domain-name", + "port_value": 20000, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "last_updated": "2022-08-30T12:31:03.354Z", + }, + }, + } + clusterMapping := map[string][]string{ + "logical_dns_cluster.service.host": {"192.168.52.101:20000", "192.168.65.131:20000"}, + } + + actual, err := parseClusters(rawCfg, clusterMapping) + require.NoError(t, err) + + require.Equal(t, expected, actual) +} + type mockPortForwarder struct { openBehavior func(context.Context) (string, error) }