Skip to main content

Context

eigenoid needs to expose svc-access APIs (Cloud Run) to two audiences with different security requirements:

  • Admin (internal users authenticated via CF Access + GitHub org)
  • Public (clients authenticated via magic link)

Constraints:

  1. Mandatory domain separation — admin and public must live on different domains for cookie isolation and security policies.
  2. Backends not directly exposed — Cloud Run only accessible via CF Tunnel (no public IP).
  3. Header sanitization — prevent clients from injecting internal headers (CF Access JWT, service tokens, proxy headers).
  4. Strict CORS — only our own portals can make cross-origin requests.
  5. Minimal cost — startup with a limited budget; we cannot justify an enterprise managed API Gateway.
  6. Low latency — the gateway must not add significant latency to the happy path.

Decision

Use Cloudflare Workers as the API Gateway layer in front of the CF Tunnels.

Each audience has its own Worker:

  • Admin Gateway (api-dev.eigenoid.services) — routes /v1/access/admin/*
  • Public Gateway (api-dev.eigenoid.com) — routes /v1/access/auth/*, /v1/access/clients/me/*, /v1/access/download/*

The Workers handle:

  • Path rewriting (strip /v1 prefix)
  • Header sanitization (strip internal headers, inject service token)
  • Conditional CORS (strict origin match)
  • Fixed-origin proxying (SSRF-safe)

Consequences

  • ~0ms additional latency: Workers execute on the same edge that resolves DNS — no additional hop. Proxying to the tunnel adds the normal RTT to Cloud Run.
  • $0 cost on the free tier: 100K requests/day free per Worker. More than enough for dev/staging; even initial production fits within the free tier.
  • Limited observability: Workers have no native persistent logging — we will need Workers Analytics or tail logs for debugging. Logpush can be added in the future.
  • Vendor lock-in at the edge: routing logic is coupled to the Cloudflare Workers API (fetch, Request, ExecutionContext). Migrating to another edge provider requires rewriting the Workers.
  • No built-in rate limiting: Workers alone do not have rate limiting. If needed, Workers KV/Durable Objects or CF WAF rules would need to be added separately.
  • Full unit testing possible: the Workers API is compatible with Vitest/Miniflare, enabling high test coverage without deployment.
  • Secrets at runtime, not in CI: service tokens are configured via wrangler secret put, not as GHA secrets. This reduces the attack surface in CI.

Alternatives considered

  • Direct CF Tunnel (no gateway): Exposes tunnel hostnames directly to the browser. Does not allow header sanitization, unified CORS, or path rewriting. Each route would need individual CF Access policies. Rejected due to lack of control at the edge.
  • Nginx sidecar in Cloud Run: Add an nginx container as a reverse proxy inside Cloud Run. Solves header sanitization but not domain separation or CORS (those still depend on the backend or another component). Adds operational complexity (sidecar lifecycle, config management). Rejected for duplicating functionality that CF Workers provide for free.
  • GCP API Gateway / Apigee: GCP's managed API Gateway. Solves routing and auth but requires exposing Cloud Run with a public IP (no tunnels). Significant cost (~$3/million calls on Apigee). Does not integrate natively with CF Access. Rejected due to cost and breaking the origin isolation model via tunnels.
  • AWS API Gateway + CloudFront: Would require migrating or duplicating infrastructure on AWS. Does not integrate with CF Access or CF Tunnels. Rejected due to unnecessary multi-cloud complexity.

References