Skip to content

pkg/commands: normalizeEndpoint produces malformed URL for IPv6 endpoints #155

@lexfrei

Description

@lexfrei

Summary

normalizeEndpoint() in pkg/commands/talosctl_wrapper.go:225-239 produces a malformed URL for IPv6 inputs. Square brackets that delimit the host literal in URI authority syntax (RFC 3986, Go net/url) are dropped, leaving an unparseable https://2001:db8::1:6443 instead of the canonical https://[2001:db8::1]:6443.

Reproduction

got := normalizeEndpoint("[2001:db8::1]:6443")
// got == "https://2001:db8::1:6443"   <- malformed

url.Parse(got) rejects this URL: per Go's net/url documentation, when the host is an IPv6 address it must be enclosed in square brackets. Any HTTP client or kubeconfig dialer that re-parses the value (e.g. through clientcmd.LoadFromFile followed by a real connect attempt) will fail.

Root cause

// pkg/commands/talosctl_wrapper.go:225-239
func normalizeEndpoint(endpoint string) string {
    endpoint = strings.TrimPrefix(endpoint, "https://")
    endpoint = strings.TrimPrefix(endpoint, "http://")
    host, _, err := net.SplitHostPort(endpoint)
    if err != nil {
        host = endpoint
    }
    return fmt.Sprintf("https://%s:6443", host)   // <-- no bracket re-add
}

net.SplitHostPort strips the brackets from [2001:db8::1]:6443, returning host == "2001:db8::1". The final fmt.Sprintf does NOT re-add them, producing the malformed string.

Suggested fix

Use net.JoinHostPort for the assembly step — it adds brackets back automatically when the host contains a colon:

return "https://" + net.JoinHostPort(host, "6443")

This is the recommended pattern in net/url documentation and works correctly for both IPv4 and IPv6 inputs.

Notes

Discovered by CodeRabbit on PR #154 while reviewing the contract test that pinned the current (broken) output. The contract test on test/chart-contract keeps the broken case with a FIXME pointing at this issue, so the test can be tightened once the fix lands.

Impact

updateKubeconfigServer() calls normalizeEndpoint() to rewrite the server: field of a managed kubeconfig. An operator running a single-node Talos on an IPv6 cluster endpoint would end up with a kubeconfig that kubectl cannot parse. Likely undiscovered today only because the IPv6-only Talos install path is uncommon in the user base.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/commandsIssues or PRs related to pkg/commands (CLI subcommands, flag parsing, root detection)kind/bugCategorizes issue or PR as related to a bugpriority/important-soonMust be staffed and worked on either currently, or very soon, ideally in time for the next releasetriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions