fix(security): phase 2 high-severity hardening #27

Merged
lerko merged 1 commits from security/phase-2-hardening into main 2026-05-26 15:31:19 +00:00
Owner

Summary

  • Push token → header: Authorization: Bearer <token> is now primary auth for push heartbeats. Query string ?token= still works but logs a deprecation warning.
  • Gotify token fix: Moved from URL query param to X-Gotify-Key header (Gotify's preferred method)
  • Rate limiting: Per-IP token-bucket on all endpoints — push 60/min, probe 30/min, backup 10/min, status 120/min
  • /metrics auth: Gated behind cluster secret when set. UPTOP_METRICS_PUBLIC=true to opt out.
  • Export redaction: /api/backup/export redacts passwords/tokens by default. Append ?redact_secrets=false for full export.
  • Placeholder fix: rewritePlaceholders now handles 100+ SQL parameters correctly via strconv.Itoa
  • AddSiteReturningID race fix: Uses LastInsertId() (SQLite) / INSERT RETURNING (Postgres) instead of insert-then-query-by-name
  • HTTP server timeouts: ReadTimeout 30s, WriteTimeout 60s, IdleTimeout 120s (prevents slowloris)

New environment variables

Variable Default Description
UPTOP_METRICS_PUBLIC false Serve /metrics without auth when no cluster secret is set

Test plan

  • go build ./... passes
  • go test -race -timeout 120s ./... — all pass
  • golangci-lint run — 0 issues
## Summary - **Push token → header**: `Authorization: Bearer <token>` is now primary auth for push heartbeats. Query string `?token=` still works but logs a deprecation warning. - **Gotify token fix**: Moved from URL query param to `X-Gotify-Key` header (Gotify's preferred method) - **Rate limiting**: Per-IP token-bucket on all endpoints — push 60/min, probe 30/min, backup 10/min, status 120/min - **`/metrics` auth**: Gated behind cluster secret when set. `UPTOP_METRICS_PUBLIC=true` to opt out. - **Export redaction**: `/api/backup/export` redacts passwords/tokens by default. Append `?redact_secrets=false` for full export. - **Placeholder fix**: `rewritePlaceholders` now handles 100+ SQL parameters correctly via `strconv.Itoa` - **`AddSiteReturningID` race fix**: Uses `LastInsertId()` (SQLite) / `INSERT RETURNING` (Postgres) instead of insert-then-query-by-name - **HTTP server timeouts**: ReadTimeout 30s, WriteTimeout 60s, IdleTimeout 120s (prevents slowloris) ## New environment variables | Variable | Default | Description | |----------|---------|-------------| | `UPTOP_METRICS_PUBLIC` | `false` | Serve `/metrics` without auth when no cluster secret is set | ## Test plan - [x] `go build ./...` passes - [x] `go test -race -timeout 120s ./...` — all pass - [x] `golangci-lint run` — 0 issues
lerko added 1 commit 2026-05-26 01:19:59 +00:00
fix(security): phase 2 high-severity hardening
CI / test (pull_request) Successful in 4m31s
CI / lint (pull_request) Successful in 56s
d30d1460bd
- Push heartbeat accepts Authorization: Bearer header (query string deprecated)
- Gotify alerts use X-Gotify-Key header instead of token in URL
- Per-IP rate limiting on all API endpoints (token-bucket)
- /metrics gated behind cluster secret (UPTOP_METRICS_PUBLIC=true to opt out)
- Config export redacts passwords/tokens by default (redact_secrets=false to override)
- Fix rewritePlaceholders for 100+ SQL parameters
- Fix AddSiteReturningID/AddAlertReturningID race with LastInsertId/RETURNING
- HTTP server timeouts: read 30s, write 60s, idle 120s
lerko merged commit 7a8f2ad15b into main 2026-05-26 15:31:19 +00:00
lerko deleted branch security/phase-2-hardening 2026-05-26 15:31:19 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: lerkolabs/uptop#27