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

VAULT-19233 First part of caching static secrets work #23054

Merged
merged 7 commits into from
Sep 22, 2023

Conversation

VioletHynes
Copy link
Contributor

@VioletHynes VioletHynes commented Sep 13, 2023

What this does:
If a new config value is set, then we cache all secrets as static secrets. We check new requests coming in to see if they can share the path. If they have demonstrated they can access this secret before, they'll receive the cached value.

This won't affect any old functionality, as it relies on the new config value being set to true. It's far from a complete solution, but I'd rather have 'smaller' PRs than a couple of thousand line monsters, for reviewers' sanity and also code quality purposes.

What's missing:

  • The caching of a token's permissions
    • This will come in the next PR
  • The updating of a token's permissions via /sys/capabilities (via the jobmanager approach)
    • This will come in a later PR and is part of a later ticket
  • The updating (event system)
    • This will come in a later PR and is part of a later ticket
  • A lot of tests, especially around e.g. cache clear

I've set no changelog for this, as I'll do a large one as a feature changelog near the end.

@VioletHynes VioletHynes added this to the 1.16 milestone Sep 13, 2023
@github-actions github-actions bot added the hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed label Sep 13, 2023
@github-actions
Copy link

github-actions bot commented Sep 13, 2023

CI Results:
All Go tests succeeded! ✅

@VioletHynes VioletHynes marked this pull request as ready for review September 13, 2023 19:50
@github-actions
Copy link

Build Results:
All builds succeeded! ✅

// which is used for static secret caching, and enabling multiple
// tokens to be able to access the same cache entry for static secrets.
// Required: false, Unique: false
Tokens []string
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated this to be a map[string]struct{} so that values had to be unique. Coming in the next PR.

@@ -337,6 +416,15 @@ func (c *LeaseCache) Send(ctx context.Context, req *SendRequest) (*SendResponse,
return resp, nil
}

// TODO: if secret.MountType == "kvv1" || secret.MountType == "kvv2"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

See #23047

Copy link
Collaborator

Choose a reason for hiding this comment

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

In addition to that condition, remember that we'll have to think more about method type: DELETE, PATCH, etc.

}

// TODO: We need to also update the cache for the token's permission capabilities.
// TODO: for this we'll need: req.Token, req.URL.Path
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just in case it wasn't clear, this is also coming in the next PR, to better separate the kinds of caching and to make it easier to review. I thought I'd leave this here so it was obvious what was missing!

// the X-Vault-Token header) to remain agnostic to which token is being
// used in the request. We care only about the path.
func computeStaticSecretCacheIndex(req *SendRequest) string {
return hex.EncodeToString(cryptoutil.Blake2b256Hash(req.Request.URL.Path))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we care about the namespace header though?

Copy link
Contributor Author

@VioletHynes VioletHynes Sep 18, 2023

Choose a reason for hiding this comment

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

Hmm, that's a good point. We'll have to ensure that the namespace header and the path get added together, e.g. a request to ns1/secret should be the same as a request to secret with ns1 as the namespace header. This is actually a shortcoming/bug of the existing approach for dynamic secrets -- we don't treat the above the same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll make sure to handle this and add a test for it. If you don't mind, I'd prefer to do this in the next PR, as I made a slight change to the static secret cache index in that PR

@@ -337,6 +416,15 @@ func (c *LeaseCache) Send(ctx context.Context, req *SendRequest) (*SendResponse,
return resp, nil
}

// TODO: if secret.MountType == "kvv1" || secret.MountType == "kvv2"
Copy link
Collaborator

Choose a reason for hiding this comment

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

In addition to that condition, remember that we'll have to think more about method type: DELETE, PATCH, etc.

@@ -263,19 +318,52 @@ func (c *LeaseCache) Send(ctx context.Context, req *SendRequest) (*SendResponse,
case <-inflight.ch:
}
} else {
inflight = newInflightRequest()
if inflight == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the purpose of this if? When do we want to re-use an existing inflight?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's so that we have one inflight irrespective of if it's a dynamic or static request, essentially. It prevents double defer close(inflight.ch) and defer inflight.remaining.Dec() etc.

Copy link
Collaborator

Choose a reason for hiding this comment

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

How could we have two? How could the same request give rise to a cache hit for both keys?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It won't result in an inflight cache hit for both keys, but we take the else branch if we get a cache miss. We also don't know at this stage if it's a cache hit for either the static secret ID or the dynamic secret ID

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To put it another way, it's likely for most requests to result in a cache miss for both of the inflight cache IDs

Copy link
Collaborator

Choose a reason for hiding this comment

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

This particular else could never see a non-nil inflight though, right? For the second case, the static if/else, might it be clearer to wrap the entire thing in an if inflight == nil instead? If we have already found the dynamic cache entry, there's no point in doing idLockStaticSecret.Lock and consulting the cache again, is there?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We still need to set both of the IDs in the cache (we don't know what kind of request it is at this stage) and unlock the inflight cache lock, so that behaviour can't be conditional. I might be misunderstanding, though?

We only have one request inflight (the stuff protected by the if inflight == nil) but we need to lock the IDs as it could be dynamic or static (or neither)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I'm the one who was missing something, I agree. Though I still feel the first if inflight == nil is a no-op, the one this comment is tied to.

@VioletHynes VioletHynes merged commit 54c84de into main Sep 22, 2023
@VioletHynes VioletHynes deleted the violethynes/VAULT-19233 branch September 22, 2023 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed pr/no-changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants