fix(security): enforce audited account, network and resource boundaries

Implement B01-B06 with regression coverage and retained validation evidence. Preserve the production ingress preflight; F04 runtime verification remains open.
This commit is contained in:
cesnimda
2026-09-09 18:21:47 +02:00
parent 7d641edc7b
commit 0954876295
116 changed files with 10101 additions and 122 deletions
+23 -8
View File
@@ -77,17 +77,32 @@ PostgreSQL is not implemented. Do not configure or recommend it without first ad
## Deployment flow
Production automation always selects `docker-compose.yml` explicitly. Local development must add
`docker-compose.dev.yml`; never add that file to a production command. The base configuration has no
host bindings for frontend, backend, ai-service, or bundled Ollama.
`docker-compose.dev.yml`; never add that file to a production command. The effective base configuration
publishes **frontend 3000 → 80** on all host interfaces for the recorded Traefik host-port dependency.
Backend, ai-service and bundled Ollama have no host-port bindings. CORS and Docker service names are
not network isolation. The backend also joins the operator-managed `jobtracker_shared` network for
existing service dependencies; its membership and other host services need operator inspection.
The external Traefik configuration is operator-owned and is not stored here. Before deployment it
must route only the exact host from `APP_PUBLIC_BASE_URL` to frontend port 80 on
`jobtracker_shared`, terminate TLS, replace `X-Forwarded-For` and `X-Forwarded-Proto=https`, and
expose no direct application or Ollama host ports.
must route only the exact host from `APP_PUBLIC_BASE_URL` to the recorded frontend host port 3000,
terminate TLS, and replace `X-Forwarded-For` with one client IP and `X-Forwarded-Proto=https`.
Do not migrate to container routing or bind port 3000 to loopback without verifying where Traefik runs.
Host firewall/Docker forwarding rules must restrict ingress appropriately; their existence is unproven.
Nginx independently rejects non-canonical Hosts except `/health`, forwards Traefik's sanitized
single-hop values, and reaches the backend only over `WEB_PROXY_SUBNET`. The backend fails startup
if forwarded-header trust is enabled without a valid known CIDR.
Nginx requires `INGRESS_PROXY_CIDRS` (space-separated observed Traefik peer CIDRs, preferably /32 or
/128), rejects other peers before serving application routes, rejects missing scheme/IP and forwarding
chains, and emits normalized forwarding metadata. It rejects non-canonical Hosts except the default
server's liveness-only `/health`. The backend trusts only `WEB_PROXY_SUBNET` and one hop. Never trust a
NAT/gateway address shared by both Traefik and untrusted callers. If peer identity cannot distinguish
them, stop rollout and establish a verified host binding/firewall or dedicated proxy route first.
Deployment requires Python 3 (standard library) and Docker Compose's JSON config support. The new
`scripts/validate-ingress.py` preflight checks the **rendered** production configuration before normal
deployment backup/build/replacement; backup-only runs remain independent of the new ingress setting.
CI runs `python3 scripts/test-ingress.py` without needing a Docker engine. Empty/missing peer values
fail safely; do not copy synthetic addresses from tests into production. Runtime/container verification
is still pending locally due to Docker Desktop failure. See the [B06 report and operator checks](../docs/audits/2026-09-07/validation/b06-completion.md)
before deploying this change. No production verification is implied by configuration tests.
1. push to `main`
2. Gitea Actions runs tests
3. if green, workflow uploads repo to server
+9
View File
@@ -590,6 +590,15 @@ if ! validate_deploy_config; then
exit 1
fi
# Render exactly the production file used below. Fail before backup/build/replacement;
# never print the rendered environment, which contains deployment secrets.
if [ "${DEPLOY_BACKUP_ONLY:-false}" != "true" ]; then
if ! compose --profile bundled-ollama config --format json | python3 scripts/validate-ingress.py; then
echo "Production ingress validation failed. No service was replaced."
exit 1
fi
fi
if ! backup_database; then
exit 1
fi
+3
View File
@@ -51,6 +51,9 @@ Verified by reading `deploy/deploy.sh` and `JobTrackerApi/Program.cs`:
it fails loud rather than silently invalidating every session on restart)
- `APP_PUBLIC_BASE_URL` — required canonical HTTPS origin with no path, query, fragment or
credentials; it controls generated links, OAuth callbacks, secure cookies and Host validation
- `INGRESS_PROXY_CIDRS` — required observed Traefik socket peers at nginx; prefer individual
/32 or /128 addresses. Do not guess the source IP or trust a shared NAT gateway. See the
[B06 deployment checks](../docs/audits/2026-09-07/validation/b06-completion.md) before rollout.
- `WEB_PROXY_SUBNET` — required dedicated IPv4 CIDR for nginx-to-backend traffic; confirm it
does not overlap any existing Docker network before deployment
- `AUTH_ADMIN_EMAIL` / `AUTH_ADMIN_PASSWORD` only if you want admin seeding on this boot