aboutsummaryrefslogtreecommitdiff
path: root/weed/command/admin.go
diff options
context:
space:
mode:
Diffstat (limited to 'weed/command/admin.go')
-rw-r--r--weed/command/admin.go236
1 files changed, 236 insertions, 0 deletions
diff --git a/weed/command/admin.go b/weed/command/admin.go
new file mode 100644
index 000000000..ef1d54bb3
--- /dev/null
+++ b/weed/command/admin.go
@@ -0,0 +1,236 @@
+package command
+
+import (
+ "context"
+ "crypto/rand"
+ "crypto/tls"
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+ "path/filepath"
+ "strings"
+ "syscall"
+ "time"
+
+ "github.com/gin-contrib/sessions"
+ "github.com/gin-contrib/sessions/cookie"
+ "github.com/gin-gonic/gin"
+
+ "github.com/seaweedfs/seaweedfs/weed/admin/dash"
+ "github.com/seaweedfs/seaweedfs/weed/admin/handlers"
+)
+
+var (
+ a AdminOptions
+)
+
+type AdminOptions struct {
+ port *int
+ masters *string
+ tlsCertPath *string
+ tlsKeyPath *string
+ adminUser *string
+ adminPassword *string
+}
+
+func init() {
+ cmdAdmin.Run = runAdmin // break init cycle
+ a.port = cmdAdmin.Flag.Int("port", 23646, "admin server port")
+ a.masters = cmdAdmin.Flag.String("masters", "localhost:9333", "comma-separated master servers")
+ a.tlsCertPath = cmdAdmin.Flag.String("tlsCert", "", "path to TLS certificate file")
+ a.tlsKeyPath = cmdAdmin.Flag.String("tlsKey", "", "path to TLS private key file")
+
+ a.adminUser = cmdAdmin.Flag.String("adminUser", "admin", "admin interface username")
+ a.adminPassword = cmdAdmin.Flag.String("adminPassword", "", "admin interface password (if empty, auth is disabled)")
+}
+
+var cmdAdmin = &Command{
+ UsageLine: "admin -port=23646 -masters=localhost:9333",
+ Short: "start SeaweedFS web admin interface",
+ Long: `Start a web admin interface for SeaweedFS cluster management.
+
+ The admin interface provides a modern web interface for:
+ - Cluster topology visualization and monitoring
+ - Volume management and operations
+ - File browser and management
+ - System metrics and performance monitoring
+ - Configuration management
+ - Maintenance operations
+
+ The admin interface automatically discovers filers from the master servers.
+
+ Example Usage:
+ weed admin -port=23646 -masters="master1:9333,master2:9333"
+ weed admin -port=443 -tlsCert=/etc/ssl/admin.crt -tlsKey=/etc/ssl/admin.key
+
+ Authentication:
+ - If adminPassword is not set, the admin interface runs without authentication
+ - If adminPassword is set, users must login with adminUser/adminPassword
+ - Sessions are secured with auto-generated session keys
+
+ Security:
+ - Use HTTPS in production by providing TLS certificates
+ - Set strong adminPassword for production deployments
+ - Configure firewall rules to restrict admin interface access
+
+`,
+}
+
+func runAdmin(cmd *Command, args []string) bool {
+ // Validate required parameters
+ if *a.masters == "" {
+ fmt.Println("Error: masters parameter is required")
+ fmt.Println("Usage: weed admin -masters=master1:9333,master2:9333")
+ return false
+ }
+
+ // Validate TLS configuration
+ if (*a.tlsCertPath != "" && *a.tlsKeyPath == "") ||
+ (*a.tlsCertPath == "" && *a.tlsKeyPath != "") {
+ fmt.Println("Error: Both tlsCert and tlsKey must be provided for TLS")
+ return false
+ }
+
+ // Security warnings
+ if *a.adminPassword == "" {
+ fmt.Println("WARNING: Admin interface is running without authentication!")
+ fmt.Println(" Set -adminPassword for production use")
+ }
+
+ if *a.tlsCertPath == "" {
+ fmt.Println("WARNING: Admin interface is running without TLS encryption!")
+ fmt.Println(" Use -tlsCert and -tlsKey for production use")
+ }
+
+ fmt.Printf("Starting SeaweedFS Admin Interface on port %d\n", *a.port)
+ fmt.Printf("Masters: %s\n", *a.masters)
+ fmt.Printf("Filers will be discovered automatically from masters\n")
+ if *a.adminPassword != "" {
+ fmt.Printf("Authentication: Enabled (user: %s)\n", *a.adminUser)
+ } else {
+ fmt.Printf("Authentication: Disabled\n")
+ }
+ if *a.tlsCertPath != "" {
+ fmt.Printf("TLS: Enabled\n")
+ } else {
+ fmt.Printf("TLS: Disabled\n")
+ }
+
+ // Set up graceful shutdown
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ // Handle interrupt signals
+ sigChan := make(chan os.Signal, 1)
+ signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+ go func() {
+ sig := <-sigChan
+ fmt.Printf("\nReceived signal %v, shutting down gracefully...\n", sig)
+ cancel()
+ }()
+
+ // Start the admin server
+ err := startAdminServer(ctx, a)
+ if err != nil {
+ fmt.Printf("Admin server error: %v\n", err)
+ return false
+ }
+
+ fmt.Println("Admin server stopped")
+ return true
+}
+
+// startAdminServer starts the actual admin server
+func startAdminServer(ctx context.Context, options AdminOptions) error {
+ // Set Gin mode
+ gin.SetMode(gin.ReleaseMode)
+
+ // Create router
+ r := gin.New()
+ r.Use(gin.Logger(), gin.Recovery())
+
+ // Session store - always auto-generate session key
+ sessionKeyBytes := make([]byte, 32)
+ _, err := rand.Read(sessionKeyBytes)
+ if err != nil {
+ return fmt.Errorf("failed to generate session key: %v", err)
+ }
+ store := cookie.NewStore(sessionKeyBytes)
+ r.Use(sessions.Sessions("admin-session", store))
+
+ // Static files - serve from filesystem
+ staticPath := filepath.Join("weed", "admin", "static")
+ if _, err := os.Stat(staticPath); err == nil {
+ r.Static("/static", staticPath)
+ } else {
+ log.Printf("Warning: Static files not found at %s", staticPath)
+ }
+
+ // Create admin server
+ adminServer := dash.NewAdminServer(*options.masters, nil)
+
+ // Show discovered filers
+ filers := adminServer.GetAllFilers()
+ if len(filers) > 0 {
+ fmt.Printf("Discovered filers: %s\n", strings.Join(filers, ", "))
+ } else {
+ fmt.Printf("No filers discovered from masters\n")
+ }
+
+ // Create handlers and setup routes
+ adminHandlers := handlers.NewAdminHandlers(adminServer)
+ adminHandlers.SetupRoutes(r, *options.adminPassword != "", *options.adminUser, *options.adminPassword)
+
+ // Server configuration
+ addr := fmt.Sprintf(":%d", *options.port)
+ server := &http.Server{
+ Addr: addr,
+ Handler: r,
+ }
+
+ // TLS configuration
+ if *options.tlsCertPath != "" && *options.tlsKeyPath != "" {
+ server.TLSConfig = &tls.Config{
+ MinVersion: tls.VersionTLS12,
+ }
+ }
+
+ // Start server
+ go func() {
+ log.Printf("Starting SeaweedFS Admin Server on port %d", *options.port)
+
+ var err error
+ if *options.tlsCertPath != "" && *options.tlsKeyPath != "" {
+ log.Printf("Using TLS with cert: %s, key: %s", *options.tlsCertPath, *options.tlsKeyPath)
+ err = server.ListenAndServeTLS(*options.tlsCertPath, *options.tlsKeyPath)
+ } else {
+ err = server.ListenAndServe()
+ }
+
+ if err != nil && err != http.ErrServerClosed {
+ log.Printf("Failed to start server: %v", err)
+ }
+ }()
+
+ // Wait for context cancellation
+ <-ctx.Done()
+
+ // Graceful shutdown
+ log.Println("Shutting down admin server...")
+ shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+ defer cancel()
+
+ if err := server.Shutdown(shutdownCtx); err != nil {
+ return fmt.Errorf("admin server forced to shutdown: %v", err)
+ }
+
+ return nil
+}
+
+// GetAdminOptions returns the admin command options for testing
+func GetAdminOptions() *AdminOptions {
+ return &AdminOptions{}
+}