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:
+23
-8
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user