backport of commit 35a5fbfc6002e0440c708e722dc8aabbcb7a81b2 (#22507)

Co-authored-by: Max Bowsher <maxbowsher@gmail.com>
This commit is contained in:
hc-github-team-secure-vault-core 2023-08-22 14:48:14 -04:00 committed by GitHub
parent 4a08ff8c48
commit 2e131c1459
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 20 deletions

3
changelog/18809.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
activity (enterprise): Fix misattribution of entities to no or child namespace auth methods
```

View File

@ -96,11 +96,6 @@ type segmentInfo struct {
tokenCount *activity.TokenCount
}
type clients struct {
distinctEntities uint64
distinctNonEntities uint64
}
// ActivityLog tracks unique entity counts and non-entity token counts.
// It handles assembling log fragments (and sending them to the active
// node), writing log segments, and precomputing queries.
@ -1912,39 +1907,50 @@ func (a *ActivityLog) loadConfigOrDefault(ctx context.Context) (activityConfig,
// HandleTokenUsage adds the TokenEntry to the current fragment of the activity log
// This currently occurs on token usage only.
func (a *ActivityLog) HandleTokenUsage(ctx context.Context, entry *logical.TokenEntry, clientID string, isTWE bool) {
func (a *ActivityLog) HandleTokenUsage(ctx context.Context, entry *logical.TokenEntry, clientID string, isTWE bool) error {
// First, check if a is enabled, so as to avoid the cost of creating an ID for
// tokens without entities in the case where it not.
a.fragmentLock.RLock()
if !a.enabled {
a.fragmentLock.RUnlock()
return
return nil
}
a.fragmentLock.RUnlock()
// Do not count wrapping tokens in client count
if IsWrappingToken(entry) {
return
return nil
}
// Do not count root tokens in client count.
if entry.IsRoot() {
return
return nil
}
// Tokens created for the purpose of Link should bypass counting for billing purposes
if entry.InternalMeta != nil && entry.InternalMeta[IgnoreForBilling] == "true" {
return
return nil
}
// Look up the mount accessor of the auth method that issued the token, taking care to resolve the token path
// against the token namespace, which may not be the same as the request namespace!
tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, a.core)
if err != nil {
return err
}
if tokenNS == nil {
return namespace.ErrNoNamespace
}
tokenCtx := namespace.ContextWithNamespace(ctx, tokenNS)
mountAccessor := ""
mountEntry := a.core.router.MatchingMountEntry(ctx, entry.Path)
mountEntry := a.core.router.MatchingMountEntry(tokenCtx, entry.Path)
if mountEntry != nil {
mountAccessor = mountEntry.Accessor
}
// Parse an entry's client ID and add it to the activity log
a.AddClientToFragment(clientID, entry.NamespaceID, entry.CreationTime, isTWE, mountAccessor)
return nil
}
func (a *ActivityLog) namespaceToLabel(ctx context.Context, nsID string) string {

View File

@ -136,7 +136,10 @@ func TestActivityLog_Creation_WrappingTokens(t *testing.T) {
}
id, isTWE := te.CreateClientID()
a.HandleTokenUsage(context.Background(), te, id, isTWE)
err := a.HandleTokenUsage(context.Background(), te, id, isTWE)
if err != nil {
t.Fatal(err)
}
a.fragmentLock.Lock()
if a.fragment != nil {
@ -153,7 +156,10 @@ func TestActivityLog_Creation_WrappingTokens(t *testing.T) {
}
id, isTWE = teNew.CreateClientID()
a.HandleTokenUsage(context.Background(), teNew, id, isTWE)
err = a.HandleTokenUsage(context.Background(), teNew, id, isTWE)
if err != nil {
t.Fatal(err)
}
a.fragmentLock.Lock()
if a.fragment != nil {
@ -380,18 +386,24 @@ func TestActivityLog_SaveTokensToStorageDoesNotUpdateTokenCount(t *testing.T) {
tokenPath := fmt.Sprintf("%sdirecttokens/%d/0", ActivityLogPrefix, a.GetStartTimestamp())
clientPath := fmt.Sprintf("sys/counters/activity/log/entity/%d/0", a.GetStartTimestamp())
// Create some entries without entityIDs
tokenEntryOne := logical.TokenEntry{NamespaceID: "ns1_id", Policies: []string{"hi"}}
entityEntry := logical.TokenEntry{EntityID: "foo", NamespaceID: "ns1_id", Policies: []string{"hi"}}
tokenEntryOne := logical.TokenEntry{NamespaceID: namespace.RootNamespaceID, Policies: []string{"hi"}}
entityEntry := logical.TokenEntry{EntityID: "foo", NamespaceID: namespace.RootNamespaceID, Policies: []string{"hi"}}
idNonEntity, isTWE := tokenEntryOne.CreateClientID()
for i := 0; i < 3; i++ {
a.HandleTokenUsage(ctx, &tokenEntryOne, idNonEntity, isTWE)
err := a.HandleTokenUsage(ctx, &tokenEntryOne, idNonEntity, isTWE)
if err != nil {
t.Fatal(err)
}
}
idEntity, isTWE := entityEntry.CreateClientID()
for i := 0; i < 2; i++ {
a.HandleTokenUsage(ctx, &entityEntry, idEntity, isTWE)
err := a.HandleTokenUsage(ctx, &entityEntry, idEntity, isTWE)
if err != nil {
t.Fatal(err)
}
}
err := a.saveCurrentSegmentToStorage(ctx, false)
if err != nil {

View File

@ -271,12 +271,12 @@ func (c *Core) CheckToken(ctx context.Context, req *logical.Request, unauth bool
var te *logical.TokenEntry
var entity *identity.Entity
var identityPolicies map[string][]string
var err error
// Even if unauth, if a token is provided, there's little reason not to
// gather as much info as possible for the audit log and to e.g. control
// trace mode for EGPs.
if !unauth || (unauth && req.ClientToken != "") {
var err error
acl, te, entity, identityPolicies, err = c.fetchACLTokenEntryAndEntity(ctx, req)
// In the unauth case we don't want to fail the command, since it's
// unauth, we just have no information to attach to the request, so
@ -438,9 +438,12 @@ func (c *Core) CheckToken(ctx context.Context, req *logical.Request, unauth bool
c.activityLogLock.RLock()
activityLog := c.activityLog
c.activityLogLock.RUnlock()
// If it is an authenticated ( i.e with vault token ) request, increment client count
// If it is an authenticated ( i.e. with vault token ) request, increment client count
if !unauth && activityLog != nil {
activityLog.HandleTokenUsage(ctx, te, clientID, isTWE)
err := c.activityLog.HandleTokenUsage(ctx, te, clientID, isTWE)
if err != nil {
return auth, te, err
}
}
return auth, te, nil
}