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
This commit is contained in:
Chris Lu
2026-02-12 13:36:33 -08:00
parent 6e393f3326
commit 5fc2fefb4a
3 changed files with 141 additions and 1 deletions

View File

@@ -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)
}

View File

@@ -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")
}

View File

@@ -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 {