ADR-0002: Zero-Trust Access Perimeter for @@G1G@@
one login gates every internal tool, new tools protected the day they ship, and $0 at our size — nothing to self-host or patch. Surfaced by ADR-0001.1, but an org-wide perimeter decision in its own right.
Context
Every internal tool needs the same thing: "only Propel humans, MFA'd." Building that per app (a Cognito gate here, a Lambda@Edge there) is duplicated, inconsistent, and slow. We already front our domain with Cloudflare and run a global Cognito IdP for application identity.
Decision TBD
Suggested decision — not yet agreed, expand to review
Adopt Cloudflare Access as the org-wide zero-trust edge over *.propellabs.io. One policy set gates every internal subdomain; the existing Cognito IdP stays the application identity layer.
flowchart LR
U([Propel engineers]) -->|Google SSO + MFA| ACC[Cloudflare Access
*.propellabs.io]
ACC --> D[docs.propellabs.io]
ACC --> O[grafana.propellabs.io]
ACC --> A[admin.propellabs.io]
ACC -.signed JWT = identity.-> BE[app backends
reuse Cognito for authZ]
style ACC fill:#EDEDFF,stroke:#3B54F7
- One perimeter, every tool. A new internal app = a new subdomain → instantly protected. No per-app gate.
- Managed-HA proxy mode, not a
cloudflaredtunnel — resolves the tunnel-reliability concern. - Two layers stay separate: Access = edge gate + coarse identity (email/groups in a signed JWT); Cognito = fine-grained app identity/authZ. Origins are locked to Cloudflare and still validate the JWT (never trust network position alone).
Consequences
Positive — one consistent zero-trust layer; MFA, device posture, and audit centralized; new tools protected for free; nothing to self-host or patch; $0 at our size (Access free ≤50 users; we're ~10–12). Negative / risks — Cloudflare becomes the critical path for internal access → mitigate with a documented break-glass route and origin-side JWT validation; every internal tool must sit behind the proxied domain.
Alternatives considered (with cost)
| Option | Cost | Why not |
|---|---|---|
| Cloudflare Access ✅ | $0 ≤50 users (then ~$7/user) | — chosen |
| Per-app gates (Cognito + Lambda@Edge each) | ~$0 infra but N× build/maintain | Duplicated, inconsistent, slow to add tools |
| AWS Verified Access | ~$200/mo per app ($0.27/hr/endpoint + $0.02/GB) | AWS-native zero-trust, but priced per app-endpoint — expensive across many tools |
| VPN | VPN infra + ops | Not zero-trust; clunky for browser tools and external-ish access |
Open questions
- Which subdomains are in scope first (docs, Grafana), and group-based policies (who sees what)?
- Break-glass procedure if Cloudflare Access is unavailable.
- Client-facing surfaces stay out of this perimeter (they use per-client access, per the observability model).
Discussion
#cloudops. This ADR gates ADR-0001.1 — the docs crawl rides on adopting it.