* fix(s3api/iam): fail config snapshot on empty or malformed IAM files A full IAM reload reads every identity/policy/service-account/group file from the filer. When an external secrets tool rewrites a file, a reload that reads it mid-rewrite sees empty or partially-written content. The identity, policy and service-account loaders silently skipped such files (``continue``), so the snapshot was missing entries that still existed on disk. The atomic swap then installed an incomplete identity set while ``isAuthEnabled`` stayed on, denying unrelated clients mid-reload (#11259). The group loader and the read-error paths already fail the snapshot in this situation (a skipped entry reads as deleted). Apply the same behavior to empty content and unmarshal failures across the identity, policy, service-account and group loaders, so a transient mid-rewrite fails the reload (preserving the last known-good state) instead of silently dropping entries. * fix(s3api/iam): coalesce burst IAM config reloads through the reload queue onIamConfigChange did a full synchronous reload for every identity/policy file change event. When several independently-refreshing credentials rewrite their files within the same second, that produced a burst of dozens of back-to-back full reloads, each reading the whole store and widening the window where a mid-rewrite file is observed (#11259). Route every IAM config change through the existing coalescing reload queue (scheduleReload/reloadRetryLoop) instead. A burst of N events now collapses into a single reload (plus one tail reload for events that arrived while one was in flight). scheduleReload gains a reason argument for the existing log line; the reloadRetryLoop already retries failed reloads, so the per-event failure handoff is no longer needed. Tests that asserted on the synchronous reload now wire up the queue (centralized in newTestS3ApiServerWithMemoryIAM) and poll via waitForIdentity/waitForIdentityGone. Adds TestOnIamConfigChangeCoalescesBurstReloads showing 50 events coalesce into <=3 reloads. * fix(s3api/iam): skip non-JSON auxiliary files before failing IAM snapshot Per review: the multi-file loaders unmarshal every entry in an IAM directory, so a non-JSON auxiliary file (README, .DS_Store, a migration backup such as identity.json.old) would hit the new empty/malformed errors and reject the whole snapshot, blocking all later IAM reloads. Only *.json files are IAM objects (SeaweedFS writes identities, policies, service accounts and groups as <name>.json, and other call sites already gate on the .json suffix). Skip non-.json entries at the top of each loader loop, before reading content, so auxiliary files are ignored while empty/malformed .json files still fail the snapshot. Adds TestLoadConfigurationIgnoresNonJsonAuxiliaryFiles. * fix(s3api/iam): reject IAM files with empty identifiers and skip aux in listing Per review: - ListPolicyNames listed every regular entry in the policies directory as a policy name, including non-JSON auxiliary files, but GetPolicy cannot retrieve them. Apply the same .json suffix filter used by the loader so the list only exposes retrievable policies. - json.Unmarshal accepts `{}` and unknown fields. The identity and group loaders merge by the decoded Name (not the file name), so a `{}` file could install an empty-key record and displace a real one; the service-account loader accepted an empty Id. Validate Identity.Name, Group.Name and ServiceAccount.Id (via validateServiceAccountId) after unmarshal and fail the snapshot on empty identifiers. Adds TestFilerEtcStoreListPolicyNamesSkipsNonJsonAuxiliary and empty-identifier regression tests for identity, group and service-account files.
Credential Store Integration
This document shows how the credential store has been integrated into SeaweedFS's S3 API and IAM API components.
Quick Start
-
Generate credential configuration:
weed scaffold -config=credential -output=. -
Edit credential.toml to enable your preferred store (filer_etc is enabled by default)
-
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
- Easy Configuration - Generate template with
weed scaffold -config=credential - Pluggable Storage - Switch between filer_etc, PostgreSQL without code changes
- Backward Compatibility - Filer-based storage works with existing deployments
- Scalability - Database stores support multiple concurrent servers
- Performance - Database access can be faster than file-based storage
- Testing - Memory store simplifies unit testing
- 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.