diff --git a/.changelog/_1284.txt b/.changelog/_1284.txt new file mode 100644 index 0000000000..dd17e0990f --- /dev/null +++ b/.changelog/_1284.txt @@ -0,0 +1,3 @@ +```release-note:bug +state: **(Enterprise Only)** ensure partition delete triggers namespace deletes +``` diff --git a/agent/consul/acl_endpoint.go b/agent/consul/acl_endpoint.go index 3579e245b8..6370924016 100644 --- a/agent/consul/acl_endpoint.go +++ b/agent/consul/acl_endpoint.go @@ -188,7 +188,7 @@ func (a *ACL) BootstrapTokens(args *structs.DCSpecificRequest, reply *structs.AC } // Verify we are allowed to serve this request - if !a.srv.InACLDatacenter() { + if !a.srv.InPrimaryDatacenter() { return acl.ErrDisabled } @@ -360,7 +360,7 @@ func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLTok return err } else if token == nil || token.IsExpired(time.Now()) { return acl.ErrNotFound - } else if !a.srv.InACLDatacenter() && !token.Local { + } else if !a.srv.InPrimaryDatacenter() && !token.Local { // global token writes must be forwarded to the primary DC args.Datacenter = a.srv.config.PrimaryDatacenter return a.srv.forwardDC("ACL.TokenClone", a.srv.config.PrimaryDatacenter, args, reply) @@ -435,7 +435,7 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. if !a.srv.LocalTokensEnabled() { // local token operations return fmt.Errorf("Cannot upsert tokens within this datacenter") - } else if !a.srv.InACLDatacenter() && !token.Local { + } else if !a.srv.InPrimaryDatacenter() && !token.Local { return fmt.Errorf("Cannot upsert global tokens within this datacenter") } @@ -854,11 +854,11 @@ func (a *ACL) TokenDelete(args *structs.ACLTokenDeleteRequest, reply *string) er // No need to check expiration time because it's being deleted. // token found in secondary DC but its not local so it must be deleted in the primary - if !a.srv.InACLDatacenter() && !token.Local { + if !a.srv.InPrimaryDatacenter() && !token.Local { args.Datacenter = a.srv.config.PrimaryDatacenter return a.srv.forwardDC("ACL.TokenDelete", a.srv.config.PrimaryDatacenter, args, reply) } - } else if !a.srv.InACLDatacenter() { + } else if !a.srv.InPrimaryDatacenter() { // token not found in secondary DC - attempt to delete within the primary args.Datacenter = a.srv.config.PrimaryDatacenter return a.srv.forwardDC("ACL.TokenDelete", a.srv.config.PrimaryDatacenter, args, reply) @@ -1087,7 +1087,7 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol return err } - if !a.srv.InACLDatacenter() { + if !a.srv.InPrimaryDatacenter() { args.Datacenter = a.srv.config.PrimaryDatacenter } @@ -1218,7 +1218,7 @@ func (a *ACL) PolicyDelete(args *structs.ACLPolicyDeleteRequest, reply *string) return err } - if !a.srv.InACLDatacenter() { + if !a.srv.InPrimaryDatacenter() { args.Datacenter = a.srv.config.PrimaryDatacenter } @@ -1471,7 +1471,7 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e return err } - if !a.srv.InACLDatacenter() { + if !a.srv.InPrimaryDatacenter() { args.Datacenter = a.srv.config.PrimaryDatacenter } @@ -1629,7 +1629,7 @@ func (a *ACL) RoleDelete(args *structs.ACLRoleDeleteRequest, reply *string) erro return err } - if !a.srv.InACLDatacenter() { + if !a.srv.InPrimaryDatacenter() { args.Datacenter = a.srv.config.PrimaryDatacenter } @@ -2142,7 +2142,7 @@ func (a *ACL) AuthMethodSet(args *structs.ACLAuthMethodSetRequest, reply *struct switch method.TokenLocality { case "local", "": case "global": - if !a.srv.InACLDatacenter() { + if !a.srv.InPrimaryDatacenter() { return fmt.Errorf("Invalid Auth Method: TokenLocality 'global' can only be used in the primary datacenter") } default: @@ -2401,7 +2401,7 @@ func (a *ACL) tokenSetFromAuthMethod( } if method.TokenLocality == "global" { - if !a.srv.InACLDatacenter() { + if !a.srv.InPrimaryDatacenter() { return errors.New("creating global tokens via auth methods is only permitted in the primary datacenter") } createReq.ACLToken.Local = false @@ -2465,7 +2465,7 @@ func (a *ACL) Logout(args *structs.ACLLogoutRequest, reply *bool) error { // Can't "logout" of a token that wasn't a result of login. return acl.ErrPermissionDenied - } else if !a.srv.InACLDatacenter() && !token.Local { + } else if !a.srv.InPrimaryDatacenter() && !token.Local { // global token writes must be forwarded to the primary DC args.Datacenter = a.srv.config.PrimaryDatacenter return a.srv.forwardDC("ACL.Logout", a.srv.config.PrimaryDatacenter, args, reply) diff --git a/agent/consul/acl_server.go b/agent/consul/acl_server.go index 21969ca49b..f16b1cc864 100644 --- a/agent/consul/acl_server.go +++ b/agent/consul/acl_server.go @@ -82,13 +82,13 @@ func (s *Server) checkBindingRuleUUID(id string) (bool, error) { return !structs.ACLIDReserved(id), nil } -func (s *Server) InACLDatacenter() bool { +func (s *Server) InPrimaryDatacenter() bool { return s.config.PrimaryDatacenter == "" || s.config.Datacenter == s.config.PrimaryDatacenter } func (s *Server) LocalTokensEnabled() bool { // in ACL datacenter so local tokens are always enabled - if s.InACLDatacenter() { + if s.InPrimaryDatacenter() { return true } @@ -117,7 +117,7 @@ func (s *Server) ACLDatacenter() string { func (s *Server) ResolveIdentityFromToken(token string) (bool, structs.ACLIdentity, error) { // only allow remote RPC resolution when token replication is off and // when not in the ACL datacenter - if !s.InACLDatacenter() && !s.config.ACLTokenReplication { + if !s.InPrimaryDatacenter() && !s.config.ACLTokenReplication { return false, nil, nil } @@ -128,7 +128,7 @@ func (s *Server) ResolveIdentityFromToken(token string) (bool, structs.ACLIdenti return true, aclToken, nil } - return s.InACLDatacenter() || index > 0, nil, acl.ErrNotFound + return s.InPrimaryDatacenter() || index > 0, nil, acl.ErrNotFound } func (s *Server) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, error) { @@ -142,7 +142,7 @@ func (s *Server) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, // If the max index of the policies table is non-zero then we have acls, until then // we may need to allow remote resolution. This is particularly useful to allow updating // the replication token via the API in a non-primary dc. - return s.InACLDatacenter() || index > 0, policy, acl.ErrNotFound + return s.InPrimaryDatacenter() || index > 0, policy, acl.ErrNotFound } func (s *Server) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error) { @@ -156,7 +156,7 @@ func (s *Server) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error // If the max index of the roles table is non-zero then we have acls, until then // we may need to allow remote resolution. This is particularly useful to allow updating // the replication token via the API in a non-primary dc. - return s.InACLDatacenter() || index > 0, role, acl.ErrNotFound + return s.InPrimaryDatacenter() || index > 0, role, acl.ErrNotFound } func (s *Server) ResolveToken(token string) (acl.Authorizer, error) { diff --git a/agent/consul/acl_token_exp.go b/agent/consul/acl_token_exp.go index 9ad3f1ec45..a4dfa180fb 100644 --- a/agent/consul/acl_token_exp.go +++ b/agent/consul/acl_token_exp.go @@ -22,7 +22,7 @@ func (s *Server) reapExpiredTokens(ctx context.Context) error { s.logger.Error("error reaping expired local ACL tokens", "error", err) } } - if s.InACLDatacenter() { + if s.InPrimaryDatacenter() { if _, err := s.reapExpiredGlobalACLTokens(); err != nil { s.logger.Error("error reaping expired global ACL tokens", "error", err) } @@ -37,7 +37,7 @@ func (s *Server) startACLTokenReaping(ctx context.Context) { // We can only check the config settings here that cannot change without a // restart, so we omit the check for a non-empty replication token as that // can be changed at runtime. - if !s.InACLDatacenter() && !s.config.ACLTokenReplication { + if !s.InPrimaryDatacenter() && !s.config.ACLTokenReplication { return } diff --git a/agent/consul/leader.go b/agent/consul/leader.go index 3c1633290b..c78ab4ed88 100644 --- a/agent/consul/leader.go +++ b/agent/consul/leader.go @@ -375,7 +375,7 @@ func (s *Server) initializeACLs(ctx context.Context) error { s.aclAuthMethodValidators.Purge() // Remove any token affected by CVE-2019-8336 - if !s.InACLDatacenter() { + if !s.InPrimaryDatacenter() { _, token, err := s.fsm.State().ACLTokenGetBySecret(nil, redactedToken, nil) if err == nil && token != nil { req := structs.ACLTokenBatchDeleteRequest{ @@ -389,7 +389,7 @@ func (s *Server) initializeACLs(ctx context.Context) error { } } - if s.InACLDatacenter() { + if s.InPrimaryDatacenter() { s.logger.Info("initializing acls") // TODO(partitions): initialize acls in all of the partitions? @@ -623,7 +623,7 @@ func (s *Server) stopACLUpgrade() { } func (s *Server) startACLReplication(ctx context.Context) { - if s.InACLDatacenter() { + if s.InPrimaryDatacenter() { return } diff --git a/agent/consul/state/state_store.go b/agent/consul/state/state_store.go index fdfcb16fcf..82dc8d3567 100644 --- a/agent/consul/state/state_store.go +++ b/agent/consul/state/state_store.go @@ -203,6 +203,28 @@ func (s *Store) Snapshot() *Snapshot { return &Snapshot{s, tx, idx} } +// WalkAllTables basically lets you dump memdb generically and exists primarily +// for very specific types of unit tests and should not be executed in +// production code. +func (s *Store) WalkAllTables(fn func(table string, item interface{}) bool) error { + snap := s.Snapshot() + defer snap.Close() + + for name := range s.schema.Tables { + iter, err := snap.tx.Get(name, indexID) + if err != nil { + return fmt.Errorf("error walking table %q: %w", name, err) + } + for item := iter.Next(); item != nil; item = iter.Next() { + if keepGoing := fn(name, item); !keepGoing { + break + } + } + } + + return nil +} + // LastIndex returns that last index that affects the snapshotted data. func (s *Snapshot) LastIndex() uint64 { return s.lastIndex