-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Token revocation refactor #4512
Changes from 13 commits
5474ec0
d9edff1
4498426
a882e21
4889825
2115361
5095373
77ae4f0
87dfdb3
8024726
5a1441d
397f313
0383875
8d6ab91
a39597e
c107e0c
4455e38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -561,18 +561,33 @@ func (m *ExpirationManager) RevokeByToken(te *TokenEntry) error { | |
defer metrics.MeasureSince([]string{"expire", "revoke-by-token"}, time.Now()) | ||
|
||
// Lookup the leases | ||
existing, err := m.lookupByToken(te.ID) | ||
existing, err := m.lookupLeasesByToken(te.ID) | ||
if err != nil { | ||
return errwrap.Wrapf("failed to scan for leases: {{err}}", err) | ||
} | ||
|
||
// Revoke all the keys | ||
for idx, leaseID := range existing { | ||
if err := m.revokeCommon(leaseID, false, false); err != nil { | ||
return errwrap.Wrapf(fmt.Sprintf("failed to revoke %q (%d / %d): {{err}}", leaseID, idx+1, len(existing)), err) | ||
for _, leaseID := range existing { | ||
// Load the entry | ||
le, err := m.loadEntry(leaseID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// If there's a lease, set expiration to now, persist, and call | ||
// updatePending to hand off revocation to the expiration manager | ||
if le != nil { | ||
le.ExpireTime = time.Now() | ||
|
||
if err := m.persistEntry(le); err != nil { | ||
return err | ||
} | ||
|
||
m.updatePending(le, 0) | ||
} | ||
} | ||
|
||
// te.Path should never be empty, but we check just in case | ||
if te.Path != "" { | ||
saltedID, err := m.tokenStore.SaltID(m.quitContext, te.ID) | ||
if err != nil { | ||
|
@@ -1054,7 +1069,7 @@ func (m *ExpirationManager) revokeEntry(le *leaseEntry) error { | |
// Revocation of login tokens is special since we can by-pass the | ||
// backend and directly interact with the token store | ||
if le.Auth != nil { | ||
if err := m.tokenStore.RevokeTree(m.quitContext, le.ClientToken); err != nil { | ||
if err := m.tokenStore.revokeTree(m.quitContext, le.ClientToken); err != nil { | ||
return errwrap.Wrapf("failed to revoke token: {{err}}", err) | ||
} | ||
|
||
|
@@ -1247,8 +1262,63 @@ func (m *ExpirationManager) removeIndexByToken(token, leaseID string) error { | |
return nil | ||
} | ||
|
||
// lookupByToken is used to lookup all the leaseID's via the | ||
func (m *ExpirationManager) lookupByToken(token string) ([]string, error) { | ||
// CreateOrFetchRevocationLeaseByToken is used to create or fetch the matching | ||
// leaseID for a particular token. The lease is set to expire immediately after | ||
// it's created. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to create a lease? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to created a lease in case the token was created, but never added to the expiration manager (i.e. never auth'ed with) which would mean that there's no lease entry for it. |
||
func (m *ExpirationManager) CreateOrFetchRevocationLeaseByToken(te *TokenEntry) (string, error) { | ||
// Fetch the saltedID of the token and construct the leaseID | ||
saltedID, err := m.tokenStore.SaltID(m.quitContext, te.ID) | ||
if err != nil { | ||
return "", err | ||
} | ||
leaseID := path.Join(te.Path, saltedID) | ||
|
||
// Load the entry | ||
le, err := m.loadEntry(leaseID) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
// If there's no associated leaseEntry for the token, we create one | ||
if le == nil { | ||
auth := &logical.Auth{ | ||
ClientToken: te.ID, | ||
LeaseOptions: logical.LeaseOptions{ | ||
TTL: time.Nanosecond, | ||
}, | ||
} | ||
|
||
if strings.Contains(te.Path, "..") { | ||
return "", consts.ErrPathContainsParentReferences | ||
} | ||
|
||
saltedID, err := m.tokenStore.SaltID(m.quitContext, auth.ClientToken) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You already have this value from earlier in the function. |
||
if err != nil { | ||
return "", err | ||
} | ||
|
||
// Create a lease entry | ||
now := time.Now() | ||
le = &leaseEntry{ | ||
LeaseID: path.Join(te.Path, saltedID), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
ClientToken: auth.ClientToken, | ||
Auth: auth, | ||
Path: te.Path, | ||
IssueTime: now, | ||
ExpireTime: now.Add(time.Nanosecond), | ||
} | ||
|
||
// Encode the entry | ||
if err := m.persistEntry(le); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we need to call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need to call |
||
return "", err | ||
} | ||
} | ||
|
||
return le.LeaseID, nil | ||
} | ||
|
||
// lookupLeasesByToken is used to lookup all the leaseID's via the tokenID | ||
func (m *ExpirationManager) lookupLeasesByToken(token string) ([]string, error) { | ||
saltedID, err := m.tokenStore.SaltID(m.quitContext, token) | ||
if err != nil { | ||
return nil, err | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -178,12 +178,15 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp | |
retErr = multierror.Append(retErr, logical.ErrPermissionDenied) | ||
return nil, nil, retErr | ||
} | ||
if te.NumUses == -1 { | ||
if te.NumUses == tokenRevocationPending { | ||
// We defer a revocation until after logic has run, since this is a | ||
// valid request (this is the token's final use). We pass the ID in | ||
// directly just to be safe in case something else modifies te later. | ||
defer func(id string) { | ||
err = c.tokenStore.Revoke(ctx, id) | ||
leaseID, err := c.expiration.CreateOrFetchRevocationLeaseByToken(te) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The two places in core (Seal/StepDown) probably need this updated logic too right? (They're still using c.tokenStore.Revoke) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if doing an async Revoke (i.e. via the expiration manager) is the right call in seal/step down. My concern is that it can be racy if core seals/give up the lock before the timer in expiration manager triggers to do the revocation via There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually nevermind, I got things mixed up. Revoke() in the expiration manager deletes the lease entry directly as well as the timer in the pending map so it shouldn't be a concern. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Responding here for future reference. This should be addressed now that |
||
if err == nil { | ||
err = c.expiration.Revoke(leaseID) | ||
} | ||
if err != nil { | ||
c.logger.Error("failed to revoke token", "error", err) | ||
retResp = nil | ||
|
@@ -394,7 +397,7 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp | |
} | ||
|
||
if err := c.expiration.RegisterAuth(te.Path, resp.Auth); err != nil { | ||
c.tokenStore.Revoke(ctx, te.ID) | ||
c.tokenStore.revokeOrphan(ctx, te.ID) | ||
c.logger.Error("failed to register token lease", "request_path", req.Path, "error", err) | ||
retErr = multierror.Append(retErr, ErrInternalError) | ||
return nil, auth, retErr | ||
|
@@ -598,7 +601,7 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re | |
|
||
// Register with the expiration manager | ||
if err := c.expiration.RegisterAuth(te.Path, auth); err != nil { | ||
c.tokenStore.Revoke(ctx, te.ID) | ||
c.tokenStore.revokeOrphan(ctx, te.ID) | ||
c.logger.Error("failed to register token lease", "request_path", req.Path, "error", err) | ||
return nil, auth, ErrInternalError | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we update this comment as this is already in expiration manager?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expanded the comment