6eb75204b6
New controller features:
- Web-based setup wizard replaces docker-setup.sh interactive config
- Dual listener: :8080 (Traefik) + :8081 (direct HTTP for LAN)
- Drive scanner finds .felhom-infra-backup/ on all block devices
- Hub recovery pull (GET /api/v1/recovery/{id}) with retrieval password
- Fresh install: Hub config download or manual wizard
- CSRF protection, state persistence, Hungarian UI
- Local infra backup written to all connected drives after each backup cycle
- .felhom-infra-backup/backup.json + metadata.json with SHA256 checksum
- Hub verification: parse customer_blocked from report push response
- Limited mode after 7 days without verification
- Recovery info page on Settings + recovery-info.txt file generation
- Pending events queue: DR events sent to Hub on next report push
- docker-setup.sh v6.0.0: removed interactive wizard, minimal controller.yaml only
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
55 lines
1.4 KiB
Go
55 lines
1.4 KiB
Go
package setup
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"net/http"
|
|
)
|
|
|
|
const csrfCookieName = "felhom_csrf"
|
|
const csrfFormField = "_csrf"
|
|
|
|
// generateCSRFToken creates a random 32-byte hex token.
|
|
func generateCSRFToken() string {
|
|
b := make([]byte, 32)
|
|
if _, err := rand.Read(b); err != nil {
|
|
// Fallback to time-based (extremely unlikely)
|
|
return "fallback-csrf-token"
|
|
}
|
|
return hex.EncodeToString(b)
|
|
}
|
|
|
|
// setCSRFCookie sets the CSRF cookie on the response.
|
|
func setCSRFCookie(w http.ResponseWriter, token string) {
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: csrfCookieName,
|
|
Value: token,
|
|
Path: "/",
|
|
SameSite: http.SameSiteStrictMode,
|
|
HttpOnly: false, // JavaScript needs to read it for AJAX if needed
|
|
})
|
|
}
|
|
|
|
// validateCSRF checks that the form field matches the cookie.
|
|
func validateCSRF(r *http.Request) bool {
|
|
cookie, err := r.Cookie(csrfCookieName)
|
|
if err != nil || cookie.Value == "" {
|
|
return false
|
|
}
|
|
formToken := r.FormValue(csrfFormField)
|
|
if formToken == "" {
|
|
return false
|
|
}
|
|
return cookie.Value == formToken
|
|
}
|
|
|
|
// ensureCSRFToken returns the existing CSRF token from the cookie, or generates a new one.
|
|
func ensureCSRFToken(w http.ResponseWriter, r *http.Request) string {
|
|
if cookie, err := r.Cookie(csrfCookieName); err == nil && cookie.Value != "" {
|
|
return cookie.Value
|
|
}
|
|
token := generateCSRFToken()
|
|
setCSRFCookie(w, token)
|
|
return token
|
|
}
|