Add bearer token API authentication

- Auto-generate random 32-char bearer token on first startup, stored in
  secrets.yaml as api.bearerToken
- BearerAuthMiddleware checks Authorization: Bearer <token> on all /api/
  endpoints except /health, /health/reconcile, /events (SSE), and non-API
  paths (frontend static files)
- Development mode (WILD_CENTRAL_ENV=development) skips auth entirely
- Web app ApiClient: add setToken/clearToken/hasToken methods, persist
  token in localStorage, automatically include Authorization header on
  all API requests
- Token can be found in secrets.yaml for CLI/automation use
This commit is contained in:
2026-07-14 12:45:22 +00:00
parent 60f3ca4a3a
commit f5a030fd44
4 changed files with 88 additions and 5 deletions

15
main.go
View File

@@ -119,6 +119,19 @@ func main() {
os.Exit(1)
}
// Load or generate API bearer token
apiToken, _ := secretsMgr.GetSecret("api.bearerToken")
if apiToken == "" {
apiToken, _ = secrets.GenerateSecret(32)
_ = secretsMgr.SetSecret("api.bearerToken", apiToken)
slog.Info("generated API bearer token", "component", "startup")
}
isDev := os.Getenv("WILD_CENTRAL_ENV") == "development"
if isDev {
slog.Info("development mode: API authentication disabled", "component", "startup")
}
allowedOrigins := buildAllowedOrigins(dataDir)
api, err := v1.NewAPI(dataDir, Version, allowedOrigins)
@@ -137,7 +150,7 @@ func main() {
api.StartDNSFilter(ctx)
router := mux.NewRouter()
api.RegisterRoutes(router)
api.RegisterRoutes(router, apiToken, isDev)
router.HandleFunc("/api/v1/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")