security: Zufallspasswörter beim Erststart, kryptographisch sichere JTI-Generierung
- seedDefaultUsers: generiert kryptographisch zufällige Passwörter (crypto/rand) statt hartkodiertes "archivmailrockz" — Passwörter werden einmalig im Terminal angezeigt und können danach nicht wiederhergestellt werden - generateJTI: verwendet crypto/rand (16 Byte, hex) statt time.UnixNano XOR deadbeef Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+38
-4
@@ -2,6 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@@ -321,6 +323,8 @@ func runIntegrityCheck(ctx context.Context, store *storage.Store, logger *slog.L
|
||||
}
|
||||
|
||||
// seedDefaultUsers creates default admin and auditor accounts if no users exist yet.
|
||||
// Passwords are randomly generated and printed once to stdout — there is no way to
|
||||
// recover them afterwards; they must be changed immediately after the first login.
|
||||
func seedDefaultUsers(users *userstore.Store, logger *slog.Logger) error {
|
||||
all, err := users.List("")
|
||||
if err != nil {
|
||||
@@ -329,16 +333,46 @@ func seedDefaultUsers(users *userstore.Store, logger *slog.Logger) error {
|
||||
if len(all) > 0 {
|
||||
return nil // already seeded
|
||||
}
|
||||
|
||||
adminPw, err := randomPassword()
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate admin password: %w", err)
|
||||
}
|
||||
auditorPw, err := randomPassword()
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate auditor password: %w", err)
|
||||
}
|
||||
|
||||
defaults := []userstore.CreateUserRequest{
|
||||
{Username: "admin", Email: "admin@archivmail.local", Password: "archivmailrockz", Role: userstore.RoleAdmin},
|
||||
{Username: "auditor", Email: "auditor@archivmail.local", Password: "archivmailrockz", Role: userstore.RoleAuditor},
|
||||
{Username: "admin", Email: "admin@archivmail.local", Password: adminPw, Role: userstore.RoleAdmin},
|
||||
{Username: "auditor", Email: "auditor@archivmail.local", Password: auditorPw, Role: userstore.RoleAuditor},
|
||||
}
|
||||
for _, req := range defaults {
|
||||
if _, err := users.Create(req); err != nil {
|
||||
return fmt.Errorf("create default user %s: %w", req.Username, err)
|
||||
}
|
||||
logger.Info("created default user", "username", req.Username, "role", req.Role)
|
||||
}
|
||||
logger.Warn("default users created — change passwords immediately!", "admin", "admin", "auditor", "auditor")
|
||||
|
||||
// Print credentials prominently — this is the only time they are visible.
|
||||
fmt.Println()
|
||||
fmt.Println("╔══════════════════════════════════════════════════════════════╗")
|
||||
fmt.Println("║ ARCHIVMAIL — ERSTMALIGE EINRICHTUNG ║")
|
||||
fmt.Println("║ Initiale Zugangsdaten (NUR EINMAL ANGEZEIGT): ║")
|
||||
fmt.Printf( "║ admin : %-50s ║\n", adminPw)
|
||||
fmt.Printf( "║ auditor : %-50s ║\n", auditorPw)
|
||||
fmt.Println("║ Passwörter sofort nach dem ersten Login ändern! ║")
|
||||
fmt.Println("╚══════════════════════════════════════════════════════════════╝")
|
||||
fmt.Println()
|
||||
|
||||
logger.Warn("default users created — change passwords immediately!")
|
||||
return nil
|
||||
}
|
||||
|
||||
// randomPassword generates a cryptographically random 16-byte hex password.
|
||||
func randomPassword() (string, error) {
|
||||
b := make([]byte, 16)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user