Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion cmd/headscale/cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions cmd/headscale/cli/utils_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
2 changes: 1 addition & 1 deletion docs/ref/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Headscale server at `/api/v1/docs` for details.

```console
curl -H "Authorization: Bearer <API_KEY>" \
--json '{"user": "<USER>", "authId": "AUTH_ID>"}' \
--json '{"user": "<USER>", "authId": "<AUTH_ID>"}' \
https://headscale.example.com/api/v1/auth/register
```

Expand Down
2 changes: 2 additions & 0 deletions docs/ref/integration/web-ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ Headscale doesn't provide a built-in web interface but users may pick one from t
- [HeadControl](https://github.com/ahmadzip/HeadControl) - Minimal Headscale admin dashboard, built with Go and HTMX
- [Headscale Manager](https://github.com/hkdone/headscalemanager) - Headscale UI for Android
- [Headscale UI](https://github.com/MunMunMiao/headscale-ui) - Headscale UI online and Self-hosting
- [Headscale Panel](https://github.com/headscale-panel/panel) - A modern Headscale management panel with a clean,
network-operations-focused UI

You can ask for support on our [Discord server](https://discord.gg/c84AZQhmpx) in the "web-interfaces" channel.
Loading