From 09717f1111d054930ea2c85b008fb5bec6200444 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Sat, 17 Oct 2026 12:29:53 +0000 Subject: [PATCH] hscontrol, types: strip trailing slashes from server_url --- hscontrol/handlers.go | 10 ++-------- hscontrol/oidc.go | 2 +- hscontrol/types/config.go | 2 +- hscontrol/types/config_test.go | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/hscontrol/handlers.go b/hscontrol/handlers.go index 717934bf0..a4c981a22 100644 --- a/hscontrol/handlers.go +++ b/hscontrol/handlers.go @@ -8,7 +8,6 @@ import ( "io" "net/http" "strconv" - "strings" "time" "github.com/juanfont/headscale/hscontrol/assets" @@ -305,14 +304,9 @@ func NewAuthProviderWeb(serverURL string) *AuthProviderWeb { } // authPathURL builds an auth-flow URL of the form -// "//", trimming a trailing slash from serverURL. +// "//". func authPathURL(serverURL, kind string, authID types.AuthID) string { - return fmt.Sprintf( - "%s/%s/%s", - strings.TrimSuffix(serverURL, "/"), - kind, - authID.String(), - ) + return fmt.Sprintf("%s/%s/%s", serverURL, kind, authID.String()) } func (a *AuthProviderWeb) RegisterURL(authID types.AuthID) string { diff --git a/hscontrol/oidc.go b/hscontrol/oidc.go index be4421097..8ea128db8 100644 --- a/hscontrol/oidc.go +++ b/hscontrol/oidc.go @@ -128,7 +128,7 @@ func (a *AuthProviderOIDC) cookiesSecure() bool { } func oidcCallbackURL(serverURL string) string { - return strings.TrimSuffix(serverURL, "/") + "/oidc/callback" + return serverURL + "/oidc/callback" } func (a *AuthProviderOIDC) oidcCallbackPath() string { diff --git a/hscontrol/types/config.go b/hscontrol/types/config.go index 8792498d2..e67ef35e1 100644 --- a/hscontrol/types/config.go +++ b/hscontrol/types/config.go @@ -1661,7 +1661,7 @@ func LoadServerConfig() (*Config, error) { } } - serverURL := viper.GetString("server_url") + serverURL := strings.TrimRight(viper.GetString("server_url"), "/") // BaseDomain cannot be the same as the server URL. // This is because Tailscale takes over the domain in BaseDomain, diff --git a/hscontrol/types/config_test.go b/hscontrol/types/config_test.go index a5f1d2202..507649f69 100644 --- a/hscontrol/types/config_test.go +++ b/hscontrol/types/config_test.go @@ -817,6 +817,38 @@ dns: } } +func TestServerURLTrailingSlash(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + {name: "no slash", input: "https://h", want: "https://h"}, + {name: "one slash", input: "https://h/", want: "https://h"}, + {name: "two slashes", input: "https://h//", want: "https://h"}, + {name: "port", input: "https://h:8443/", want: "https://h:8443"}, + {name: "path prefix", input: "https://h/hs/", want: "https://h/hs"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + viper.Reset() + t.Cleanup(viper.Reset) + require.NoError(t, LoadConfig("testdata/minimal.yaml", true)) + + viper.Set("server_url", tt.input) + viper.Set("prefixes.v4", "100.64.0.0/10") + viper.Set("database.type", "sqlite3") + viper.Set("dns.magic_dns", false) + viper.Set("dns.override_local_dns", false) + + cfg, err := LoadServerConfig() + require.NoError(t, err) + assert.Equal(t, tt.want, cfg.ServerURL) + }) + } +} + // OK // server_url: headscale.com, base: clients.headscale.com // server_url: headscale.com, base: headscale.net