api/v2: soft-revoke auth keys with a configurable collector

Tailscale's keys API has no separate expire verb: DELETE is the revoke.
Map it to a soft revoke so the key stays retrievable as invalid afterwards
instead of vanishing, matching the SDK and Terraform's expectations.

Add a revoked timestamp to pre-auth keys (migration plus schema), mark a
key invalid once revoked, and have the keys DELETE handler stamp it rather
than destroy the row. A background collector reaps revoked keys after a
configurable retention window (preauth_keys.revoked_retention, default
168h), so the table does not grow without bound.
This commit is contained in:
Kristoffer Dalby
2026-06-20 20:16:51 +00:00
parent ff7dd807fe
commit 6b413fe237
10 changed files with 150 additions and 2 deletions
+9 -2
View File
@@ -249,9 +249,12 @@ func registerKeys(api huma.API, b Backend) {
return nil, err
}
err = b.State.DeletePreAuthKey(id)
// Tailscale's DELETE revokes the key but keeps it retrievable (invalid)
// rather than destroying it; the collector reaps it after the retention
// window.
err = b.State.RevokePreAuthKey(id)
if err != nil {
return nil, mapError("deleting auth key", err)
return nil, mapError("revoking auth key", err)
}
return &deleteKeyOutput{}, nil
@@ -330,6 +333,10 @@ func keyFromStored(pak *types.PreAuthKey) Key {
key.ExpirySeconds = expirySeconds(pak.CreatedAt, pak.Expiration)
}
if pak.Revoked != nil {
key.Revoked = pak.Revoked
}
if len(pak.Tags) == 0 && pak.User != nil {
key.UserID = pak.User.StringID()
}