|
|
|
@ -191,31 +191,7 @@ func startAdminServer(ctx context.Context, options AdminOptions) error { |
|
|
|
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: %w", err) |
|
|
|
} |
|
|
|
store := cookie.NewStore(sessionKeyBytes) |
|
|
|
|
|
|
|
// Configure session options to ensure cookies are properly saved
|
|
|
|
store.Options(sessions.Options{ |
|
|
|
Path: "/", |
|
|
|
MaxAge: 3600 * 24, // 24 hours
|
|
|
|
}) |
|
|
|
|
|
|
|
r.Use(sessions.Sessions("admin-session", store)) |
|
|
|
|
|
|
|
// Static files - serve from embedded filesystem
|
|
|
|
staticFS, err := admin.GetStaticFS() |
|
|
|
if err != nil { |
|
|
|
log.Printf("Warning: Failed to load embedded static files: %v", err) |
|
|
|
} else { |
|
|
|
r.StaticFS("/static", http.FS(staticFS)) |
|
|
|
} |
|
|
|
|
|
|
|
// Create data directory if specified
|
|
|
|
// Create data directory first if specified (needed for session key storage)
|
|
|
|
var dataDir string |
|
|
|
if *options.dataDir != "" { |
|
|
|
// Expand tilde (~) to home directory
|
|
|
|
@ -236,6 +212,35 @@ func startAdminServer(ctx context.Context, options AdminOptions) error { |
|
|
|
fmt.Printf("Data directory created/verified: %s\n", dataDir) |
|
|
|
} |
|
|
|
|
|
|
|
// Detect TLS configuration to set Secure cookie flag
|
|
|
|
cookieSecure := viper.GetString("https.admin.key") != "" |
|
|
|
|
|
|
|
// Session store - load or generate session key
|
|
|
|
sessionKeyBytes, err := loadOrGenerateSessionKey(dataDir) |
|
|
|
if err != nil { |
|
|
|
return fmt.Errorf("failed to get session key: %w", err) |
|
|
|
} |
|
|
|
store := cookie.NewStore(sessionKeyBytes) |
|
|
|
|
|
|
|
// Configure session options to ensure cookies are properly saved
|
|
|
|
store.Options(sessions.Options{ |
|
|
|
Path: "/", |
|
|
|
MaxAge: 3600 * 24, // 24 hours
|
|
|
|
HttpOnly: true, // Prevent JavaScript access
|
|
|
|
Secure: cookieSecure, // Set based on actual TLS configuration
|
|
|
|
SameSite: http.SameSiteLaxMode, |
|
|
|
}) |
|
|
|
|
|
|
|
r.Use(sessions.Sessions("admin-session", store)) |
|
|
|
|
|
|
|
// Static files - serve from embedded filesystem
|
|
|
|
staticFS, err := admin.GetStaticFS() |
|
|
|
if err != nil { |
|
|
|
log.Printf("Warning: Failed to load embedded static files: %v", err) |
|
|
|
} else { |
|
|
|
r.StaticFS("/static", http.FS(staticFS)) |
|
|
|
} |
|
|
|
|
|
|
|
// Create admin server
|
|
|
|
adminServer := dash.NewAdminServer(*options.masters, nil, dataDir) |
|
|
|
|
|
|
|
@ -331,6 +336,46 @@ func GetAdminOptions() *AdminOptions { |
|
|
|
return &AdminOptions{} |
|
|
|
} |
|
|
|
|
|
|
|
// loadOrGenerateSessionKey loads an existing session key from dataDir or generates a new one
|
|
|
|
func loadOrGenerateSessionKey(dataDir string) ([]byte, error) { |
|
|
|
const sessionKeyLength = 32 |
|
|
|
if dataDir == "" { |
|
|
|
// No persistence, generate random key
|
|
|
|
log.Println("No dataDir specified, generating ephemeral session key") |
|
|
|
key := make([]byte, sessionKeyLength) |
|
|
|
_, err := rand.Read(key) |
|
|
|
return key, err |
|
|
|
} |
|
|
|
|
|
|
|
sessionKeyPath := filepath.Join(dataDir, ".session_key") |
|
|
|
|
|
|
|
// Try to load existing key
|
|
|
|
if data, err := os.ReadFile(sessionKeyPath); err == nil { |
|
|
|
if len(data) == sessionKeyLength { |
|
|
|
log.Printf("Loaded persisted session key from %s", sessionKeyPath) |
|
|
|
return data, nil |
|
|
|
} |
|
|
|
log.Printf("Warning: Invalid session key file (expected %d bytes, got %d), generating new key", sessionKeyLength, len(data)) |
|
|
|
} else if !os.IsNotExist(err) { |
|
|
|
log.Printf("Warning: Failed to read session key from %s: %v. A new key will be generated.", sessionKeyPath, err) |
|
|
|
} |
|
|
|
|
|
|
|
// Generate new key
|
|
|
|
key := make([]byte, sessionKeyLength) |
|
|
|
if _, err := rand.Read(key); err != nil { |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
|
|
|
|
// Save key for future use
|
|
|
|
if err := os.WriteFile(sessionKeyPath, key, 0600); err != nil { |
|
|
|
log.Printf("Warning: Failed to persist session key: %v", err) |
|
|
|
} else { |
|
|
|
log.Printf("Generated and persisted new session key to %s", sessionKeyPath) |
|
|
|
} |
|
|
|
|
|
|
|
return key, nil |
|
|
|
} |
|
|
|
|
|
|
|
// expandHomeDir expands the tilde (~) in a path to the user's home directory
|
|
|
|
func expandHomeDir(path string) (string, error) { |
|
|
|
if path == "" { |
|
|
|
|