134 lines
7.4 KiB
Markdown
134 lines
7.4 KiB
Markdown
# Agent
|
|
|
|
Name: Agent 07 - Security And Deployment Hardening
|
|
|
|
Stage: Security And Deployment Hardening
|
|
|
|
Date: 2026-04-15
|
|
|
|
## Scope
|
|
|
|
Hardened the existing Go/chi/SQLite application for internet-facing deployment without changing the product model or admin/client flow.
|
|
|
|
Completed in this stage:
|
|
|
|
- added stricter HTTP server defaults:
|
|
- `ReadHeaderTimeout`
|
|
- `MaxHeaderBytes`
|
|
- higher default read/write/idle timeouts suitable for uploads and authenticated downloads
|
|
- added global security headers and route-specific cache controls:
|
|
- `Content-Security-Policy`
|
|
- `X-Frame-Options`
|
|
- `X-Content-Type-Options`
|
|
- `Referrer-Policy`
|
|
- `Permissions-Policy`
|
|
- `Cross-Origin-Opener-Policy`
|
|
- `Cross-Origin-Resource-Policy`
|
|
- `Strict-Transport-Security` when `APP_BASE_URL` is `https`
|
|
- `Cache-Control: no-store` on admin and protected client API responses
|
|
- `Vary: Cookie` or `Vary: Authorization` on the appropriate routes
|
|
- added trusted-proxy awareness so forwarded headers are only trusted when `TRUST_PROXY_HEADERS=true`;
|
|
- added in-memory rate limiting for:
|
|
- `POST /admin/login`
|
|
- protected client API routes under `/api/v1`
|
|
- added CSRF protection for all admin POST flows, including login, logout, project/tag/API key forms, tag attach or detach, archive or restore, and multipart release upload;
|
|
- rotated or cleared the admin CSRF cookie on login and logout, and scoped both session and CSRF cookies to `/admin`;
|
|
- tightened local data directory permissions for SQLite and data storage;
|
|
- added regression coverage for CSRF rejection, hardened cookie defaults, security headers, and both rate-limited route groups;
|
|
- added first-pass deployment assets:
|
|
- multi-stage `Dockerfile`
|
|
- hardened `docker-compose.yml`
|
|
- Caddy example
|
|
- Nginx example
|
|
- env example
|
|
- deployment, Proxmox, backup, and restore notes.
|
|
|
|
## Files Changed
|
|
|
|
- `/Users/delete/projects/update_server/internal/config/config.go`
|
|
- `/Users/delete/projects/update_server/internal/app/app.go`
|
|
- `/Users/delete/projects/update_server/internal/db/open.go`
|
|
- `/Users/delete/projects/update_server/internal/auth/service.go`
|
|
- `/Users/delete/projects/update_server/internal/http/router.go`
|
|
- `/Users/delete/projects/update_server/internal/http/security.go`
|
|
- `/Users/delete/projects/update_server/internal/http/rate_limit.go`
|
|
- `/Users/delete/projects/update_server/internal/http/csrf.go`
|
|
- `/Users/delete/projects/update_server/internal/http/auth_handlers.go`
|
|
- `/Users/delete/projects/update_server/internal/http/admin_common.go`
|
|
- `/Users/delete/projects/update_server/internal/http/admin_api_keys.go`
|
|
- `/Users/delete/projects/update_server/internal/http/view_data.go`
|
|
- `/Users/delete/projects/update_server/internal/http/auth_integration_test.go`
|
|
- `/Users/delete/projects/update_server/internal/http/projects_integration_test.go`
|
|
- `/Users/delete/projects/update_server/internal/http/api_keys_integration_test.go`
|
|
- `/Users/delete/projects/update_server/internal/http/security_integration_test.go`
|
|
- `/Users/delete/projects/update_server/web/templates/layouts/base.gohtml`
|
|
- `/Users/delete/projects/update_server/web/templates/pages/login.gohtml`
|
|
- `/Users/delete/projects/update_server/web/templates/pages/project_form.gohtml`
|
|
- `/Users/delete/projects/update_server/web/templates/pages/project_detail.gohtml`
|
|
- `/Users/delete/projects/update_server/web/templates/pages/tag_form.gohtml`
|
|
- `/Users/delete/projects/update_server/web/templates/pages/api_key_form.gohtml`
|
|
- `/Users/delete/projects/update_server/web/templates/partials/csrf_field.gohtml`
|
|
- `/Users/delete/projects/update_server/.dockerignore`
|
|
- `/Users/delete/projects/update_server/Dockerfile`
|
|
- `/Users/delete/projects/update_server/docker-compose.yml`
|
|
- `/Users/delete/projects/update_server/deploy/Caddyfile.example`
|
|
- `/Users/delete/projects/update_server/deploy/nginx.update-server.conf.example`
|
|
- `/Users/delete/projects/update_server/deploy/update-server.env.example`
|
|
- `/Users/delete/projects/update_server/docs/DEPLOYMENT.md`
|
|
- `/Users/delete/projects/update_server/docs/agents/handoffs/07-security-deploy.md`
|
|
|
|
## Database Changes
|
|
|
|
- no migrations or schema changes were required;
|
|
- SQLite remains the production database;
|
|
- deployment notes now explicitly call out WAL-mode backup and restore handling for:
|
|
- `db.sqlite`
|
|
- `db.sqlite-wal`
|
|
- `db.sqlite-shm`
|
|
- `artifacts/`.
|
|
|
|
## API Or Route Changes
|
|
|
|
- no new product endpoints were added;
|
|
- middleware behavior changed for existing routes:
|
|
- all `/admin` responses now emit hardened headers and `no-store` caching rules;
|
|
- all `/admin` POST routes now require a valid CSRF token;
|
|
- `POST /admin/login` is rate limited by client IP;
|
|
- protected `/api/v1` routes now emit hardened headers and `no-store` caching rules;
|
|
- protected `/api/v1` routes are rate limited by client IP before API-key auth;
|
|
- session cookies are now scoped to `/admin` and retain `HttpOnly`, `SameSite=Lax`, and conditional `Secure`;
|
|
- CSRF cookies are now scoped to `/admin` and use `HttpOnly`, `SameSite=Strict`, and conditional `Secure`.
|
|
|
|
## Commands And Tests Run
|
|
|
|
- `gofmt -w internal/config/config.go internal/app/app.go internal/db/open.go internal/auth/service.go internal/http/router.go internal/http/security.go internal/http/rate_limit.go internal/http/csrf.go internal/http/auth_handlers.go internal/http/view_data.go internal/http/admin_common.go internal/http/admin_api_keys.go internal/http/auth_integration_test.go internal/http/projects_integration_test.go internal/http/api_keys_integration_test.go internal/http/security_integration_test.go` - passed
|
|
- `go test ./internal/http` - passed
|
|
- `go test ./...` - passed
|
|
- `go build ./cmd/server` - passed
|
|
- `go build ./cmd/migrate` - passed
|
|
- `docker compose config` - could not run in this environment because `docker` is not installed
|
|
|
|
## Known Limitations
|
|
|
|
- rate limiting is in-memory and per-process, so counters reset on restart and are not shared across multiple app instances;
|
|
- Docker and reverse-proxy examples were added and reviewed statically, but they were not live-validated here because Docker is unavailable in this workspace;
|
|
- `Strict-Transport-Security` only appears when `APP_BASE_URL` is configured with `https`, which is the intended production setup behind TLS termination;
|
|
- the proxy still must be configured correctly to keep the app private and to strip or control forwarded headers before `TRUST_PROXY_HEADERS=true` is safe.
|
|
|
|
## Recommended Next Step
|
|
|
|
Run the Agent 07 validation pass, with special attention to:
|
|
|
|
- CSRF enforcement on every admin POST flow, including multipart upload;
|
|
- security headers and private-cache headers on admin and protected client API responses;
|
|
- login and client API rate limiting behavior;
|
|
- cookie flags and `/admin` cookie scoping;
|
|
- deployment docs and example proxy/container files on a host that has Docker and a reverse proxy available.
|
|
|
|
## Notes For Validator
|
|
|
|
- verify that a missing or invalid admin CSRF token returns `403` and that the normal admin flows still succeed when the token is present;
|
|
- verify that session cookies are only scoped to `/admin` and that secure-cookie behavior follows the configured `APP_BASE_URL` scheme;
|
|
- verify that `TRUST_PROXY_HEADERS=false` leaves client IP handling on the socket remote address;
|
|
- verify that protected `/api/v1` responses include `Cache-Control: no-store`, `Vary: Authorization`, and the shared security headers;
|
|
- if possible, validate `docker compose up --build` plus one reverse-proxy example on a real machine, since that could not be executed in this environment.
|