feat(auth): Self-Service-Admin-Password-Reset via CLI-Token
Operator hat Admin-Passwort vergessen aber SSH-Zugang zur Box →
schneller Reset ohne SMTP/Email-Setup.
Flow:
1. `sudo edgeguard-ctl reset-password` auf der Box → 32-hex-Token
+ ISO-Expiry werden nach /var/lib/edgeguard/.reset-token (mode
0600 edgeguard:edgeguard) geschrieben, Token kommt auf stdout.
TTL: 30 min.
2. Login-Seite hat „Passwort vergessen?"-Link → /reset-password.
3. Reset-Page: Token + neues Passwort (min. 12). POST /auth/reset-
password validiert Token (constant-time compare), prüft Expiry,
löscht das File (single-use), hash't das Passwort + speichert
in setup.json.
internal/services/setup/:
- SetAdminPassword() — bcrypt-hash + save, fehler wenn setup nicht
completed
- GenerateResetToken() / ConsumeResetToken() — File-basiert,
Format: "<token>|<RFC3339-expiry>"
internal/handlers/auth.go: POST /api/v1/auth/reset-password.
cmd/edgeguard-ctl/main.go: `reset-password` command.
UI: /reset-password Page mit Info-Alert für CLI-Snippet
(„sudo edgeguard-ctl reset-password" im dunklen Code-Block); Login-
Seite bekommt den „Passwort vergessen?"-Link.
Verifiziert auf 1.0.76: CLI druckt Token + schreibt File mit 0600
edgeguard:edgeguard.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@ import (
|
||||
wgsvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/wireguard"
|
||||
)
|
||||
|
||||
var version = "1.0.75"
|
||||
var version = "1.0.76"
|
||||
|
||||
func main() {
|
||||
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
||||
|
||||
@@ -7,9 +7,11 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
||||
)
|
||||
|
||||
var version = "1.0.75"
|
||||
var version = "1.0.76"
|
||||
|
||||
const usage = `edgeguard-ctl — EdgeGuard CLI
|
||||
|
||||
@@ -25,6 +27,7 @@ Commands:
|
||||
initdb Create PostgreSQL role + database (idempotent)
|
||||
render-config Regenerate haproxy / nftables configs from PG (--no-reload, --only=)
|
||||
wg-import [--path <dir>] Import existing /etc/wireguard/*.conf files into the DB
|
||||
reset-password Generate a one-time token for the /reset-password UI flow
|
||||
cluster-join Join an existing cluster (Phase 3, not yet implemented)
|
||||
promote Promote this node's PG to primary (Phase 3, not yet implemented)
|
||||
dump-config Print effective config (Phase 3, not yet implemented)
|
||||
@@ -48,6 +51,8 @@ func main() {
|
||||
os.Exit(cmdRenderConfig(os.Args[2:]))
|
||||
case "wg-import":
|
||||
os.Exit(cmdWGImport(os.Args[2:]))
|
||||
case "reset-password":
|
||||
os.Exit(cmdResetPassword())
|
||||
case "cluster-join", "cluster-leave", "promote", "dump-config":
|
||||
fmt.Fprintf(os.Stderr, "edgeguard-ctl: %q is a Phase-3 stub — not yet implemented\n", os.Args[1])
|
||||
os.Exit(1)
|
||||
@@ -57,3 +62,25 @@ func main() {
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
// cmdResetPassword generates a single-use reset token and prints it to
|
||||
// stdout. Operator pastes it into the /reset-password UI within 30 min.
|
||||
// File mode 0600, owned by the edgeguard user — the CLI muss als sudo
|
||||
// edgeguard ausgeführt werden (oder als root); fremde User sehen das
|
||||
// Token nicht.
|
||||
func cmdResetPassword() int {
|
||||
store := setup.NewStore(setup.DefaultDir)
|
||||
token, err := store.GenerateResetToken()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "edgeguard-ctl reset-password: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
fmt.Println("Admin-Password-Reset-Token (gültig 30 Minuten):")
|
||||
fmt.Println()
|
||||
fmt.Println(" " + token)
|
||||
fmt.Println()
|
||||
fmt.Println("→ UI öffnen: https://<box-fqdn>/reset-password")
|
||||
fmt.Println("→ Token eingeben, neues Passwort setzen (min. 12 Zeichen)")
|
||||
fmt.Println("→ Token ist single-use und wird beim erfolgreichen Reset gelöscht.")
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
||||
)
|
||||
|
||||
var version = "1.0.75"
|
||||
var version = "1.0.76"
|
||||
|
||||
const (
|
||||
// renewTickInterval — how often we re-evaluate expiring certs.
|
||||
|
||||
Reference in New Issue
Block a user