Browse Source
Fix IAM identity loss on S3 restart migration (#8343)
Fix IAM identity loss on S3 restart migration (#8343)
* Fix IAM reload after legacy config migration Handle legacy identity.json metadata events by reloading from the credential manager instead of parsing event content, and watch the correct /etc/iam multi-file directories so identity changes are applied. Add regression tests for legacy deletion and /etc/iam/identities change events. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix auth_credentials_subscribe_test helper to not pollute global memory store The SaveConfiguration call was affecting other tests. Use local credential manager and ReplaceS3ApiConfiguration instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix IAM event watching: subscribe to IAM directories and improve directory matching - Add /etc/iam and its subdirectories (identities, policies, service_accounts) to directoriesToWatch - Fix directory matching to avoid false positives from sibling directories - Use exact match or prefix with trailing slash instead of plain HasPrefix - Prevents matching hypothetical /etc/iam/identities_backup directory This ensures IAM config change events are actually delivered to the handler. * fix tests --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>pull/8344/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 169 additions and 22 deletions
-
45weed/s3api/auth_credentials_subscribe.go
-
124weed/s3api/auth_credentials_subscribe_test.go
-
14weed/s3api/auth_credentials_test.go
-
8weed/s3api/s3api_server.go
@ -0,0 +1,124 @@ |
|||
package s3api |
|||
|
|||
import ( |
|||
"context" |
|||
"sync" |
|||
"testing" |
|||
|
|||
"github.com/seaweedfs/seaweedfs/weed/credential" |
|||
_ "github.com/seaweedfs/seaweedfs/weed/credential/memory" |
|||
"github.com/seaweedfs/seaweedfs/weed/filer" |
|||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" |
|||
"github.com/seaweedfs/seaweedfs/weed/pb/iam_pb" |
|||
) |
|||
|
|||
func TestOnIamConfigChangeLegacyIdentityDeletionReloadsConfiguration(t *testing.T) { |
|||
s3a := newTestS3ApiServerWithMemoryIAM(t, []*iam_pb.Identity{ |
|||
{ |
|||
Name: "anonymous", |
|||
Actions: []string{ |
|||
"Read:test", |
|||
}, |
|||
}, |
|||
}) |
|||
|
|||
err := s3a.onIamConfigChange( |
|||
filer.IamConfigDirectory, |
|||
&filer_pb.Entry{Name: filer.IamIdentityFile}, |
|||
nil, |
|||
) |
|||
if err != nil { |
|||
t.Fatalf("onIamConfigChange returned error for legacy identity deletion: %v", err) |
|||
} |
|||
|
|||
if !hasIdentity(s3a.iam, "anonymous") { |
|||
t.Fatalf("expected anonymous identity to remain loaded after legacy identity deletion event") |
|||
} |
|||
} |
|||
|
|||
func TestOnIamConfigChangeReloadsOnIamIdentityDirectoryChanges(t *testing.T) { |
|||
s3a := newTestS3ApiServerWithMemoryIAM(t, []*iam_pb.Identity{ |
|||
{Name: "anonymous"}, |
|||
}) |
|||
|
|||
// Seed initial in-memory IAM state.
|
|||
if err := s3a.iam.LoadS3ApiConfigurationFromCredentialManager(); err != nil { |
|||
t.Fatalf("failed to load initial IAM configuration: %v", err) |
|||
} |
|||
if hasIdentity(s3a.iam, "alice") { |
|||
t.Fatalf("did not expect alice identity before creating user") |
|||
} |
|||
|
|||
if err := s3a.iam.credentialManager.CreateUser(context.Background(), &iam_pb.Identity{Name: "alice"}); err != nil { |
|||
t.Fatalf("failed to create alice in memory credential manager: %v", err) |
|||
} |
|||
|
|||
err := s3a.onIamConfigChange( |
|||
filer.IamConfigDirectory+"/identities", |
|||
nil, |
|||
&filer_pb.Entry{Name: "alice.json"}, |
|||
) |
|||
if err != nil { |
|||
t.Fatalf("onIamConfigChange returned error for identities directory update: %v", err) |
|||
} |
|||
|
|||
if !hasIdentity(s3a.iam, "alice") { |
|||
t.Fatalf("expected alice identity to be loaded after /etc/iam/identities update") |
|||
} |
|||
} |
|||
|
|||
func newTestS3ApiServerWithMemoryIAM(t *testing.T, identities []*iam_pb.Identity) *S3ApiServer { |
|||
t.Helper() |
|||
|
|||
// Create S3ApiConfiguration for test with provided identities
|
|||
config := &iam_pb.S3ApiConfiguration{ |
|||
Identities: identities, |
|||
Accounts: []*iam_pb.Account{}, |
|||
ServiceAccounts: []*iam_pb.ServiceAccount{}, |
|||
} |
|||
|
|||
// Create memory credential manager
|
|||
cm, err := credential.NewCredentialManager(credential.StoreTypeMemory, nil, "") |
|||
if err != nil { |
|||
t.Fatalf("failed to create memory credential manager: %v", err) |
|||
} |
|||
|
|||
// Save test configuration
|
|||
if err := cm.SaveConfiguration(context.Background(), config); err != nil { |
|||
t.Fatalf("failed to save test configuration: %v", err) |
|||
} |
|||
|
|||
// Create a test IAM instance
|
|||
iam := &IdentityAccessManagement{ |
|||
m: sync.RWMutex{}, |
|||
nameToIdentity: make(map[string]*Identity), |
|||
accessKeyIdent: make(map[string]*Identity), |
|||
identities: []*Identity{}, |
|||
policies: make(map[string]*iam_pb.Policy), |
|||
accounts: make(map[string]*Account), |
|||
emailAccount: make(map[string]*Account), |
|||
hashes: make(map[string]*sync.Pool), |
|||
hashCounters: make(map[string]*int32), |
|||
isAuthEnabled: false, |
|||
stopChan: make(chan struct{}), |
|||
useStaticConfig: false, |
|||
credentialManager: cm, |
|||
} |
|||
|
|||
// Load test configuration
|
|||
if err := iam.ReplaceS3ApiConfiguration(config); err != nil { |
|||
t.Fatalf("failed to load test configuration: %v", err) |
|||
} |
|||
|
|||
return &S3ApiServer{ |
|||
iam: iam, |
|||
} |
|||
} |
|||
|
|||
func hasIdentity(iam *IdentityAccessManagement, identityName string) bool { |
|||
iam.m.RLock() |
|||
defer iam.m.RUnlock() |
|||
|
|||
_, ok := iam.nameToIdentity[identityName] |
|||
return ok |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue