Files
seaweedfs/weed/credential
Chris Lu c390448906 fix(s3): preserve exact policy document in embedded IAM put/get-user-policy (#9025)
* fix(s3): preserve exact policy document in embedded IAM PutUserPolicy/GetUserPolicy (#9008)

The embedded IAM implementation (used when IAM requests go through the
S3 gateway) discarded the original policy document on PutUserPolicy,
storing only the lossy ident.Actions representation. GetUserPolicy then
reconstructed the document from these coarse-grained actions, producing
wildcard-expanded actions (s3:GetObject → s3:Get*), duplicates, and
collapsed resources (array → single string).

PR #9009 fixed this in the standalone IAM server (weed/iamapi/) but the
embedded IAM (weed/s3api/) — which is the code path most users hit —
had the same bugs.

Changes:

- Add InlinePolicyStore optional interface to credential store, with
  implementations for FilerEtcStore (uses existing PoliciesCollection),
  MemoryStore, and PropagatingCredentialStore.

- Embedded IAM PutUserPolicy now persists the original policy document
  via CredentialManager.PutUserInlinePolicy for lossless round-trips.

- Embedded IAM GetUserPolicy first tries the stored inline policy; only
  falls back to lossy reconstruction from ident.Actions when no stored
  document exists (e.g. policies created before this fix).

- Fix the fallback reconstruction: add action deduplication and preserve
  resource paths verbatim (no more spurious /* appending).

- Update DeleteUserPolicy/ListUserPolicies to use stored inline policies.

* fix(s3): address PR review feedback for embedded IAM inline policies

- Validate PolicyName is non-empty in PutUserPolicy and DeleteUserPolicy
- Add recomputeActions() to aggregate ident.Actions from ALL stored
  inline policies on put/delete, fixing the issue where a second
  PutUserPolicy would overwrite the first policy's enforcement
- Log errors from GetUserInlinePolicy in the GetUserPolicy fallback
  instead of silently ignoring them
- Add initialization guards to MemoryStore GetUserInlinePolicy and
  ListUserInlinePolicies for consistency with other read methods

* fix(s3): make inline policy persistence fatal and propagate recompute errors

Address second round of review feedback:

- recomputeActions() now returns ([]string, error) so callers can
  distinguish store failures from "no stored policies" and abort the
  mutation on transient errors instead of silently falling back.

- PutUserInlinePolicy and DeleteUserInlinePolicy failures are now fatal:
  the API call returns ServiceFailure instead of logging and continuing,
  keeping ident.Actions and stored policy state in sync.

* chore: gofmt weed/s3api/iceberg/handlers_oauth.go

Pre-existing formatting issue from #9017; fixes S3 Tables Format Check CI.
2026-04-10 18:09:22 -07:00
..

Credential Store Integration

This document shows how the credential store has been integrated into SeaweedFS's S3 API and IAM API components.

Quick Start

  1. Generate credential configuration:

    weed scaffold -config=credential -output=.
    
  2. Edit credential.toml to enable your preferred store (filer_etc is enabled by default)

  3. Start S3 API server - it will automatically load credential.toml:

    weed s3 -filer=localhost:8888
    

Integration Overview

The credential store provides a pluggable backend for storing S3 identities and credentials, supporting:

  • Filer-based storage (filer_etc) - Uses existing filer storage (default)
  • PostgreSQL - Shared database for multiple servers
  • Memory - In-memory storage for testing

Configuration

Using credential.toml

Generate the configuration template:

weed scaffold -config=credential

This creates a credential.toml file with all available options. The filer_etc store is enabled by default:

# Filer-based credential store (default, uses existing filer storage)
[credential.filer_etc]
enabled = true


# PostgreSQL credential store (recommended for multi-node deployments)
[credential.postgres]
enabled = false
hostname = "localhost"
port = 5432
username = "seaweedfs"
password = "your_password"
database = "seaweedfs"

# Memory credential store (for testing only, data is lost on restart)
[credential.memory]
enabled = false

The credential.toml file is automatically loaded from these locations (in priority order):

  • ./credential.toml
  • $HOME/.seaweedfs/credential.toml
  • /etc/seaweedfs/credential.toml

Server Configuration

Both S3 API and IAM API servers automatically load credential.toml during startup. No additional configuration is required.

Usage Examples

Filer-based Store (Default)

[credential.filer_etc]
enabled = true

This uses the existing filer storage and is compatible with current deployments.

PostgreSQL Store

[credential.postgres]
enabled = true
hostname = "localhost"
port = 5432
username = "seaweedfs"
password = "your_password"
database = "seaweedfs"
schema = "public"
sslmode = "disable"
table_prefix = "sw_"
connection_max_idle = 10
connection_max_open = 100
connection_max_lifetime_seconds = 3600

Memory Store (Testing)

[credential.memory]
enabled = true

Environment Variables

All credential configuration can be overridden with environment variables:

# Override PostgreSQL password
export WEED_CREDENTIAL_POSTGRES_PASSWORD=secret


# Override PostgreSQL hostname
export WEED_CREDENTIAL_POSTGRES_HOSTNAME=db.example.com

# Enable/disable stores
export WEED_CREDENTIAL_FILER_ETC_ENABLED=true

Rules:

  • Prefix with WEED_CREDENTIAL_
  • Convert to uppercase
  • Replace . with _

Implementation Details

Components automatically load credential configuration during startup:

// Server initialization
if credConfig, err := credential.LoadCredentialConfiguration(); err == nil && credConfig != nil {
    credentialManager, err := credential.NewCredentialManager(
        credConfig.Store,
        credConfig.Config,
        credConfig.Prefix,
    )
    if err != nil {
        return nil, fmt.Errorf("failed to initialize credential manager: %v", err)
    }
    // Use credential manager for operations
}

Benefits

  1. Easy Configuration - Generate template with weed scaffold -config=credential
  2. Pluggable Storage - Switch between filer_etc, PostgreSQL without code changes
  3. Backward Compatibility - Filer-based storage works with existing deployments
  4. Scalability - Database stores support multiple concurrent servers
  5. Performance - Database access can be faster than file-based storage
  6. Testing - Memory store simplifies unit testing
  7. Environment Override - All settings can be overridden with environment variables

Error Handling

When a credential store is configured, it must initialize successfully or the server will fail to start:

if credConfig != nil {
    credentialManager, err = credential.NewCredentialManager(...)
    if err != nil {
        return nil, fmt.Errorf("failed to initialize credential manager: %v", err)
    }
}

This ensures explicit configuration - if you configure a credential store, it must work properly.