diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 8b8c67f7..9a5c311c 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -241,6 +241,18 @@ func dialHeadscaleSocket(ctx context.Context, socketPath string) (net.Conn, erro }, backoff.WithBackOff(b)) } +// clientBaseURL turns a configured CLI address into a client base URL. A bare +// host[:port] (the historical form) defaults to https; an address that already +// carries a scheme is used as-is, so an explicit http:// or https:// is honoured +// rather than doubled into https://https://... +func clientBaseURL(address string) string { + if strings.Contains(address, "://") { + return address + } + + return "https://" + address +} + // newRemoteClient builds an API client for a remote Headscale over HTTPS, // honouring insecure (skip TLS verification) and injecting the API key as a // bearer token on every request. @@ -257,7 +269,7 @@ func newRemoteClient(address, apiKey string, insecure bool) (*clientv1.ClientWit httpClient := &http.Client{Transport: transport} return clientv1.NewClientWithResponses( - "https://"+address, + clientBaseURL(address), clientv1.WithHTTPClient(httpClient), clientv1.WithRequestEditorFn(func(_ context.Context, req *http.Request) error { req.Header.Set("Authorization", "Bearer "+apiKey) diff --git a/cmd/headscale/cli/utils_test.go b/cmd/headscale/cli/utils_test.go new file mode 100644 index 00000000..845d676c --- /dev/null +++ b/cmd/headscale/cli/utils_test.go @@ -0,0 +1,35 @@ +package cli + +import "testing" + +func TestClientBaseURL(t *testing.T) { + tests := []struct { + name string + address string + want string + }{ + { + name: "bare host defaults to https", + address: "headscale.example.com:50443", + want: "https://headscale.example.com:50443", + }, + { + name: "explicit https scheme is kept", + address: "https://headscale.example.com", + want: "https://headscale.example.com", + }, + { + name: "explicit http scheme is kept", + address: "http://localhost:8080", + want: "http://localhost:8080", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := clientBaseURL(tt.address); got != tt.want { + t.Errorf("clientBaseURL(%q) = %q, want %q", tt.address, got, tt.want) + } + }) + } +}