Skip to content

Security: ZJPex/LLMRouter

Security

SECURITY.md

Security Policy

LLMRouter holds upstream provider credentials and exposes a model-routing data plane plus an administrative control plane. Its supported deployment boundary is one trusted host or a small trusted network behind a firewall or same-origin HTTPS reverse proxy. Do not expose the backend directly to the public internet.

Supported Version

Security fixes target the latest main branch and the newest published release. Older snapshots may not receive backports unless a release notice says so.

Reporting A Vulnerability

Use the repository's GitHub Security Advisories → Report a vulnerability flow when available. Include:

  • affected version or commit;
  • prerequisites and a minimal reproduction;
  • the data, permission, network, or availability impact;
  • whether the issue is reachable from the data plane, dashboard, or a trusted administrator action;
  • only redacted logs and synthetic credentials.

If private reporting is unavailable, contact the repository owner through a private channel listed on their GitHub profile and ask to open a private channel. Do not place exploit details, provider keys, session tokens, backups, or a full .env in a public issue.

Assets To Protect

  • Provider API keys and any local router token.
  • Dashboard session cookies and password hashes.
  • Dashboard-issued API keys; only hashes are retained after creation.
  • PostgreSQL/JSON control state, which includes identity, policy, usage, IP, audit, and credential-variable metadata.
  • CLI backups, which contain password and API-key hashes and can restore state.
  • Prompts and provider responses, even though LLMRouter does not intentionally persist complete request/response bodies in its usage log.

Authentication Boundaries

  • /v1/*, /metrics, and detailed diagnostics require a router or dashboard- issued API key. For a shared deployment, create control-plane keys and set LLMROUTER_REQUIRE_CONTROL_API_KEYS=1 so the unrestricted legacy token is not accepted.
  • Dashboard users authenticate separately. Passwords use Argon2 hashes. Hash work runs outside the auth-state mutex on blocking workers, with at most four concurrent login hashes; waiting longer than five seconds returns HTTP 429. Unknown and disabled users still perform an equivalently expensive hash-class operation to reduce timing enumeration. Five failures for a username lock it for 15 minutes. Lockout counters, the worker gate, and sessions are process-local and reset on restart.
  • Session cookies are HttpOnly and SameSite=Lax. Set LLMROUTER_ADMIN_COOKIE_SECURE=1 whenever the dashboard is served over HTTPS.
  • Dashboard writes require a session, X-LLMRouter-CSRF, and an allowed Origin/Referer when present. LLMROUTER_ALLOWED_ORIGINS extends that write check; it does not enable browser CORS.
  • The backend has no general CORS response policy. Serve dashboard and API from one trusted origin.

Network And Provider URLs

  • Keep LLMROUTER_BIND and published Docker ports on loopback unless a trusted network or reverse proxy needs them.
  • Configure LLMROUTER_TRUSTED_PROXIES with exact proxy IPs/CIDRs. Forwarded client-IP headers are security inputs for IP policy and rate limiting. LLMRouter walks XFF from the connected peer right-to-left and removes only explicitly trusted hops; a single-hop proxy should overwrite XFF with its observed client address. Preserve the original Host authority including its port so browser Origin/Host write checks remain aligned.
  • Provider URL validation rejects non-HTTP schemes, userinfo, query strings, fragments, and literal private/link-local/metadata addresses by default, including private IPv4 addresses encoded as IPv4-mapped IPv6 literals. Local/custom providers have explicit loopback allowances. Credentials must come from environment-backed header configuration, never URL userinfo or a query parameter.
  • Non-local/non-custom Providers require HTTPS by default. Plain HTTP exposes the Provider API key and prompt/response content to every network hop. Use LLMROUTER_ALLOW_INSECURE_PROVIDER_HTTP=1 only for an explicitly trusted internal upstream; local/custom runtimes retain HTTP support for controlled local integration. The HTTP override does not disable private/metadata-IP protection.
  • Hostnames are not currently pinned or revalidated after DNS resolution. A hostname that resolves to an internal address is outside the current SSRF guard. Use outbound firewall rules or an allowlist when administrators are not fully trusted.
  • Upstream HTTP redirects are disabled and every GET/POST/SSE handshake requires 2xx. A 3xx is treated as an upstream failure and mapped to client-facing 502, not followed or exposed as a client redirect. Upstream non-stream bodies and raw SSE bytes are bounded, and streams have an idle timeout. SSE additionally rejects 204 and any missing/non-text/event-stream media type before local headers. Non-2xx/non-SSE error-body reads have total and idle timeouts, and a live stream must reach its protocol termination event rather than treating EOF as successful completion.
  • PostgreSQL connections use SQLx with rustls. Development mode defaults to prefer, which can fall back to an unencrypted connection when the server does not offer TLS; the default development Compose database therefore stays on its private bridge. Remote and production databases should use verify-full with a trusted root and a hostname that matches the server certificate. Enterprise mode requires verify-full and refuses to start without LLMROUTER_DATABASE_URL. Compose's constructed URL also requires a URL-safe password or an explicitly percent-encoded LLMROUTER_DATABASE_URL override.

LLMROUTER_ALLOW_PRIVATE_PROVIDER_URLS=1 deliberately weakens the URL boundary and should only be used for a trusted internal runtime.

Logs, Errors, And Backups

The usage log stores request ID, identity/team labels, model/provider, token and cost estimates, status, latency, retry/fallback, client IP, and a category-only error summary. Durable usage, request/attempt ledger, and Provider-health error fields remove request values, Tool validation paths, Provider bodies, URLs, and storage diagnostics. Startup migration also rewrites older retained error detail. LLMRouter does not intentionally persist prompts, complete messages, raw provider bodies, authorization headers, or plaintext keys.

The current authenticated caller can receive a bounded upstream error whose common credential fields and token patterns are redacted. That live response redaction remains best-effort and is not proof that arbitrary third-party text is safe to publish. It is deliberately separate from the stricter category-only durable audit record. Review client-captured errors before sharing them.

Common secret-bearing configuration, auth, control-store, login, password-input, and API-key creation types use custom or redacted Debug output, with regression tests for the sensitive fields. This reduces accidental tracing and diagnostic leaks; it does not remove secrets from process memory, crash/core dumps, or every third-party value. Keep dumps and unrestricted debug access disabled or tightly controlled.

LLMROUTER_USAGE_LOG_LIMIT controls retained usage records. Dashboard diagnostic snapshots are redacted but contain personal/usage data. CLI backups are complete restore artifacts and must be encrypted, access-controlled, and deleted under a defined retention policy.

Diagnostic snapshot export is a CSRF-protected POST /admin/backup operation and creates an audit event; no safe-method GET alias is exposed. Provider model discovery is likewise a mutating POST because it stores provider-test and audit state.

CLI validation/restore deeply deserializes both documents and enforces critical auth invariants, including unique identities, valid role/status/password hashes, and retention of an active admin. This reduces corrupt-restore risk but does not make the sequential auth/control replacement transactional; stop writers and retain the automatically saved previous values plus a storage-native backup.

Deployment Checklist

  1. Replace every placeholder and use a long unique admin password and router token.
  2. Run scripts/config-validate.sh; it fails closed on enterprise database/TLS policy, malformed PostgreSQL URLs and pool bounds, invalid lease timing, trusted-proxy CIDRs, and allowed origins as well as application settings. It does not test database reachability or the certificate chain. Never set LLMROUTER_ALLOW_NO_AUTH=1 on a shared host.
  3. Bind to loopback or place the service behind a firewall and same-origin HTTPS reverse proxy.
  4. Set secure cookies, exact trusted proxies, and the expected dashboard origin.
  5. Require dashboard-issued API keys for shared data-plane traffic.
  6. Protect .env, PostgreSQL/JSON state, journal logs, and complete backups.
  7. Keep request/response byte limits, stream timeouts, and concurrent-stream permits finite.
  8. Run the acceptance checks appropriate to auth, routing, or deployment changes.

Known Security Limits

  • The optional OIDC console sign-in preview is single-host and process-local; it is not complete enterprise IAM, distributed SSO/session coordination, SCIM, service-account lifecycle, or public multi-tenant isolation.
  • No complete DNS-rebinding protection for provider hostnames.
  • The default development Compose PostgreSQL service does not provision a server certificate; its prefer connection can remain plaintext inside the private bridge. Use a TLS-configured PostgreSQL endpoint with verify-full for an enterprise deployment.
  • Rate limits, concurrent-stream permits, and login/session state are not shared across instances. Stream permits remain occupied until body completion/drop.
  • Origin validation allows non-browser requests without Origin/Referer; it is not an authorization mechanism.
  • Secret redaction cannot recognize every credential format.
  • Compatibility user/API-key/team quota pre-check/update is not a transactional reservation under concurrency. The tenant budget ledger has a separate transactional reservation and settlement path in PostgreSQL mode.

See Architecture, Configuration, and Operations for the corresponding implementation limits.

There aren't any published security advisories