Skip to content

infra-feat: harden edge origin trust#430

Open
Seranged wants to merge 1 commit into
developmentfrom
feature/eul4-211-deepsecinfra-harden-edge-origin-trust-for-api-protections
Open

infra-feat: harden edge origin trust#430
Seranged wants to merge 1 commit into
developmentfrom
feature/eul4-211-deepsecinfra-harden-edge-origin-trust-for-api-protections

Conversation

@Seranged

@Seranged Seranged commented May 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Harden the API edge/origin trust boundary so public Cloudflare headers are only trusted after the request is verified as coming through trusted ingress.
  • Replace the spoofable loopback CF-Connecting-IP internal sentinel with a private internal request header for server-side $fetch calls.
  • Document the production ingress contract so deployers know which headers must be added, stripped, or overwritten at the edge.

Changes

  • Add shared runtime helpers for production detection and trusted ingress validation, including fail-closed behavior when NODE_ENV=production is present without a known Doppler environment.
  • Enforce trusted ingress in CORS, geo-gate, and rate-limit paths before using Cloudflare country or client-IP headers.
  • Keep server-internal fetches working through a private x-euler-internal-request header, with an optional shared INTERNAL_FETCH_SECRET for multi-worker deployments.
  • Add focused server tests for spoofed internal headers, production preflights, trusted-ingress failures, production env fallback, and rate-limit identity behavior.

DevOps rollout notes

  • Add a new server-side secret in production: EDGE_ORIGIN_SECRET.
  • Configure trusted ingress to add x-euler-edge-origin-secret: <EDGE_ORIGIN_SECRET> on requests forwarded to the app origin.
  • Ensure ingress strips or overwrites any client-supplied x-euler-edge-origin-secret, x-euler-internal-request, and CF-* forwarding headers before forwarding to origin.
  • Keep direct origin access blocked except from Cloudflare/trusted ingress. This PR makes the app fail closed when production-like traffic reaches origin without the trusted-ingress marker.
  • Review health checks: production checks that hit /api/* directly must either run through trusted ingress or include the trusted ingress header. The Docker health check currently calls /api/tenderly/status.
  • INTERNAL_FETCH_SECRET is optional. Only set it if server-internal relative $fetch calls can cross worker/process boundaries; otherwise the process-local random default is fine.
  • Non-prod environments should keep DOPPLER_ENVIRONMENT=dev or DOPPLER_ENVIRONMENT=stg. If they run with NODE_ENV=production and no known Doppler environment, they now behave production-like and require trusted ingress.

Test plan

  • npm run test:run -- tests/server/internal-request.test.ts tests/server/cors.test.ts tests/server/geo-gate.test.ts tests/server/rate-limit.test.ts
  • npm run test:run
  • npm run typecheck
  • npm run lint
  • Smoke-tested built Nitro server with production-like trusted and untrusted ingress headers, including API preflights and NODE_ENV=production fallback.

Summary by CodeRabbit

  • New Features

    • Added production trusted-ingress validation requiring new environment variables for secure edge-origin verification.
    • Enhanced rate-limiting and geo-blocking to fail-closed in production when trusted-ingress signals are absent.
  • Documentation

    • Updated architecture documentation with production security requirements and trusted-ingress specifications.
    • Expanded geo-blocking guidance covering edge validation, header handling, and production behavior.
  • Tests

    • Added comprehensive test coverage for CORS, geo-gate, rate-limiting, and internal request handling.

Review Change Stack

@railway-app

railway-app Bot commented May 12, 2026

Copy link
Copy Markdown

🚅 Deployed to the euler-lite-pr-430 environment in euler-lite

Service Status Web Updated (UTC)
production-master-branch ✅ Success (View Logs) Web May 13, 2026 at 3:55 pm

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b13bd00a-e899-40a3-860e-6f0753bdcf1a

📥 Commits

Reviewing files that changed from the base of the PR and between 249d1c9 and 85e30c5.

📒 Files selected for processing (13)
  • .env.example
  • docs/architecture.md
  • docs/geo-blocking.md
  • server/middleware/cors.ts
  • server/middleware/geo-gate.ts
  • server/utils/deploy-env.ts
  • server/utils/internal-headers.ts
  • server/utils/rate-limit.ts
  • server/utils/trusted-ingress.ts
  • tests/server/cors.test.ts
  • tests/server/geo-gate.test.ts
  • tests/server/internal-request.test.ts
  • tests/server/rate-limit.test.ts
🚧 Files skipped from review as they are similar to previous changes (12)
  • server/utils/trusted-ingress.ts
  • docs/architecture.md
  • server/utils/deploy-env.ts
  • docs/geo-blocking.md
  • server/utils/rate-limit.ts
  • .env.example
  • tests/server/cors.test.ts
  • tests/server/rate-limit.test.ts
  • server/utils/internal-headers.ts
  • tests/server/geo-gate.test.ts
  • server/middleware/geo-gate.ts
  • server/middleware/cors.ts

📝 Walkthrough

Walkthrough

This PR hardens production request validation by introducing a trusted ingress system using EDGE_ORIGIN_SECRET. It replaces the loopback IP internal request sentinel with a cryptographic x-euler-internal-request header, centralizes environment detection via isProductionRuntime() and isDevelopmentRuntime(), and adds fail-closed guards across CORS, geo-gate, and rate-limit middleware when the trusted ingress secret is absent in production.

Changes

Trusted Ingress and Internal Request Validation

Layer / File(s) Summary
Runtime detection, request trust, and internal sentinel infrastructure
server/utils/deploy-env.ts, server/utils/trusted-ingress.ts, server/utils/internal-headers.ts, .env.example, tests/server/internal-request.test.ts
Introduces isProductionRuntime() and isDevelopmentRuntime() helpers with DOPPLER_ENVIRONMENT trimming and fallback to NODE_ENV. Defines isTrustedIngressRequest() contract using EDGE_ORIGIN_SECRET header validation. Replaces cf-connecting-ip: 127.0.0.1 loopback sentinel with x-euler-internal-request header derived from INTERNAL_FETCH_SECRET (trimmed) or process-local UUID. Adds EDGE_ORIGIN_SECRET and INTERNAL_FETCH_SECRET to .env.example. Tests validate internal header presence/non-emptiness and reject spoofed loopback IPs.
CORS geo-country and production ingress validation
server/middleware/cors.ts, tests/server/cors.test.ts
CORS middleware uses isDevelopmentRuntime() for origin derivation, refactors geo-country extraction to only trust cf-ipcountry after trusted ingress validation, strips client-supplied x-country-code, and adds production-only HTTP 403 block for non-internal untrusted requests. Tests verify trusted-ingress requirements for country headers, client header stripping, production preflight rejection, internal request bypass, and dev fallback behavior.
Geo-gate production trusted ingress requirement
server/middleware/geo-gate.ts, tests/server/geo-gate.test.ts
Geo-gate middleware short-circuits internal requests, enforces production requests to pass isTrustedIngressRequest() with HTTP 403 on failure, and throws HTTP 451 when country is undetermined in non-development runtimes. Tests validate trusted ingress requirements, cf-ipcountry dependency, internal fetch bypass, and production NODE_ENV handling.
Rate limiter production trusted ingress requirement
server/utils/rate-limit.ts, tests/server/rate-limit.test.ts
Rate-limit utility introduces assertProductionIngressTrusted() helper validating both cf-connecting-ip presence and trusted ingress, integrates it into consume() under isProductionRuntime() guard. Tests verify edge rejection when configuration/trust headers are missing, acceptance with trusted secret, spoofed loopback rejection, and internal request bypass of edge/rate-limit checks.
Security architecture and geo-blocking documentation
docs/architecture.md, docs/geo-blocking.md
Architecture docs update rate-limiting fail-closed behavior and Cloudflare requirement sections to document trusted ingress dependency. Geo-blocking docs clarify server-authoritative x-country-code derivation from Cloudflare after trusted-ingress verification, expand boundary enforcement with header stripping rules and production health-check requirements, and note that internal $fetch calls bypass geo-gate/rate-limit edge checks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • euler-xyz/euler-lite#136: Modifies geo-blocking flow in server/middleware/geo-gate.ts to harden fail-closed behavior using Cloudflare cf-ipcountry and DEV_GEO_COUNTRY override, directly related to this PR's geo-gate trusted-ingress integration.
  • euler-xyz/euler-lite#291: Modifies server/utils/internal-headers.ts and server/middleware/geo-gate.ts to bypass geo-gate for internal requests; this PR further replaces the loopback sentinel with x-euler-internal-request and adds trusted-ingress checks.
  • euler-xyz/euler-lite#161: Adds CF-Connecting-IP presence check in server/utils/rate-limit.ts for production fail-closed behavior; this PR expands that to require trusted-ingress validation via isTrustedIngressRequest.

Suggested reviewers

  • kasperpawlowski

Poem

🐰 A secret flows through the edge so bright,
Trusted ingress marks the path just right.
No more loopback tricks to pass the gate,
Private headers seal a safer fate!
From geo-block to rate-limit's call,
The rabbit approves this security wall. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'infra-feat: harden edge origin trust' clearly and directly summarizes the primary objective of the pull request—hardening the API edge/origin trust boundary.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/eul4-211-deepsecinfra-harden-edge-origin-trust-for-api-protections

Comment @coderabbitai help to get the list of available commands and usage tips.

@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-430 May 12, 2026 15:58 Destroyed
@Seranged Seranged force-pushed the feature/eul4-211-deepsecinfra-harden-edge-origin-trust-for-api-protections branch from f1f97fc to 249d1c9 Compare May 12, 2026 16:04
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-430 May 12, 2026 16:04 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/utils/deploy-env.ts`:
- Around line 12-14: isDevelopmentRuntime() currently compares
process.env.DOPPLER_ENVIRONMENT raw value which can mis-detect values with
whitespace; update isDevelopmentRuntime() to normalize the env var the same way
as isProductionRuntime() (trim() and optionally toLowerCase()) before comparing
to 'dev' so inputs like 'dev ' or ' DEV' are correctly recognized; adjust the
comparison in the isDevelopmentRuntime function (and ensure consistency with
isProductionRuntime) referencing the isDevelopmentRuntime and
isProductionRuntime functions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ad8855cf-ef17-4f66-b5e2-ac094a21dbff

📥 Commits

Reviewing files that changed from the base of the PR and between c8dc79b and 249d1c9.

📒 Files selected for processing (13)
  • .env.example
  • docs/architecture.md
  • docs/geo-blocking.md
  • server/middleware/cors.ts
  • server/middleware/geo-gate.ts
  • server/utils/deploy-env.ts
  • server/utils/internal-headers.ts
  • server/utils/rate-limit.ts
  • server/utils/trusted-ingress.ts
  • tests/server/cors.test.ts
  • tests/server/geo-gate.test.ts
  • tests/server/internal-request.test.ts
  • tests/server/rate-limit.test.ts

Comment thread server/utils/deploy-env.ts
@Seranged Seranged force-pushed the feature/eul4-211-deepsecinfra-harden-edge-origin-trust-for-api-protections branch from 249d1c9 to 88992a8 Compare May 13, 2026 15:30
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-430 May 13, 2026 15:30 Destroyed
@Seranged Seranged force-pushed the feature/eul4-211-deepsecinfra-harden-edge-origin-trust-for-api-protections branch from 88992a8 to 85e30c5 Compare May 13, 2026 15:54
@Seranged Seranged changed the title Harden edge origin trust infra-feat: harden edge origin trust May 13, 2026
@Seranged Seranged marked this pull request as ready for review May 13, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant