feat(admin): support env var and security.toml for credentials (#8606)

* feat(security): add [admin] section to security.toml scaffold

Add admin credential fields (user, password, readonly.user,
readonly.password) to security.toml. Via viper's WEED_ env prefix and
AutomaticEnv(), these are automatically overridable as WEED_ADMIN_USER,
WEED_ADMIN_PASSWORD, etc.

Ref: https://github.com/seaweedfs/seaweedfs/discussions/8586

* feat(admin): support env var and security.toml fallbacks for credentials

Add applyViperFallback() to read admin credentials from security.toml /
WEED_* environment variables when CLI flags are not explicitly set.
This allows systems like NixOS to pass secrets via env vars instead of
CLI flags, which appear in process listings.

Precedence: CLI flag > env var / security.toml > default value.

Also change -adminUser default from "admin" to "" so that credentials
are fully opt-in.

Ref: https://github.com/seaweedfs/seaweedfs/discussions/8586

* feat(helm): use WEED_ env vars for admin credentials instead of CLI flags

Rename SEAWEEDFS_ADMIN_USER/PASSWORD to WEED_ADMIN_USER/PASSWORD so
viper picks them up natively. Remove -adminUser/-adminPassword shell
expansion from command args since the Go binary now reads these
directly via viper.

* docs(admin): document env var and security.toml credential support

Add environment variable mapping table, security.toml example, and
precedence rules to the admin README.

* style(security): use nested [admin.readonly] table in security.toml

Use a nested TOML table instead of dotted keys for the readonly
credentials. More idiomatic and easier to read; no change in how
Viper parses it.

* fix(admin): use util.GetViper() for env var support and fix README example

applyViperFallback() was using viper.GetString() directly, which
bypasses the WEED_ env prefix and AutomaticEnv setup that only
happens in util.GetViper(). Switch to util.GetViper().GetString()
so WEED_ADMIN_* environment variables are actually picked up.

Also fix the README example to include WEED_ADMIN_USER alongside
WEED_ADMIN_PASSWORD, since runAdmin() rejects an empty username
when a password is set.

* fix(admin): restore default adminUser to "admin"

Defaulting adminUser to "" broke the common flow of setting only
WEED_ADMIN_PASSWORD — runAdmin() rejects an empty username when a
password is set. Restore "admin" as the default so that setting
only the password works out of the box.

* docs(admin): align README security.toml example with scaffold format

Use nested [admin.readonly] table instead of flat dotted keys to
match the format in weed/command/scaffold/security.toml.

* docs(admin): remove README.md in favor of wiki page

Admin documentation lives at the wiki (Admin-UI.md). Remove the
in-repo README to avoid maintaining duplicate docs.

---------

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Chris Lu
2026-03-11 17:40:24 -07:00
committed by GitHub
parent 013362d2d3
commit e4a77b8b16
4 changed files with 43 additions and 285 deletions

View File

@@ -17,6 +17,8 @@ import (
"syscall"
"time"
flag "github.com/seaweedfs/seaweedfs/weed/util/fla9"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"github.com/spf13/viper"
@@ -98,6 +100,9 @@ var cmdAdmin = &Command{
- IMPORTANT: When read-only credentials are configured, adminPassword MUST also be set
- This ensures an admin account exists to manage and authorize read-only access
- Sessions are secured with auto-generated session keys
- Credentials can also be set via security.toml [admin] section or environment variables:
WEED_ADMIN_USER, WEED_ADMIN_PASSWORD, WEED_ADMIN_READONLY_USER, WEED_ADMIN_READONLY_PASSWORD
- Precedence: CLI flag > env var / security.toml > default value
Security Configuration:
- The admin server reads TLS configuration from security.toml
@@ -137,6 +142,13 @@ func runAdmin(cmd *Command, args []string) bool {
// Load security configuration
util.LoadSecurityConfiguration()
// Apply security.toml / env var fallbacks for credential flags.
// CLI flags take precedence over security.toml / WEED_* env vars.
applyViperFallback(cmd, a.adminUser, "adminUser", "admin.user")
applyViperFallback(cmd, a.adminPassword, "adminPassword", "admin.password")
applyViperFallback(cmd, a.readOnlyUser, "readOnlyUser", "admin.readonly.user")
applyViperFallback(cmd, a.readOnlyPassword, "readOnlyPassword", "admin.readonly.password")
// Backward compatibility: if -masters is provided, use it
if *a.masters != "" {
*a.master = *a.masters
@@ -534,6 +546,22 @@ func loadOrGenerateSessionKeys(dataDir string) ([]byte, []byte, error) {
return key[:keyLen], key[keyLen:], nil
}
// applyViperFallback sets a flag's value from viper (security.toml / env var)
// when the flag was not explicitly set on the command line.
func applyViperFallback(cmd *Command, flagPtr *string, flagName, viperKey string) {
explicitlySet := false
cmd.Flag.Visit(func(f *flag.Flag) {
if f.Name == flagName {
explicitlySet = true
}
})
if !explicitlySet {
if v := util.GetViper().GetString(viperKey); v != "" {
*flagPtr = v
}
}
}
// expandHomeDir expands the tilde (~) in a path to the user's home directory
func expandHomeDir(path string) (string, error) {
if path == "" {

View File

@@ -165,6 +165,19 @@ cert = ""
key = ""
ca = ""
# Admin server authentication
# If password is set, users must login to access the admin interface
# These can be overridden by environment variables with WEED_ prefix:
# WEED_ADMIN_USER, WEED_ADMIN_PASSWORD
# WEED_ADMIN_READONLY_USER, WEED_ADMIN_READONLY_PASSWORD
[admin]
user = ""
password = ""
[admin.readonly]
user = ""
password = ""
# white list. It's checking request ip address.
[guard]
white_list = ""