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:
@@ -93,12 +93,12 @@ spec:
|
|||||||
{{- $adminSecretName := .Values.admin.secret.existingSecret | default (printf "%s-admin-secret" (include "seaweedfs.fullname" .)) }}
|
{{- $adminSecretName := .Values.admin.secret.existingSecret | default (printf "%s-admin-secret" (include "seaweedfs.fullname" .)) }}
|
||||||
env:
|
env:
|
||||||
{{- if $adminAuthEnabled }}
|
{{- if $adminAuthEnabled }}
|
||||||
- name: SEAWEEDFS_ADMIN_USER
|
- name: WEED_ADMIN_USER
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ $adminSecretName }}
|
name: {{ $adminSecretName }}
|
||||||
key: {{ if .Values.admin.secret.existingSecret }}{{ .Values.admin.secret.userKey }}{{ else }}adminUser{{ end }}
|
key: {{ if .Values.admin.secret.existingSecret }}{{ .Values.admin.secret.userKey }}{{ else }}adminUser{{ end }}
|
||||||
- name: SEAWEEDFS_ADMIN_PASSWORD
|
- name: WEED_ADMIN_PASSWORD
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ $adminSecretName }}
|
name: {{ $adminSecretName }}
|
||||||
@@ -153,10 +153,6 @@ spec:
|
|||||||
{{- else if .Values.admin.dataDir }}
|
{{- else if .Values.admin.dataDir }}
|
||||||
-dataDir={{ .Values.admin.dataDir }} \
|
-dataDir={{ .Values.admin.dataDir }} \
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if $adminAuthEnabled }}
|
|
||||||
-adminUser="${SEAWEEDFS_ADMIN_USER}" \
|
|
||||||
-adminPassword="${SEAWEEDFS_ADMIN_PASSWORD}" \
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.admin.masters }}
|
{{- if .Values.admin.masters }}
|
||||||
-masters={{ .Values.admin.masters }}{{- if .Values.admin.extraArgs }} \{{ end }}
|
-masters={{ .Values.admin.masters }}{{- if .Values.admin.extraArgs }} \{{ end }}
|
||||||
{{- else if .Values.global.masterServer }}
|
{{- else if .Values.global.masterServer }}
|
||||||
|
|||||||
@@ -1,279 +0,0 @@
|
|||||||
# SeaweedFS Admin Component
|
|
||||||
|
|
||||||
A modern web-based administration interface for SeaweedFS clusters built with Go, Gin, Templ, and Bootstrap.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Dashboard**: Real-time cluster status and metrics
|
|
||||||
- **Master Management**: Monitor master nodes and leadership status
|
|
||||||
- **Volume Server Management**: View volume servers, capacity, and health
|
|
||||||
- **Object Store Bucket Management**: Create, delete, and manage Object Store buckets with web interface
|
|
||||||
- **S3 Tables Management**: Manage table buckets, namespaces, tables, tags, and policies via the admin UI
|
|
||||||
- **System Health**: Overall cluster health monitoring
|
|
||||||
- **Responsive Design**: Bootstrap-based UI that works on all devices
|
|
||||||
- **Authentication**: Optional user authentication with sessions
|
|
||||||
- **TLS Support**: HTTPS support for production deployments
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
### Using the Admin Makefile
|
|
||||||
|
|
||||||
The admin component has its own Makefile for development and building:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Navigate to admin directory
|
|
||||||
cd weed/admin
|
|
||||||
|
|
||||||
# View all available targets
|
|
||||||
make help
|
|
||||||
|
|
||||||
# Generate templates and build
|
|
||||||
make build
|
|
||||||
|
|
||||||
# Development mode with template watching
|
|
||||||
make dev
|
|
||||||
|
|
||||||
# Run the admin server
|
|
||||||
make run
|
|
||||||
|
|
||||||
# Clean build artifacts
|
|
||||||
make clean
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the Root Makefile
|
|
||||||
|
|
||||||
The root SeaweedFS Makefile automatically integrates the admin component:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# From the root directory
|
|
||||||
make install # Builds weed with admin component
|
|
||||||
make full_install # Full build with all tags
|
|
||||||
make test # Runs tests including admin component
|
|
||||||
|
|
||||||
# Admin-specific targets from root
|
|
||||||
make admin-generate # Generate admin templates
|
|
||||||
make admin-build # Build admin component
|
|
||||||
make admin-run # Run admin server
|
|
||||||
make admin-dev # Development mode
|
|
||||||
make admin-clean # Clean admin artifacts
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Building
|
|
||||||
|
|
||||||
If you prefer to build manually:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install templ compiler
|
|
||||||
go install github.com/a-h/templ/cmd/templ@latest
|
|
||||||
|
|
||||||
# Generate templates
|
|
||||||
templ generate
|
|
||||||
|
|
||||||
# Build the main weed binary
|
|
||||||
cd ../../../
|
|
||||||
go build -o weed ./weed
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
### Template Development
|
|
||||||
|
|
||||||
The admin interface uses [Templ](https://templ.guide/) for type-safe HTML templates:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Watch for template changes and auto-regenerate
|
|
||||||
make watch
|
|
||||||
|
|
||||||
# Or manually generate templates
|
|
||||||
make generate
|
|
||||||
|
|
||||||
# Format templates
|
|
||||||
make fmt
|
|
||||||
```
|
|
||||||
|
|
||||||
### File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
weed/admin/
|
|
||||||
├── Makefile # Admin-specific build tasks
|
|
||||||
├── README.md # This file
|
|
||||||
├── admin.go # Main application entry point
|
|
||||||
├── dash/ # Server and handler logic
|
|
||||||
│ ├── admin_server.go # HTTP server setup
|
|
||||||
│ ├── handler_admin.go # Admin dashboard handlers
|
|
||||||
│ ├── handler_auth.go # Authentication handlers
|
|
||||||
│ └── middleware.go # HTTP middleware
|
|
||||||
├── static/ # Static assets
|
|
||||||
│ ├── css/admin.css # Admin-specific styles
|
|
||||||
│ └── js/admin.js # Admin-specific JavaScript
|
|
||||||
└── view/ # Templates
|
|
||||||
├── app/ # Application templates
|
|
||||||
│ ├── admin.templ # Main dashboard template
|
|
||||||
│ ├── s3_buckets.templ # Object Store bucket management template
|
|
||||||
│ ├── s3tables_*.templ # S3 Tables management templates
|
|
||||||
│ └── *_templ.go # Generated Go code
|
|
||||||
└── layout/ # Layout templates
|
|
||||||
├── layout.templ # Base layout template
|
|
||||||
└── layout_templ.go # Generated Go code
|
|
||||||
```
|
|
||||||
|
|
||||||
### Object Store Management
|
|
||||||
|
|
||||||
The admin interface includes Object Store and S3 Tables management capabilities:
|
|
||||||
|
|
||||||
- Create/delete Object Store buckets and adjust quotas or ownership.
|
|
||||||
- Manage S3 Tables buckets, namespaces, and tables.
|
|
||||||
- Update S3 Tables policies and tags via the UI and API endpoints.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Basic Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Start admin interface on default port (23646)
|
|
||||||
weed admin
|
|
||||||
|
|
||||||
# Start with custom configuration
|
|
||||||
weed admin -port=8080 -masters="master1:9333,master2:9333"
|
|
||||||
|
|
||||||
# Start with authentication
|
|
||||||
weed admin -adminUser=admin -adminPassword=secret123
|
|
||||||
|
|
||||||
# Start with HTTPS
|
|
||||||
weed admin -port=443 -tlsCert=/path/to/cert.pem -tlsKey=/path/to/key.pem
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration Options
|
|
||||||
|
|
||||||
| Option | Default | Description |
|
|
||||||
|--------|---------|-------------|
|
|
||||||
| `-port` | 23646 | Admin server port |
|
|
||||||
| `-masters` | localhost:9333 | Comma-separated master servers |
|
|
||||||
| `-adminUser` | admin | Admin username (if auth enabled) |
|
|
||||||
| `-adminPassword` | "" | Admin password (empty = no auth) |
|
|
||||||
| `-tlsCert` | "" | Path to TLS certificate |
|
|
||||||
| `-tlsKey` | "" | Path to TLS private key |
|
|
||||||
|
|
||||||
### Docker Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build Docker image with admin component
|
|
||||||
make docker-build
|
|
||||||
|
|
||||||
# Run with Docker
|
|
||||||
docker run -p 23646:23646 seaweedfs/seaweedfs:latest admin -masters=host.docker.internal:9333
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Workflow
|
|
||||||
|
|
||||||
### Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clone and setup
|
|
||||||
git clone <seaweedfs-repo>
|
|
||||||
cd seaweedfs/weed/admin
|
|
||||||
|
|
||||||
# Install dependencies and build
|
|
||||||
make install-deps
|
|
||||||
make build
|
|
||||||
|
|
||||||
# Start development server
|
|
||||||
make dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Making Changes
|
|
||||||
|
|
||||||
1. **Template Changes**: Edit `.templ` files in `view/`
|
|
||||||
- Templates auto-regenerate in development mode
|
|
||||||
- Use `make generate` to manually regenerate
|
|
||||||
|
|
||||||
2. **Go Code Changes**: Edit `.go` files
|
|
||||||
- Restart the server to see changes
|
|
||||||
- Use `make build` to rebuild
|
|
||||||
|
|
||||||
3. **Static Assets**: Edit files in `static/`
|
|
||||||
- Changes are served immediately
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run admin component tests
|
|
||||||
make test
|
|
||||||
|
|
||||||
# Run from root directory
|
|
||||||
make admin-test
|
|
||||||
|
|
||||||
# Lint code
|
|
||||||
make lint
|
|
||||||
|
|
||||||
# Format code
|
|
||||||
make fmt
|
|
||||||
```
|
|
||||||
|
|
||||||
## Production Deployment
|
|
||||||
|
|
||||||
### Security Considerations
|
|
||||||
|
|
||||||
1. **Authentication**: Always set `adminPassword` in production
|
|
||||||
2. **HTTPS**: Use TLS certificates for encrypted connections
|
|
||||||
3. **Firewall**: Restrict admin interface access to authorized networks
|
|
||||||
|
|
||||||
### Example Production Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Production deployment with security
|
|
||||||
weed admin \
|
|
||||||
-port=443 \
|
|
||||||
-masters="master1:9333,master2:9333,master3:9333" \
|
|
||||||
-adminUser=admin \
|
|
||||||
-adminPassword=your-secure-password \
|
|
||||||
-tlsCert=/etc/ssl/certs/admin.crt \
|
|
||||||
-tlsKey=/etc/ssl/private/admin.key
|
|
||||||
```
|
|
||||||
|
|
||||||
### Monitoring
|
|
||||||
|
|
||||||
The admin interface provides endpoints for monitoring:
|
|
||||||
|
|
||||||
- `GET /health` - Health check endpoint
|
|
||||||
- `GET /metrics` - Prometheus metrics (if enabled)
|
|
||||||
- `GET /api/status` - JSON status information
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
1. **Templates not found**: Run `make generate` to create template files
|
|
||||||
2. **Build errors**: Ensure `templ` is installed with `make install-templ`
|
|
||||||
3. **Static files not loading**: Check that `static/` directory exists and has proper files
|
|
||||||
4. **Connection errors**: Verify master and filer addresses are correct
|
|
||||||
|
|
||||||
### Debug Mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Enable debug logging
|
|
||||||
weed -v=2 admin
|
|
||||||
|
|
||||||
# Check generated templates
|
|
||||||
ls -la view/app/*_templ.go view/layout/*_templ.go
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch
|
|
||||||
3. Make your changes
|
|
||||||
4. Run tests: `make test`
|
|
||||||
5. Format code: `make fmt`
|
|
||||||
6. Submit a pull request
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The admin component follows a clean architecture:
|
|
||||||
|
|
||||||
- **Presentation Layer**: Templ templates + Bootstrap CSS
|
|
||||||
- **HTTP Layer**: Gin router with middleware
|
|
||||||
- **Business Logic**: Handler functions in `dash/` package
|
|
||||||
- **Data Layer**: Communicates with SeaweedFS masters and filers
|
|
||||||
|
|
||||||
This separation makes the code maintainable and testable.
|
|
||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
flag "github.com/seaweedfs/seaweedfs/weed/util/fla9"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@@ -98,6 +100,9 @@ var cmdAdmin = &Command{
|
|||||||
- IMPORTANT: When read-only credentials are configured, adminPassword MUST also be set
|
- 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
|
- This ensures an admin account exists to manage and authorize read-only access
|
||||||
- Sessions are secured with auto-generated session keys
|
- 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:
|
Security Configuration:
|
||||||
- The admin server reads TLS configuration from security.toml
|
- The admin server reads TLS configuration from security.toml
|
||||||
@@ -137,6 +142,13 @@ func runAdmin(cmd *Command, args []string) bool {
|
|||||||
// Load security configuration
|
// Load security configuration
|
||||||
util.LoadSecurityConfiguration()
|
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
|
// Backward compatibility: if -masters is provided, use it
|
||||||
if *a.masters != "" {
|
if *a.masters != "" {
|
||||||
*a.master = *a.masters
|
*a.master = *a.masters
|
||||||
@@ -534,6 +546,22 @@ func loadOrGenerateSessionKeys(dataDir string) ([]byte, []byte, error) {
|
|||||||
return key[:keyLen], key[keyLen:], nil
|
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
|
// expandHomeDir expands the tilde (~) in a path to the user's home directory
|
||||||
func expandHomeDir(path string) (string, error) {
|
func expandHomeDir(path string) (string, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
|
|||||||
@@ -165,6 +165,19 @@ cert = ""
|
|||||||
key = ""
|
key = ""
|
||||||
ca = ""
|
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.
|
# white list. It's checking request ip address.
|
||||||
[guard]
|
[guard]
|
||||||
white_list = ""
|
white_list = ""
|
||||||
|
|||||||
Reference in New Issue
Block a user