From 259a5c6ae5aab964cb15c781244a64429bd745e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:04:03 +0000 Subject: [PATCH 1/3] Initial plan From 9f8dbeded167d1cd1ac03ded43cb26cd70e70c55 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:10:04 +0000 Subject: [PATCH 2/3] Fix: Replace 0.0.0.0 with 127.0.0.1 in kubeconfig server URL When generating the kubeconfig, if the API host is 0.0.0.0 (the default), replace it with 127.0.0.1 since 0.0.0.0 means "bind to all interfaces" but is not a valid address for clients to connect to. This fixes issues with tools like Headlamp that cannot connect to the cluster when the server URL uses 0.0.0.0. Fixes #1503 Co-authored-by: iwilltry42 <25345277+iwilltry42@users.noreply.github.com> --- pkg/client/kubeconfig.go | 4 ++ pkg/client/kubeconfig_test.go | 83 +++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 pkg/client/kubeconfig_test.go diff --git a/pkg/client/kubeconfig.go b/pkg/client/kubeconfig.go index 3eb503f425..ab0e33d712 100644 --- a/pkg/client/kubeconfig.go +++ b/pkg/client/kubeconfig.go @@ -162,6 +162,10 @@ func KubeconfigGet(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.C } // update the server URL + // Replace 0.0.0.0 with 127.0.0.1 in the kubeconfig, as 0.0.0.0 is not a valid address for clients to connect to + if APIHost == k3d.DefaultAPIHost { + APIHost = "127.0.0.1" + } kc.Clusters["default"].Server = fmt.Sprintf("https://%s:%s", APIHost, APIPort) // rename user from default to admin diff --git a/pkg/client/kubeconfig_test.go b/pkg/client/kubeconfig_test.go new file mode 100644 index 0000000000..5ecaf71ce5 --- /dev/null +++ b/pkg/client/kubeconfig_test.go @@ -0,0 +1,83 @@ +/* +Copyright © 2020-2023 The k3d Author(s) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package client + +import ( + "testing" + + k3d "github.com/k3d-io/k3d/v5/pkg/types" +) + +func TestAPIHostReplacement(t *testing.T) { + tests := []struct { + name string + inputHost string + expectedHost string + shouldBeChanged bool + }{ + { + name: "0.0.0.0 should be replaced with 127.0.0.1", + inputHost: "0.0.0.0", + expectedHost: "127.0.0.1", + shouldBeChanged: true, + }, + { + name: "127.0.0.1 should remain unchanged", + inputHost: "127.0.0.1", + expectedHost: "127.0.0.1", + shouldBeChanged: false, + }, + { + name: "localhost should remain unchanged", + inputHost: "localhost", + expectedHost: "localhost", + shouldBeChanged: false, + }, + { + name: "custom IP should remain unchanged", + inputHost: "192.168.1.100", + expectedHost: "192.168.1.100", + shouldBeChanged: false, + }, + { + name: "hostname should remain unchanged", + inputHost: "my-server.example.com", + expectedHost: "my-server.example.com", + shouldBeChanged: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + apiHost := tt.inputHost + // This is the logic from KubeconfigGet that replaces 0.0.0.0 with 127.0.0.1 + if apiHost == k3d.DefaultAPIHost { + apiHost = "127.0.0.1" + } + + if apiHost != tt.expectedHost { + t.Errorf("Expected API host to be '%s', but got '%s'", tt.expectedHost, apiHost) + } + }) + } +} From 1ba9ac3f4232ba9a12f3b240278bbbb929bca2f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:36:17 +0000 Subject: [PATCH 3/3] Change DefaultAPIHost from 0.0.0.0 to 127.0.0.1 Per reviewer feedback, changed the DefaultAPIHost constant from 0.0.0.0 to 127.0.0.1 instead of adding a runtime check. Also added checks for both the constant value and "0.0.0.0" in places where users might explicitly provide 0.0.0.0 for backward compatibility. Changes: - pkg/types/defaults.go: Changed DefaultAPIHost to 127.0.0.1 - pkg/client/registry.go: Added check for both DefaultAPIHost and 0.0.0.0 - pkg/client/cluster.go: Added check for both DefaultAPIHost and 0.0.0.0 - pkg/client/kubeconfig.go: Removed runtime replacement logic - pkg/client/kubeconfig_test.go: Updated test to verify constant value Co-authored-by: iwilltry42 <25345277+iwilltry42@users.noreply.github.com> --- pkg/client/cluster.go | 2 +- pkg/client/kubeconfig.go | 4 --- pkg/client/kubeconfig_test.go | 57 ++++------------------------------- pkg/client/registry.go | 4 +-- pkg/types/defaults.go | 2 +- 5 files changed, 10 insertions(+), 59 deletions(-) diff --git a/pkg/client/cluster.go b/pkg/client/cluster.go index d21b6f07ae..aa721ba49d 100644 --- a/pkg/client/cluster.go +++ b/pkg/client/cluster.go @@ -386,7 +386,7 @@ ClusterCreatOpts: /* * Docker Machine Special Configuration */ - if cluster.KubeAPI.Host == k3d.DefaultAPIHost && runtime == k3drt.Docker { + if (cluster.KubeAPI.Host == k3d.DefaultAPIHost || cluster.KubeAPI.Host == "0.0.0.0") && runtime == k3drt.Docker { // If the runtime is docker, attempt to use the docker host if runtime == k3drt.Docker { dockerHost := runtime.GetHost() diff --git a/pkg/client/kubeconfig.go b/pkg/client/kubeconfig.go index ab0e33d712..3eb503f425 100644 --- a/pkg/client/kubeconfig.go +++ b/pkg/client/kubeconfig.go @@ -162,10 +162,6 @@ func KubeconfigGet(ctx context.Context, runtime runtimes.Runtime, cluster *k3d.C } // update the server URL - // Replace 0.0.0.0 with 127.0.0.1 in the kubeconfig, as 0.0.0.0 is not a valid address for clients to connect to - if APIHost == k3d.DefaultAPIHost { - APIHost = "127.0.0.1" - } kc.Clusters["default"].Server = fmt.Sprintf("https://%s:%s", APIHost, APIPort) // rename user from default to admin diff --git a/pkg/client/kubeconfig_test.go b/pkg/client/kubeconfig_test.go index 5ecaf71ce5..aacca6fbdc 100644 --- a/pkg/client/kubeconfig_test.go +++ b/pkg/client/kubeconfig_test.go @@ -28,56 +28,11 @@ import ( k3d "github.com/k3d-io/k3d/v5/pkg/types" ) -func TestAPIHostReplacement(t *testing.T) { - tests := []struct { - name string - inputHost string - expectedHost string - shouldBeChanged bool - }{ - { - name: "0.0.0.0 should be replaced with 127.0.0.1", - inputHost: "0.0.0.0", - expectedHost: "127.0.0.1", - shouldBeChanged: true, - }, - { - name: "127.0.0.1 should remain unchanged", - inputHost: "127.0.0.1", - expectedHost: "127.0.0.1", - shouldBeChanged: false, - }, - { - name: "localhost should remain unchanged", - inputHost: "localhost", - expectedHost: "localhost", - shouldBeChanged: false, - }, - { - name: "custom IP should remain unchanged", - inputHost: "192.168.1.100", - expectedHost: "192.168.1.100", - shouldBeChanged: false, - }, - { - name: "hostname should remain unchanged", - inputHost: "my-server.example.com", - expectedHost: "my-server.example.com", - shouldBeChanged: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - apiHost := tt.inputHost - // This is the logic from KubeconfigGet that replaces 0.0.0.0 with 127.0.0.1 - if apiHost == k3d.DefaultAPIHost { - apiHost = "127.0.0.1" - } - - if apiHost != tt.expectedHost { - t.Errorf("Expected API host to be '%s', but got '%s'", tt.expectedHost, apiHost) - } - }) +func TestDefaultAPIHostIsLoopback(t *testing.T) { + // Verify that DefaultAPIHost is set to 127.0.0.1 (loopback) instead of 0.0.0.0 + // This ensures that kubeconfig files use a valid client-connectable address + expectedHost := "127.0.0.1" + if k3d.DefaultAPIHost != expectedHost { + t.Errorf("DefaultAPIHost should be '%s' for client connectivity, but got '%s'", expectedHost, k3d.DefaultAPIHost) } } diff --git a/pkg/client/registry.go b/pkg/client/registry.go index 9bbff86e53..18398d95d6 100644 --- a/pkg/client/registry.go +++ b/pkg/client/registry.go @@ -312,7 +312,7 @@ func RegistryGenerateLocalRegistryHostingConfigMapYAML(ctx context.Context, runt } // if the host is now 0.0.0.0, check if we can set it to the IP of the docker-machine, if it's used - if host == k3d.DefaultAPIHost && runtime == runtimes.Docker { + if (host == k3d.DefaultAPIHost || host == "0.0.0.0") && runtime == runtimes.Docker { if gort.GOOS == "windows" || gort.GOOS == "darwin" { l.Log().Tracef("Running on %s: checking if it's using docker-machine", gort.GOOS) machineIP, err := runtime.(docker.Docker).GetDockerMachineIP() @@ -328,7 +328,7 @@ func RegistryGenerateLocalRegistryHostingConfigMapYAML(ctx context.Context, runt } // if host is still 0.0.0.0, use localhost instead - if host == k3d.DefaultAPIHost { + if host == k3d.DefaultAPIHost || host == "0.0.0.0" { host = "localhost" // we prefer localhost over 0.0.0.0 } diff --git a/pkg/types/defaults.go b/pkg/types/defaults.go index e343b3ef3e..0f058b7fd1 100644 --- a/pkg/types/defaults.go +++ b/pkg/types/defaults.go @@ -83,7 +83,7 @@ const DefaultKubeconfigPrefix = DefaultObjectNamePrefix + "-kubeconfig" const DefaultAPIPort = "6443" // DefaultAPIHost defines the default host (IP) for the Kubernetes API -const DefaultAPIHost = "0.0.0.0" +const DefaultAPIHost = "127.0.0.1" // GetDefaultObjectName prefixes the passed name with the default prefix func GetDefaultObjectName(name string) string {