From 5fc2fefb4a9255be99f3d038c0d03317d8a7bb7d Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Thu, 12 Feb 2026 13:36:33 -0800 Subject: [PATCH] Fix STS credential validation by providing default configuration When no STS configuration is provided in the IAM config file, the STS service was not being initialized, causing temporary credentials from AssumeRole to be rejected with "InvalidAccessKeyId" errors. This commit adds a DefaultSTSConfig() function that provides sensible defaults (1h token duration, 12h max session, secure random signing key) and updates the IAM manager to use these defaults when no explicit STS configuration is provided. Changes: - Add DefaultSTSConfig() with secure random signing key generation - Update IAM manager to use default config when config.STS is nil - Add tests verifying initialization with and without explicit config Fixes #8312 --- weed/iam/integration/iam_manager.go | 11 +- weed/iam/integration/iam_manager_sts_test.go | 108 +++++++++++++++++++ weed/iam/sts/sts_service.go | 23 ++++ 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 weed/iam/integration/iam_manager_sts_test.go diff --git a/weed/iam/integration/iam_manager.go b/weed/iam/integration/iam_manager.go index ccbb5ef00..61556e407 100644 --- a/weed/iam/integration/iam_manager.go +++ b/weed/iam/integration/iam_manager.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" + "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/iam/policy" "github.com/seaweedfs/seaweedfs/weed/iam/providers" "github.com/seaweedfs/seaweedfs/weed/iam/sts" @@ -103,7 +104,15 @@ func (m *IAMManager) Initialize(config *IAMConfig, filerAddressProvider func() s // Initialize STS service m.stsService = sts.NewSTSService() - if err := m.stsService.Initialize(config.STS); err != nil { + + // Use default config if none provided + stsConfig := config.STS + if stsConfig == nil { + glog.V(1).Infof("No STS configuration provided, using defaults") + stsConfig = sts.DefaultSTSConfig() + } + + if err := m.stsService.Initialize(stsConfig); err != nil { return fmt.Errorf("failed to initialize STS service: %w", err) } diff --git a/weed/iam/integration/iam_manager_sts_test.go b/weed/iam/integration/iam_manager_sts_test.go new file mode 100644 index 000000000..05d21b5df --- /dev/null +++ b/weed/iam/integration/iam_manager_sts_test.go @@ -0,0 +1,108 @@ +package integration + +import ( + "testing" + "time" + + "github.com/seaweedfs/seaweedfs/weed/iam/policy" + "github.com/seaweedfs/seaweedfs/weed/iam/sts" +) + +// TestIAMManagerInitializeWithoutSTSConfig tests that IAM manager can initialize +// without an explicit STS configuration by using defaults +func TestIAMManagerInitializeWithoutSTSConfig(t *testing.T) { + manager := NewIAMManager() + + // Create config without STS section (nil) + // But with minimal policy and role configs to satisfy other components + config := &IAMConfig{ + STS: nil, // No STS config provided - will use defaults + Policy: &policy.PolicyEngineConfig{ + DefaultEffect: "Deny", + StoreType: "memory", + }, + Roles: &RoleStoreConfig{ + StoreType: "memory", + }, + } + + // Initialize should succeed with default STS config + err := manager.Initialize(config, func() string { return "localhost:8888" }) + if err != nil { + t.Fatalf("Failed to initialize IAM manager without STS config: %v", err) + } + + // Verify STS service is initialized + stsService := manager.GetSTSService() + if stsService == nil { + t.Fatal("STS service is nil after initialization") + } + + if !stsService.IsInitialized() { + t.Fatal("STS service is not initialized") + } + + // Verify the default config was applied + if stsService.Config.Issuer != "seaweedfs-sts" { + t.Errorf("Expected default issuer 'seaweedfs-sts', got '%s'", stsService.Config.Issuer) + } + + if stsService.Config.TokenDuration.Duration != 1*time.Hour { + t.Errorf("Expected default token duration 1h, got %v", stsService.Config.TokenDuration.Duration) + } + + if stsService.Config.MaxSessionLength.Duration != 12*time.Hour { + t.Errorf("Expected default max session length 12h, got %v", stsService.Config.MaxSessionLength.Duration) + } + + t.Logf("Successfully initialized STS service with default configuration") +} + +// TestIAMManagerInitializeWithExplicitSTSConfig tests that explicit STS config still works +func TestIAMManagerInitializeWithExplicitSTSConfig(t *testing.T) { + manager := NewIAMManager() + + // Create config with explicit STS section + config := &IAMConfig{ + STS: &sts.STSConfig{ + TokenDuration: sts.FlexibleDuration{Duration: 30 * time.Minute}, + MaxSessionLength: sts.FlexibleDuration{Duration: 2 * time.Hour}, + Issuer: "custom-issuer", + SigningKey: []byte("custom-signing-key-for-testing-purposes-only"), + AccountId: "999888777666", + }, + Policy: &policy.PolicyEngineConfig{ + DefaultEffect: "Deny", + StoreType: "memory", + }, + Roles: &RoleStoreConfig{ + StoreType: "memory", + }, + } + + // Initialize should succeed with explicit config + err := manager.Initialize(config, func() string { return "localhost:8888" }) + if err != nil { + t.Fatalf("Failed to initialize IAM manager with explicit STS config: %v", err) + } + + // Verify STS service uses the custom config + stsService := manager.GetSTSService() + if stsService == nil { + t.Fatal("STS service is nil after initialization") + } + + if !stsService.IsInitialized() { + t.Fatal("STS service is not initialized") + } + + if stsService.Config.Issuer != "custom-issuer" { + t.Errorf("Expected issuer 'custom-issuer', got '%s'", stsService.Config.Issuer) + } + + if stsService.Config.AccountId != "999888777666" { + t.Errorf("Expected account ID '999888777666', got '%s'", stsService.Config.AccountId) + } + + t.Logf("Successfully initialized STS service with explicit configuration") +} diff --git a/weed/iam/sts/sts_service.go b/weed/iam/sts/sts_service.go index ffbd69e27..43845bf41 100644 --- a/weed/iam/sts/sts_service.go +++ b/weed/iam/sts/sts_service.go @@ -2,6 +2,7 @@ package sts import ( "context" + "crypto/rand" "encoding/json" "errors" "fmt" @@ -258,6 +259,28 @@ func NewSTSService() *STSService { } } +// DefaultSTSConfig returns a default STS configuration with sensible defaults +// This ensures the STS service can be initialized even when no explicit configuration is provided +func DefaultSTSConfig() *STSConfig { + // Generate a secure random signing key + signingKey := make([]byte, 32) // 256 bits + if _, err := rand.Read(signingKey); err != nil { + // Fallback to a warning message if random generation fails + // This should never happen in practice, but we handle it defensively + glog.Warningf("Failed to generate random signing key, using deterministic fallback: %v", err) + signingKey = []byte("default-signing-key-change-in-production-environments") + } + + return &STSConfig{ + TokenDuration: FlexibleDuration{Duration: 1 * time.Hour}, // 1 hour default + MaxSessionLength: FlexibleDuration{Duration: 12 * time.Hour}, // 12 hours max + Issuer: "seaweedfs-sts", + SigningKey: signingKey, + AccountId: "111122223333", // Default AWS account ID + Providers: nil, // No providers by default + } +} + // Initialize initializes the STS service with configuration func (s *STSService) Initialize(config *STSConfig) error { if config == nil {