Flight-booking app scaffolded from the source running on the GCP host, plus the four-screen frontend and three OWASP attack endpoints (SQLi, reflected XSS, path traversal) built and locally verified against a live uvicorn/vite dev pair. Includes deploy.sh (local build -> ship over ssh -> swap on host) and docs/ carried over from the handoff.
4.3 KiB
Architecture
Overview
Single GCP e2-micro running three rootless Podman containers under user matt.
Imperva Cloud WAF sits in front of everything. The origin is network-isolated so that
the only things that can reach the app are Imperva's edge nodes.
Visitor ──TLS──▶ Imperva Cloud WAF ──TLS (re-encrypt)──▶ Caddy :443 ──HTTP──▶ backend/frontend
(terminates, (self-signed cert)
inspects,
re-encrypts)
The two TLS legs (this is the crux of the design)
There are two independent TLS connections, and they use different certificates. Mixing them up is the single easiest way to break this setup.
Leg 1 — Visitor → Imperva (edge).
Imperva presents a publicly-trusted certificate for flytoouter.space to browsers.
This is Imperva's managed cert (auto-provisioned by Imperva). It must be publicly
trusted or every visitor gets a browser warning. We do not touch this with our
self-signed cert.
Leg 2 — Imperva → origin (re-encryption).
Imperva opens a second TLS connection back to the GCP origin and inspects/re-encrypts
before forwarding. Caddy serves this leg on :443 with a self-signed certificate.
Imperva is the only client that ever sees this cert, so self-signed is fine and
produces no visitor-facing warnings.
DNS for flytoouter.space must point at Imperva, not at 35.203.147.44. If it
resolves straight to the origin, browsers try to negotiate TLS with the self-signed
origin cert and fail.
Request flow through the origin
Caddy (caddy-proxy) is the only container bound to a public port. It terminates the
re-encryption TLS and reverse-proxies over plain HTTP to the app containers via the
host.containers.internal alias on the default rootless bridge network:
handle /api/*→host.containers.internal:8000(FastAPI backend)handle /*→host.containers.internal:8080(Vue frontend)
handle (not handle_path) is used so the /api prefix is preserved for the backend
routes (/api/v1/users, etc.).
Container stack
| Container | Image | Port | Role |
|---|---|---|---|
caddy-proxy |
docker.io/library/caddy:2-alpine |
443:443 |
TLS termination (re-encryption leg) + reverse proxy |
backend-api |
custom Python 3.11-slim (FastAPI) | 8000:8000 |
REST API + local SQLite demo.db |
frontend-vue |
custom multi-stage (Node build → Nginx) | 8080:8080 |
Vue SPA (flight booking UI) |
Caddy currently serves only :443. There is intentionally no :80 site block (see
DECISIONS.md — a stray :80 block under auto_https off caused the ambiguous site definition failure). If Imperva needs an HTTP health check, an :80 block can be
added back as a separate, non-conflicting listener.
Network / edge security
- Origin target tag:
imperva-origin. allow-imperva-waf: HTTP/HTTPS ingress restricted to Imperva's published IPv4 CIDR blocks (e.g.199.83.128.0/21,198.143.32.0/19, …). Confirmtcp:443is in the allowed ports since the origin path is now 443-only.allow-home-ssh: SSH (22) locked to the operator's static residential IP.- All default open (
0.0.0.0/0) rules purged.
Because the origin only answers Imperva CIDRs, the deliberately-vulnerable endpoints are unreachable directly. Every attack must pass through the WAF — which is exactly why they register as blocked events in the portal.
Host hardening (from original build)
/swapfilemounted via/etc/fstabto mitigate OOM kills on the burstable e2-micro.loginctl enable-linger mattso rootless containers persist across reboots.sysctl net.ipv4.ip_unprivileged_port_start=80to let rootless Caddy bind low ports.
Cert details (origin, Leg 2)
- Self-signed, RSA 2048, 3650-day validity.
- SAN covers both
DNS:flytoouter.spaceandIP:35.203.147.44, so whichever way Imperva addresses the backend, the cert matches. - Files live on the host at
~/imperva-proxy/certs/{origin.crt,origin.key}and are bind-mounted read-only into Caddy at/etc/caddy/certs/.