Files
OmniRoute/fly.toml
diegosouzapw 262c4e9046 fix(security): container/Fly REQUIRE_API_KEY posture + free-tier usage leak (#13679)
PR E of the #13679 insecure-defaults umbrella (items #6, #7; item #8 analyzed
as by-design, no change). The published Docker image and fly.toml shipped
without REQUIRE_API_KEY set, so a bare `docker run` (README/QUICK-START
one-liners, no --env-file) or a `fly deploy` combined "keyless" with
"world-reachable" for the anonymous /v1 LLM proxy. docker-compose.yml already
mitigates this via loopback-only binding (#12568) and correctly keeps
following the operator's own .env, so it is untouched. The npm/CLI
local-first REQUIRE_API_KEY=false default in featureFlagDefinitions.ts is
also untouched per the owner's decision.

/api/free-tier/summary ships an unconditional Access-Control-Allow-Origin: "*"
and always included the operator's own local usedThisMonth/remaining usage
regardless of auth — a low-severity info leak to any reachable origin. Both
fields are now withheld from unauthenticated callers while the intentionally
public catalog data stays served to everyone.

The gemini-SSE (openai-to-gemini-sse.ts) sub-finding needed no code change:
/v1beta/models/*:streamGenerateContent is already classified CLIENT_API and
fronted by clientApiPolicy through src/proxy.ts before the translator ever
runs, and its CORS-header echo was already hardened fail-closed by #12573.
REQUIRE_API_KEY=true (this PR's container/Fly default) closes the dependency
that finding cited. Added a locking regression test confirming this chain.

Regression tests:
- tests/unit/issue-13679-container-posture-require-api-key.test.ts
- tests/unit/issue-13679-free-tier-summary-usage-leak.test.ts
- tests/unit/issue-13679-gemini-sse-requires-api-key.test.ts (confirmation)

Refs #13679
2026-09-16 14:08:52 -03:00

53 lines
1.3 KiB
TOML

# fly.toml app configuration file generated for oroute on 2026-03-20T09:12:34Z
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'omniroute'
primary_region = 'sin'
[build]
[processes]
app = 'node run-standalone.mjs'
[deploy]
# release_command = "node ./dbsetup.js"
[[mounts]]
source = 'data'
destination = '/data'
auto_extend_size_threshold = 80
auto_extend_size_increment = '1GB'
auto_extend_size_limit = '10GB'
[http_service]
internal_port = 20128
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 1
processes = ['app']
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1
memory_mb = 1024
[env]
TZ = "Asia/Shanghai"
# Bind to all interfaces for Fly runtime networking.
HOST = "0.0.0.0"
HOSTNAME = "0.0.0.0"
BIND = "0.0.0.0"
# #13679: a Fly deployment is reachable over the public internet by design
# (force_https + Fly's edge proxy in [http_service] above) — unlike the
# docker-compose path, there is no loopback-bind option here. Require an
# API key by default so a `fly deploy` from this manifest is never keyless
# AND world-reachable at once. This does not change the npm/CLI local-dev
# default; it is specific to this published Fly manifest.
REQUIRE_API_KEY = "true"