mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-26 18:24:54 +09:00
db: accept tskey-client- prefix for OAuth client auth
The upstream tailscale client only runs its OAuth client-credentials exchange for secrets prefixed tskey-client-, so accept it as an alias for hskey-client-. The prefix is only a label sliced off before lookup, so the same stored client authenticates under either; lets the official client and GitHub Action mint auth keys against headscale.
This commit is contained in:
@@ -192,7 +192,12 @@ func (hsdb *HSDatabase) AuthenticateOAuthClient(secretStr string) (*types.OAuthC
|
||||
// used directly as an auth key; strip them before parsing.
|
||||
secretStr, _, _ = strings.Cut(secretStr, "?")
|
||||
|
||||
_, rest, found := strings.Cut(secretStr, types.OAuthClientPrefix)
|
||||
// See [types.TailscaleOAuthClientPrefix] for why the alias is accepted.
|
||||
rest, found := strings.CutPrefix(secretStr, types.OAuthClientPrefix)
|
||||
if !found {
|
||||
rest, found = strings.CutPrefix(secretStr, types.TailscaleOAuthClientPrefix)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, ErrOAuthClientFailedToParse
|
||||
}
|
||||
|
||||
@@ -86,6 +86,54 @@ func TestOAuthClientCreateAndAuthenticate(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
// TestOAuthClientAuthenticateTailscalePrefix asserts the same stored client
|
||||
// authenticates under the tskey-client- alias, and that only a leading prefix
|
||||
// is recognised.
|
||||
func TestOAuthClientAuthenticateTailscalePrefix(t *testing.T) {
|
||||
db, err := newSQLiteTestDB()
|
||||
require.NoError(t, err)
|
||||
|
||||
secret, client, err := db.CreateOAuthClient(
|
||||
[]string{"auth_keys"},
|
||||
[]string{"tag:ci"},
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
rest := strings.TrimPrefix(secret, types.OAuthClientPrefix)
|
||||
tsSecret := types.TailscaleOAuthClientPrefix + rest
|
||||
|
||||
for _, s := range []string{
|
||||
tsSecret,
|
||||
// Callers may pass the raw auth-key form; ?attributes are stripped.
|
||||
tsSecret + "?baseURL=http://127.0.0.1:8080&ephemeral=true",
|
||||
} {
|
||||
got, err := db.AuthenticateOAuthClient(s)
|
||||
require.NoError(t, err, s)
|
||||
assert.Equal(t, client.ClientID, got.ClientID)
|
||||
}
|
||||
|
||||
// A wrong secret under the alias parses but fails verification.
|
||||
_, err = db.AuthenticateOAuthClient(
|
||||
types.TailscaleOAuthClientPrefix + client.ClientID + "-" + strings.Repeat("0", 64),
|
||||
)
|
||||
require.Error(t, err)
|
||||
require.NotErrorIs(t, err, ErrOAuthClientFailedToParse)
|
||||
|
||||
for _, s := range []string{
|
||||
types.TailscaleOAuthClientPrefix,
|
||||
"tskey-auth-" + rest,
|
||||
"tskey-" + rest,
|
||||
"junk-" + tsSecret,
|
||||
"junk-" + secret,
|
||||
types.TailscaleOAuthClientPrefix + secret,
|
||||
} {
|
||||
_, err := db.AuthenticateOAuthClient(s)
|
||||
require.ErrorIs(t, err, ErrOAuthClientFailedToParse, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashSecretRoundTrip(t *testing.T) {
|
||||
const secret = "a-high-entropy-credential-secret"
|
||||
|
||||
|
||||
@@ -11,6 +11,14 @@ const (
|
||||
// hskey-client-<clientID>-<secret>.
|
||||
OAuthClientPrefix = "hskey-client-" //nolint:gosec // prefix, not a credential
|
||||
|
||||
// TailscaleOAuthClientPrefix is an accepted alias for [OAuthClientPrefix].
|
||||
// The tailscale client only runs its OAuth client-credentials exchange
|
||||
// (feature/oauthkey) for secrets with this prefix, so accepting it lets the
|
||||
// stock client and GitHub Action mint auth keys against headscale. The
|
||||
// prefix is only a label, cut before lookup; the same stored client
|
||||
// authenticates under either.
|
||||
TailscaleOAuthClientPrefix = "tskey-client-" //nolint:gosec // prefix, not a credential
|
||||
|
||||
// AccessTokenPrefix prefixes an OAuth access token:
|
||||
// hskey-oauthtok-<prefix>-<secret>. The v2 auth middleware dispatches a
|
||||
// scope-limited token from an all-access admin key on this prefix alone, so
|
||||
|
||||
Reference in New Issue
Block a user