Skip to content

feat(server): add proxy.require_api_key to authenticate proxy routes - #2477

Closed
shijinfeng57 wants to merge 1 commit into
opensandbox-group:mainfrom
shijinfeng57:feat/proxy-require-api-key
Closed

shijinfeng57 wants to merge 1 commit into
opensandbox-group:mainfrom
shijinfeng57:feat/proxy-require-api-key

Conversation

@shijinfeng57

Copy link
Copy Markdown

Summary

In single-tenant mode the auth middleware skips the API key check on /sandboxes/{id}/proxy/{port}/..., and WebSocket proxy connections are only authenticated in multi-tenant mode. Multi-tenant mode requires the Kubernetes runtime (validate_tenant_config rejects [tenants] with runtime.type = "docker"), so a Docker deployment has no way to require a key on proxy routes. Any process that can reach the server can then use the proxy to reach sandbox ports.

We hit this while putting OpenSandbox behind our own gateway, which authenticates users and forwards to the server proxy with OPEN-SANDBOX-API-KEY. With the server bound to 127.0.0.1, other machines are blocked, but any other process on the same host could still open a sandbox's preview without a key.

This adds an opt-in [proxy] require_api_key (default false, so current behavior is unchanged):

 AuthMiddleware.dispatch (HTTP)
   exempt paths (/health, /docs, ...) -> pass
-  proxy path and single-tenant      -> pass
+  proxy path and single-tenant and not proxy.require_api_key -> pass
   check OPEN-SANDBOX-API-KEY

 _authenticate_websocket_tenant (WebSocket proxy)
   no tenant provider
-    -> allow
+    proxy.require_api_key false -> allow
+    else compare key with server.api_key (hmac.compare_digest)
+         mismatch or missing -> accept, close 1008
  • Setting require_api_key = true without server.api_key (and without [tenants]) is rejected at config load, so the routes cannot silently stay open.
  • Documented in configuration.md (also notes the existing proxy exemption on the api_key row) and the four example configs (commented out).

Testing

  • Not run (explain why)
  • Unit tests
  • Integration tests
  • e2e / manual verification

New tests, run on the unpatched source and then with the change:

Test Before After
test_auth_middleware_proxy_requires_key_when_require_api_key (no key / wrong key → 401, right key → 200) fail pass
test_require_api_key_without_server_api_key_is_rejected fail pass
test_proxy_websocket_requires_key_when_require_api_key (no key / wrong key → close 1008, right key → relayed) fail pass

uv run ruff check passes. Full server suite on this branch (rebased on main at cac36146): 2623 passed, 32 skipped, coverage 84.69%. The existing WebSocket relay test stubs get_config() with only proxy.resolve_internal; it now also sets require_api_key=False.

Manual check on a Docker + gVisor deployment with require_api_key = true, against a running sandbox:

Request to /v1/sandboxes/{id}/proxy/3000/ Result
HTTP, no key / wrong key 401
WebSocket /_next/webpack-hmr, no key handshake, then close 1008, no data relayed
HTTP with key 200, page served
WebSocket with key HMR messages relayed

Breaking Changes

  • None
  • Yes (describe impact and migration path)

Default is false. Configs that set require_api_key = true without server.api_key fail to load, which is intended.

Checklist

  • Linked Issue or clearly described motivation
  • Added/updated docs (if needed)
  • Added/updated tests (if needed)
  • Security impact considered
  • Backward compatibility considered

Security note: with the option on, browsers can no longer open server-proxy URLs directly (they cannot send the header), so it fits deployments where a gateway or backend calls the proxy. Multi-tenant mode is unchanged and still authenticates proxy routes through the tenant provider.

🤖 Generated with Claude Code

In single-tenant mode the auth middleware skips the API key check on
/sandboxes/{id}/proxy/{port}, and WebSocket proxy connections are only
authenticated in multi-tenant mode, which the docker runtime does not
support. Any process that can reach the server can therefore use the
proxy to reach sandbox ports without a key.

Add [proxy] require_api_key (default false, behavior unchanged). When
true, the HTTP and WebSocket proxy routes also require
OPEN-SANDBOX-API-KEY in single-tenant mode; the WebSocket check uses a
constant-time compare and closes with 1008 on a missing or wrong key.
Enabling it without server.api_key (and without [tenants]) is rejected
at config load so the routes cannot silently stay open.

Document the option in configuration.md and the example configs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added component/server size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Oct 29, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-29T08:16:05.938512Z
We couldn’t complete this review. Comment @codex review to try again.
d5d4bc2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Pangjiping

Copy link
Copy Markdown
Collaborator

Thanks for the contribution and for clearly explaining the security concern.

We no longer plan to introduce further updates to the server proxy. This data path is slated for deprecation: routing sandbox data-plane traffic through the control-plane server is not suitable for deployments at scale. Our direction is to keep sandbox data-plane traffic separate from the control plane, rather than extend the server proxy.

For that reason, we do not plan to merge this change. This is a decision about the architectural direction, not a dismissal of the security concern you raised.

@Pangjiping Pangjiping closed this Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/server size/M Denotes a PR that changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants