[Admin UI] Login not possible due to securecookie error (#7374)
* [Admin UI] Login not possible due to securecookie error * avoid 404 favicon * Update weed/admin/dash/auth_middleware.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * address comments * avoid variable over shadowing * log session save error --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ShowLogin displays the login page
|
// ShowLogin displays the login page
|
||||||
@@ -31,9 +32,16 @@ func (s *AdminServer) HandleLogin(username, password string) gin.HandlerFunc {
|
|||||||
|
|
||||||
if loginUsername == username && loginPassword == password {
|
if loginUsername == username && loginPassword == password {
|
||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
|
// Clear any existing invalid session data before setting new values
|
||||||
|
session.Clear()
|
||||||
session.Set("authenticated", true)
|
session.Set("authenticated", true)
|
||||||
session.Set("username", loginUsername)
|
session.Set("username", loginUsername)
|
||||||
session.Save()
|
if err := session.Save(); err != nil {
|
||||||
|
// Log the detailed error server-side for diagnostics
|
||||||
|
glog.Errorf("Failed to save session for user %s: %v", loginUsername, err)
|
||||||
|
c.Redirect(http.StatusSeeOther, "/login?error=Unable to create session. Please try again or contact administrator.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.Redirect(http.StatusSeeOther, "/admin")
|
c.Redirect(http.StatusSeeOther, "/admin")
|
||||||
return
|
return
|
||||||
@@ -48,6 +56,8 @@ func (s *AdminServer) HandleLogin(username, password string) gin.HandlerFunc {
|
|||||||
func (s *AdminServer) HandleLogout(c *gin.Context) {
|
func (s *AdminServer) HandleLogout(c *gin.Context) {
|
||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
session.Clear()
|
session.Clear()
|
||||||
session.Save()
|
if err := session.Save(); err != nil {
|
||||||
|
glog.Warningf("Failed to save session during logout: %v", err)
|
||||||
|
}
|
||||||
c.Redirect(http.StatusSeeOther, "/login")
|
c.Redirect(http.StatusSeeOther, "/login")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ func (h *AdminHandlers) SetupRoutes(r *gin.Engine, authRequired bool, username,
|
|||||||
// Health check (no auth required)
|
// Health check (no auth required)
|
||||||
r.GET("/health", h.HealthCheck)
|
r.GET("/health", h.HealthCheck)
|
||||||
|
|
||||||
|
// Favicon route (no auth required) - redirect to static version
|
||||||
|
r.GET("/favicon.ico", func(c *gin.Context) {
|
||||||
|
c.Redirect(http.StatusMovedPermanently, "/static/favicon.ico")
|
||||||
|
})
|
||||||
|
|
||||||
if authRequired {
|
if authRequired {
|
||||||
// Authentication routes (no auth required)
|
// Authentication routes (no auth required)
|
||||||
r.GET("/login", h.authHandlers.ShowLogin)
|
r.GET("/login", h.authHandlers.ShowLogin)
|
||||||
|
|||||||
@@ -191,31 +191,7 @@ func startAdminServer(ctx context.Context, options AdminOptions) error {
|
|||||||
r := gin.New()
|
r := gin.New()
|
||||||
r.Use(gin.Logger(), gin.Recovery())
|
r.Use(gin.Logger(), gin.Recovery())
|
||||||
|
|
||||||
// Session store - always auto-generate session key
|
// Create data directory first if specified (needed for session key storage)
|
||||||
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
|
|
||||||
var dataDir string
|
var dataDir string
|
||||||
if *options.dataDir != "" {
|
if *options.dataDir != "" {
|
||||||
// Expand tilde (~) to home directory
|
// 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)
|
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
|
// Create admin server
|
||||||
adminServer := dash.NewAdminServer(*options.masters, nil, dataDir)
|
adminServer := dash.NewAdminServer(*options.masters, nil, dataDir)
|
||||||
|
|
||||||
@@ -331,6 +336,46 @@ func GetAdminOptions() *AdminOptions {
|
|||||||
return &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
|
// 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 == "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user