Skip to content
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

Merged
merged 17 commits into from
May 10, 2018
Merged

Token revocation refactor #4512

merged 17 commits into from
May 10, 2018

Conversation

calvn
Copy link
Member

@calvn calvn commented May 4, 2018

Fixes #4143

@calvn calvn added this to the 0.10.2 milestone May 4, 2018
@calvn calvn changed the title [WIP] Token revocation refactor Token revocation refactor May 7, 2018
@calvn calvn requested a review from jefferai May 7, 2018 14:42
return "", consts.ErrPathContainsParentReferences
}

saltedID, err := m.tokenStore.SaltID(m.quitContext, auth.ClientToken)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already have this value from earlier in the function.

// 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)
Copy link
Member

Choose a reason for hiding this comment

The 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)

Copy link
Member Author

Choose a reason for hiding this comment

The 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 expireID. I assume there might be other storage operations during the process so if the storage call to delete blocks (such that the delete is delayed) we have bigger issues, but I'm not sure if we should be concerned that core could seal/give up the write lock before the token store gets updated.

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Responding here for future reference. This should be addressed now that revokeSalted will mark the token with tokenRevocationPending early on so that it's invalidated for any lookup/use prior to async deletion.

// failed (state is false)
state, loaded := ts.tokensPendingDeletion.LoadOrStore(saltedID, true)

// If the entry was loaded and it's state is true, we shortcircuit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/it's/its

jefferai
jefferai previously approved these changes May 7, 2018
Copy link
Member

@jefferai jefferai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of super minor comments, looks great!

// before we return, we can remove the token store entry
if ret == nil {
path := lookupPrefix + saltedID
if err := ts.view.Delete(ctx, path); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this in the defer seems unnecessary, i think it makes more sense as the last thing in the function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea it makes no difference having it vs it being called last. I moved it here to guard against the possibility of accidentally adding token entry updates after the deletion if we appended code to the end of the function which would re-introduce the deleted token. We would still need the defer func even if we had the deletion outside of this in order to properly update ts.tokensPendingDeletion.

* Rename some functions and variables to be more clear
* Change step-down and seal to use expmgr for revoke functionality like
during request handling
* Attempt to WAL the token as being invalid as soon as possible so that
further usage will fail even if revocation does not fully complete
return err
}

// If there's a lease, set expiration to now, persist, and call
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expanded the comment

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to create a lease?

Copy link
Member Author

Choose a reason for hiding this comment

The 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.

// Create a lease entry
now := time.Now()
le = &leaseEntry{
LeaseID: path.Join(te.Path, saltedID),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leaseID is also computed earlier in the function.

}

// Encode the entry
if err := m.persistEntry(le); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to call updatePending after this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to call updatePending since this method should only be in charge of to creating or fetching us the corresponding lease to a token. This method is then used in conjunction with m.Revoke to remove the token and its associated entries.

lock.Unlock()
if err != nil {
return err
if entry.NumUses != tokenRevocationPending {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this if check?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd make sense to keep this if for the case that the token is already in this state so that we don't have to re-store it with the same value.

// Revoke the token and its children
if err := ts.RevokeTree(ctx, aEntry.TokenID); err != nil {
return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
te, err := ts.Lookup(ctx, aEntry.TokenID)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lookup, CreateOr.. and Revoke. This pattern is repeated a couple of times. Can we abstract it out?

@calvn calvn merged commit 0678d6b into master May 10, 2018
@calvn calvn deleted the f-ts-issue branch May 10, 2018 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants