fix: complete remaining remediation (#5, #8, #9)
Some checks failed
Security Checks / dependency-audit (push) Has been cancelled
Security Checks / secret-scanning (push) Has been cancelled
Security Checks / dockerfile-lint (push) Has been cancelled

#5 Gateway Trust Model:
- Token validation now uses protected endpoints, not health checks
- Unknown services rejected (no fallback to unprotected endpoint)
- Trust model documented in docs/trust-model.md

#8 CI Enforcement:
- Added .gitea/workflows/security.yml with:
  - Dependency audit (npm audit --audit-level=high for budget)
  - Secret scanning (checks for tracked .env/.db, hardcoded secrets)
  - Dockerfile lint (non-root USER, HEALTHCHECK presence)

#9 Performance Hardening:
- Budget /summary: 1-minute in-memory cache (avoids repeated account fan-out)
- Gateway /api/dashboard: 30-second per-user cache (50x faster on repeat)
- Inventory health endpoint added before auth middleware

Closes #5, #8, #9
This commit is contained in:
Yusuf Suleman
2026-03-29 10:13:00 -05:00
parent 72747668f9
commit 4ecd2336b5
5 changed files with 212 additions and 41 deletions

52
docs/trust-model.md Normal file
View File

@@ -0,0 +1,52 @@
# Gateway Trust Model
## Architecture
All frontend requests go through: Browser → Pangolin → frontend-v2 (SvelteKit hooks) → gateway → backend services.
## Authentication Layers
### Gateway (platform auth)
- Users authenticate via `/api/auth/login` with username/password (bcrypt)
- Session stored as `platform_session` cookie (HttpOnly, Secure, SameSite=Lax)
- All `(app)` routes require valid session (checked in `+layout.server.ts`)
### Service-level auth
Each backend service has its own auth mechanism. The gateway injects credentials when proxying:
| Service | Auth Type | Injected By Gateway | Validated Against |
|---------|-----------|--------------------|--------------------|
| Trips | Bearer token | `Authorization: Bearer {token}` | `/api/trips` (protected endpoint) |
| Fitness | Bearer token | `Authorization: Bearer {token}` | `/api/user` (protected endpoint) |
| Reader | API key | `X-Auth-Token: {key}` | `/v1/feeds/counters` |
| Inventory | API key | `X-API-Key: {key}` | `/summary` |
| Budget | API key | `X-API-Key: {key}` | `/summary` |
| Books (Shelfmark) | None (proxied) | — | Gateway auth only |
| Music (Spotizerr) | None (proxied) | — | Gateway auth only |
### Frontend hooks auth (SvelteKit)
- Immich proxy: validates `platform_session` cookie before proxying
- Karakeep proxy: validates `platform_session` cookie before proxying
- Legacy trips Immich: validates `platform_session` cookie before proxying
## Service Connections
- Users connect services via Settings page
- Token validation uses a **protected endpoint**, not health checks
- Unknown services cannot be connected (rejected with 400)
- Tokens stored in `service_connections` table, per-user
## Internal Network
- All services communicate on Docker internal network
- No service port is exposed to the host (except frontend-v2 via Pangolin)
- Gateway is the single entry point for all API traffic
## TLS
- External HTTPS: default TLS verification (certificate + hostname)
- Internal services: `_internal_ssl_ctx` with verification disabled (Docker services don't have valid certs)
- Image proxy: default TLS verification + domain allowlist
## Secrets
- All secrets loaded from environment variables
- No hardcoded credentials in code
- `.env` files excluded from git
- Admin credentials required via `ADMIN_USERNAME`/`ADMIN_PASSWORD` env vars