Skip to content

Commit eb14be9

Browse files
backport of commit bbe458d (#30414)
Co-authored-by: Mike Palmiotto <[email protected]>
1 parent c0ccb41 commit eb14be9

File tree

3 files changed

+129
-26
lines changed

3 files changed

+129
-26
lines changed

changelog/30390.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
```release-note:bug
2+
identity: Fix non-deterministic merge behavior when two entities have
3+
conflicting local aliases.
4+
```

vault/identity_store_injector_testonly.go

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,18 @@ func entityTestonlyPaths(i *IdentityStore) []*framework.Path {
7474
Type: framework.TypeInt,
7575
Description: "Number of entity aliases to create",
7676
},
77+
"local": {
78+
Type: framework.TypeBool,
79+
Description: "Local alias toggle",
80+
},
7781
},
7882
Operations: map[logical.Operation]framework.OperationHandler{
7983
logical.UpdateOperation: &framework.PathOperation{
8084
Callback: i.createDuplicateEntityAliases(),
8185
ForwardPerformanceStandby: true,
82-
// Writing global (non-local) state should be replicated.
83-
ForwardPerformanceSecondary: true,
86+
// Duplicate entity alias injector now forwards
87+
// CreateEntity calls when flags.Local is true.
88+
ForwardPerformanceSecondary: false,
8489
},
8590
},
8691
},
@@ -291,7 +296,8 @@ type DuplicateGroupFlags struct {
291296
type DuplicateEntityAliasFlags struct {
292297
CommonDuplicateFlags
293298
CommonAliasFlags
294-
Count int `json:"count"`
299+
Count int `json:"count"`
300+
Local bool `json:"local"`
295301
}
296302

297303
type DuplicateGroupAliasFlags struct {
@@ -353,6 +359,7 @@ func (i *IdentityStore) createDuplicateEntityAliases() framework.OperationFunc {
353359
MountAccessor: data.Get("mount_accessor").(string),
354360
},
355361
Count: data.Get("count").(int),
362+
Local: data.Get("local").(bool),
356363
}
357364

358365
if flags.Count < 1 {
@@ -361,13 +368,14 @@ func (i *IdentityStore) createDuplicateEntityAliases() framework.OperationFunc {
361368

362369
ids, err := i.CreateDuplicateEntityAliasesInStorage(ctx, flags)
363370
if err != nil {
364-
i.logger.Error("error creating duplicate entities", "error", err)
365-
return logical.ErrorResponse("error creating duplicate entities"), err
371+
i.logger.Error("error creating duplicate entity aliases", "error", err)
372+
return logical.ErrorResponse("error creating duplicate entity aliases"), err
366373
}
367374

368375
return &logical.Response{
369376
Data: map[string]interface{}{
370377
"entity_ids": ids,
378+
"local": flags.Local,
371379
},
372380
}, nil
373381
}
@@ -377,7 +385,7 @@ func (i *IdentityStore) createDuplicateLocalEntityAlias() framework.OperationFun
377385
return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
378386
metadata, ok := data.GetOk("metadata")
379387
if !ok {
380-
metadata = make(map[string]interface{})
388+
metadata = make(map[string]string)
381389
}
382390

383391
flags := DuplicateEntityAliasFlags{
@@ -532,28 +540,73 @@ func (i *IdentityStore) CreateDuplicateEntityAliasesInStorage(ctx context.Contex
532540
e.NamespaceID = flags.NamespaceID
533541
err = i.sanitizeEntity(ctx, e)
534542
if err != nil {
535-
return nil, err
543+
return nil, fmt.Errorf("error sanitizing entity: %w", err)
536544
}
537545
entityIDs = append(entityIDs, e.ID)
538546

539547
a := &identity.Alias{
540548
ID: aliasID,
549+
NamespaceID: flags.NamespaceID,
541550
CanonicalID: e.ID,
542-
MountAccessor: flags.CommonAliasFlags.MountAccessor,
551+
MountAccessor: flags.MountAccessor,
543552
Name: dupAliasName,
553+
Local: flags.Local,
544554
}
545-
e.UpsertAlias(a)
546555

547-
entity, err := ptypes.MarshalAny(e)
548-
if err != nil {
549-
return nil, err
550-
}
551-
item := &storagepacker.Item{
552-
ID: e.ID,
553-
Message: entity,
556+
persistEntity := func(ent *identity.Entity) error {
557+
entity, err := ptypes.MarshalAny(ent)
558+
if err != nil {
559+
return fmt.Errorf("error marhsaling entity: %w", err)
560+
}
561+
item := &storagepacker.Item{
562+
ID: ent.ID,
563+
Message: entity,
564+
}
565+
if err = i.entityPacker.PutItem(ctx, item); err != nil {
566+
return err
567+
}
568+
569+
return nil
554570
}
555-
if err = i.entityPacker.PutItem(ctx, item); err != nil {
556-
return nil, err
571+
572+
if flags.Local {
573+
// Check to see if the entity creation should be forwarded.
574+
i.logger.Trace("forwarding entity creation for local alias cache")
575+
e, err := i.entityCreator.CreateEntity(ctx)
576+
if err != nil {
577+
return nil, err
578+
}
579+
580+
localAliases, err := i.parseLocalAliases(e.ID)
581+
if err != nil {
582+
return nil, fmt.Errorf("failed to parse local aliases from entity: %w", err)
583+
}
584+
if localAliases == nil {
585+
localAliases = &identity.LocalAliases{}
586+
}
587+
588+
// Don't check if this is a duplicate, since we're allowing the developer to
589+
// create duplicates here.
590+
localAliases.Aliases = append(localAliases.Aliases, a)
591+
592+
marshaledAliases, err := anypb.New(localAliases)
593+
if err != nil {
594+
return nil, fmt.Errorf("failed to marshal local aliases: %w", err)
595+
}
596+
item := &storagepacker.Item{
597+
ID: e.ID,
598+
Message: marshaledAliases,
599+
}
600+
if err := i.localAliasPacker.PutItem(ctx, item); err != nil {
601+
return nil, fmt.Errorf("failed to put item in local alias packer: %w", err)
602+
}
603+
} else {
604+
e.UpsertAlias(a)
605+
err := persistEntity(e)
606+
if err != nil {
607+
return nil, err
608+
}
609+
557610
}
558611
}
559612

@@ -583,8 +636,9 @@ func (i *IdentityStore) CreateDuplicateLocalEntityAliasInStorage(ctx context.Con
583636

584637
a := &identity.Alias{
585638
ID: aliasID,
586-
CanonicalID: flags.CommonAliasFlags.CanonicalID,
587-
MountAccessor: flags.CommonAliasFlags.MountAccessor,
639+
NamespaceID: flags.NamespaceID,
640+
CanonicalID: flags.CanonicalID,
641+
MountAccessor: flags.MountAccessor,
588642
Name: flags.Name,
589643
Local: true,
590644
}

vault/identity_store_util.go

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -799,12 +799,20 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e
799799
entity.NamespaceID = namespace.RootNamespaceID
800800
}
801801

802+
ns, err := i.namespacer.NamespaceByID(ctx, entity.NamespaceID)
803+
if err != nil {
804+
return false, err
805+
}
806+
807+
nsCtx := namespace.ContextWithNamespace(ctx, ns)
808+
802809
if previousEntity != nil && previousEntity.NamespaceID != entity.NamespaceID {
803810
return false, errors.New("entity and previous entity are not in the same namespace")
804811
}
805812

806813
aliasFactors := make([]string, len(entity.Aliases))
807814

815+
var localAliasesToDrop []*identity.Alias
808816
for index, alias := range entity.Aliases {
809817
// Verify that alias is not associated to a different one already
810818
aliasByFactors, err := i.MemDBAliasByFactorsInTxn(txn, alias.MountAccessor, alias.Name, false, false)
@@ -830,6 +838,20 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e
830838
return false, errors.New("alias from factors and entity are not in the same namespace")
831839
}
832840

841+
case i.localNode.ReplicationState().HasState(consts.ReplicationPerformanceSecondary) && alias.Local:
842+
// If this alias is local and we're on the perf secondary, don't
843+
// merge! We do this because we can't persist the entity merge to
844+
// storage on a secondary and sending it upstream to the primary
845+
// leads to all sorts of distributed state problems. Instead, just
846+
// let the first alias win and remove any duplicates from the local
847+
// alias packer.
848+
//
849+
// Rather than delete immediately, keep track of the list of local
850+
// aliases to delete once we're done iterating.
851+
i.logger.Trace("skipping entity merge and dropping local alias on secondary")
852+
localAliasesToDrop = append(localAliasesToDrop, alias)
853+
continue
854+
833855
case previousEntity != nil && aliasByFactors.CanonicalID == previousEntity.ID:
834856
// previousEntity isn't upserted yet so may still contain the old
835857
// alias reference in memdb if it was just changed; validate
@@ -864,7 +886,7 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e
864886
"alias_by_factors", aliasByFactors)
865887

866888
persistMerge := persist || persistMerges
867-
respErr, intErr := i.mergeEntityAsPartOfUpsert(ctx, txn, entity, aliasByFactors.CanonicalID, persistMerge)
889+
respErr, intErr := i.mergeEntityAsPartOfUpsert(nsCtx, txn, entity, aliasByFactors.CanonicalID, persistMerge)
868890
switch {
869891
case respErr != nil:
870892
return false, respErr
@@ -894,7 +916,7 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e
894916
// are in case-sensitive mode so we can report these to the operator ahead
895917
// of them disabling case-sensitive mode. Note that alias resolvers don't
896918
// ever modify right now so ignore the bool.
897-
_, conflictErr := i.conflictResolver.ResolveAliases(ctx, entity, aliasByFactors, alias)
919+
_, conflictErr := i.conflictResolver.ResolveAliases(nsCtx, entity, aliasByFactors, alias)
898920

899921
// This appears to be accounting for any duplicate aliases for the same
900922
// Entity. In that case we would have skipped over the merge above in the
@@ -926,24 +948,42 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e
926948

927949
if persist {
928950
// Persist the previous entity object
929-
if err := i.persistEntity(ctx, previousEntity); err != nil {
951+
if err := i.persistEntity(nsCtx, previousEntity); err != nil {
930952
return false, err
931953
}
932954
}
933955
}
934956

957+
// Now that we've gone through all aliases, we can update this entity and
958+
// remove any local aliases before upserting in MemDB.
959+
for _, alias := range localAliasesToDrop {
960+
entity.DeleteAliasByID(alias.ID)
961+
}
962+
935963
// Insert or update entity in MemDB using the transaction created above
936964
err = i.MemDBUpsertEntityInTxn(txn, entity)
937965
if err != nil {
938966
return false, err
939967
}
940968

941969
if persist {
942-
if err := i.persistEntity(ctx, entity); err != nil {
970+
if err := i.persistEntity(nsCtx, entity); err != nil {
943971
return false, err
944972
}
945973
}
946974

975+
// Drop any local aliases detected above from the local alias packer on the
976+
// Secondary active node. We'll only populate the localAliasesToDrop slice
977+
// on Perf Secondaries, so check here to see if we're an active node so we
978+
// can persist the change.
979+
if i.localNode.HAState() == consts.Active {
980+
for _, alias := range localAliasesToDrop {
981+
if err := i.localAliasPacker.DeleteItem(nsCtx, alias.CanonicalID); err != nil {
982+
i.logger.Warn("failed to delete entity from local alias packer", "entity_id", alias.CanonicalID)
983+
}
984+
}
985+
}
986+
947987
return false, nil
948988
}
949989

@@ -3022,19 +3062,24 @@ func makeLocalAliasWithName(t *testing.T, name, entityID string, bucketKey strin
30223062
// MakeDeduplicationDoneChan creates a new done channel for synchronization
30233063
// with tests outside of the vault package (e.g. in external_tests).
30243064
func (i *IdentityStore) MakeDeduplicationDoneChan() {
3065+
i.lock.Lock()
3066+
defer i.lock.Unlock()
3067+
30253068
i.activateDeduplicationDone = make(chan struct{})
30263069
}
30273070

30283071
// WaitForActivateDeduplicationDone is a test helper to wait for the identity
30293072
// deduplication activation to finish.
30303073
func (i *IdentityStore) WaitForActivateDeduplicationDone(ctx context.Context) error {
3031-
timeoutCtx, cancel := context.WithTimeout(ctx, 90*time.Second)
3074+
i.logger.Trace("waiting for activation", "channel", i.activateDeduplicationDone)
3075+
3076+
timeoutCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
30323077
defer cancel()
30333078
select {
30343079
case <-i.activateDeduplicationDone:
3080+
i.logger.Trace("activation write received", "channel", i.activateDeduplicationDone)
30353081
return nil
30363082
case <-timeoutCtx.Done():
30373083
return fmt.Errorf("timed out waiting for deduplication")
3038-
30393084
}
30403085
}

0 commit comments

Comments
 (0)