Browse Source

cmd/containerboot: handle v6 pod ips that are missing square brackets (#18519)

This commit fixes an issue within containerboot that arose from the
kubernetes operator. When users enable metrics on custom resources that
are running on dual stack or ipv6 only clusters, they end up with an error
as we pass the hostport combintation using $(POD_IP):PORT.

In go, `netip.ParseAddrPort` expects square brackets `[]` to wrap the host
portion of an ipv6 address and would naturally, crash.

When loading the containerboot configuration from the environment we now
check if the `TS_LOCAL_ADDR_PORT` value contains the pod's v6 ip address.
If it does & does not already contain brackets, we add the brackets in.

Closes: #15762
Closes: #15467

Signed-off-by: David Bond <[email protected]>
David Bond 1 month ago
parent
commit
f2b4d7065d
2 changed files with 40 additions and 0 deletions
  1. 12 0
      cmd/containerboot/settings.go
  2. 28 0
      cmd/containerboot/settings_test.go

+ 12 - 0
cmd/containerboot/settings.go

@@ -126,6 +126,7 @@ func configFromEnv() (*settings, error) {
 		IngressProxiesCfgPath:                 defaultEnv("TS_INGRESS_PROXIES_CONFIG_PATH", ""),
 		PodUID:                                defaultEnv("POD_UID", ""),
 	}
+
 	podIPs, ok := os.LookupEnv("POD_IPS")
 	if ok {
 		ips := strings.Split(podIPs, ",")
@@ -144,6 +145,7 @@ func configFromEnv() (*settings, error) {
 			cfg.PodIPv6 = parsed.String()
 		}
 	}
+
 	// If cert share is enabled, set the replica as read or write. Only 0th
 	// replica should be able to write.
 	isInCertShareMode := defaultBool("TS_EXPERIMENTAL_CERT_SHARE", false)
@@ -165,9 +167,19 @@ func configFromEnv() (*settings, error) {
 		cfg.AcceptDNS = &acceptDNSNew
 	}
 
+	// In Kubernetes clusters, people like to use the "$(POD_IP):PORT" combination to configure the TS_LOCAL_ADDR_PORT
+	// environment variable (we even do this by default in the operator when enabling metrics), leading to a v6 address
+	// and port combo we cannot parse, as netip.ParseAddrPort expects the host segment to be enclosed in square brackets.
+	// We perform a check here to see if TS_LOCAL_ADDR_PORT is using the pod's IPv6 address and is not using brackets,
+	// adding the brackets in if need be.
+	if cfg.PodIPv6 != "" && strings.Contains(cfg.LocalAddrPort, cfg.PodIPv6) && !strings.ContainsAny(cfg.LocalAddrPort, "[]") {
+		cfg.LocalAddrPort = strings.Replace(cfg.LocalAddrPort, cfg.PodIPv6, "["+cfg.PodIPv6+"]", 1)
+	}
+
 	if err := cfg.validate(); err != nil {
 		return nil, fmt.Errorf("invalid configuration: %v", err)
 	}
+
 	return cfg, nil
 }
 

+ 28 - 0
cmd/containerboot/settings_test.go

@@ -6,6 +6,7 @@
 package main
 
 import (
+	"net/netip"
 	"strings"
 	"testing"
 )
@@ -226,3 +227,30 @@ func TestValidateAuthMethods(t *testing.T) {
 		})
 	}
 }
+
+func TestHandlesKubeIPV6(t *testing.T) {
+	t.Setenv("TS_LOCAL_ADDR_PORT", "fd7a:115c:a1e0::6c34:352:9002")
+	t.Setenv("POD_IPS", "fd7a:115c:a1e0::6c34:352")
+
+	cfg, err := configFromEnv()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if cfg.LocalAddrPort != "[fd7a:115c:a1e0::6c34:352]:9002" {
+		t.Errorf("LocalAddrPort is not set correctly")
+	}
+
+	parsed, err := netip.ParseAddrPort(cfg.LocalAddrPort)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !parsed.Addr().Is6() {
+		t.Errorf("expected v6 address but got %s", parsed)
+	}
+
+	if parsed.Port() != 9002 {
+		t.Errorf("expected port 9002 but got %d", parsed.Port())
+	}
+}