Repository navigation
Conversation
Off by default (OPENENV_GUARD_ENABLED=1 to enable): IP block/allow lists, rate limiting with auto-ban, user-agent blocking, and penetration detection, wired into both create_fastapi_app and the gradio web-interface path. Ships as a 'security' extra; in-memory state by default, Redis via OPENENV_GUARD_REDIS_URL.
…loud blocking, and log options Brings the OPENENV_GUARD_* surface to parity: passive/log-only mode, optional security headers, enforce-HTTPS, blocked/allowed countries and cloud-provider blocking, custom log file/format, and IPINFO_TOKEN geo lookups. Country rules require IPINFO_TOKEN (guard-core resolves a geo handler from it).
…enforcement, strict booleans, rate-limit auto-ban knob - web_interface no longer attaches guard twice (create_fastapi_app already covers it) and attach_guard is idempotent via app.state - enforce_https now sets trust_x_forwarded_proto by default so TLS-terminating proxies are read correctly (explicit override via OPENENV_GUARD_TRUST_X_FORWARDED_PROTO) - invalid boolean env values now fail loudly instead of silently disabling security - new OPENENV_GUARD_RATE_LIMIT_AUTO_BAN knob
…security-middleware
- Trusted proxies default to empty: trusting RFC1918 ranges let any LAN peer spoof X-Forwarded-For past the blocklist and rate limits. Behind a reverse proxy, list it in OPENENV_GUARD_TRUSTED_PROXIES. - Penetration-detection body scanning is now opt-in via OPENENV_GUARD_SCAN_BODY: request bodies are model input (observations, prompts, completions), not commands, so signature scans false-positived on ordinary content and rejected legitimate requests with 400. URL, query, and header screening stay on. - Rate-limit auto-ban defaults on; OPENENV_GUARD_RATE_LIMIT_AUTO_BAN=0 keeps 429-only enforcement. _env_bool gained a keyword-only default.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1360
Up to date version of #1361, which closed when #1492 merged (its body says "resolve #1361", a closing keyword). Same branch, merged with main at 693777b, @burtenshaw's lifespan commits (67e82bd, ea5bbc9) included.
Summary
Opt-in fastapi-guard middleware wired into both app factories (
create_fastapi_appand the gradio web-interface path), covering HTTP requests and WebSocket handshakes. Default off: withOPENENV_GUARD_ENABLEDunset,attach_guard()is a no-op and env servers run exactly as before. Enabling without the extra installed raises a clear ImportError with the install command.Passive mode and Redis outages (the open thread on #1361)
Decision: passive mode lets infrastructure failures through too, not only policy violations, matching upstream's "passive mode never blocks". Passive mode now sets fastapi-guard's
redis_fail_open, and the WebSocket startup gate follows the same passive branch as every other refusal.Retry-Afterwhile the guard can't initialize and 500 after a successful start. That 500 is fastapi-guard'sfail_secureanswer on 8.x; I'm changing it to 503 withRetry-Afterin fastapi-guard, OpenEnv needs no change for it.Type of Change
Alignment Checklist
.claude/docs/PRINCIPLES.mdand this PR aligns with our principles.claude/docs/INVARIANTS.mdand no invariants are violated (no API signature or wire-type change, env-var only config, nothing secret logged)/pre-submit-pr(orbash .claude/hooks/lint.shand tests) and addressed all issuesRFC Status
What you get when enabled
pip install "openenv[security]"plusOPENENV_GUARD_ENABLED=1:OPENENV_GUARD_RATE_LIMIT/_RATE_LIMIT_WINDOW(defaults 100 req / 60s),_AUTO_BAN_THRESHOLD/_AUTO_BAN_DURATION(defaults 10 / 300s); repeated 429s feed the ban (OPENENV_GUARD_RATE_LIMIT_AUTO_BAN=0keeps 429-only)OPENENV_GUARD_BLOCKED_IPS/_ALLOWED_IPSOPENENV_GUARD_BLOCKED_USER_AGENTSOPENENV_GUARD_SCAN_BODY)/ws,/mcp,/harnessand/ws/uigo through fastapi-guard'sguard_websocket: ban check, IP and country lists, a rate-limit bucket separate from HTTP, and penetration detection on the handshake. A refused handshake is closed beforeaccept(), so the client gets HTTP 403 on the upgrade and no socket. Messages on an accepted socket are not screened, and a ban issued mid-session applies from the next handshake. With Redis configured, the guard initializes at startup (the app lifespan runs inside fastapi-guard'sguard_lifespan), and while Redis is unreachable handshakes are closed with 1013 in active modeOPENENV_GUARD_PASSIVE_MODE), see above; optional security headers (OPENENV_GUARD_SECURITY_HEADERS), enforce-HTTPS (OPENENV_GUARD_ENFORCE_HTTPS), custom log file/format, and blocked/allowed countries plus cloud-provider blocking (countries requireIPINFO_TOKEN)OPENENV_GUARD_TRUSTED_PROXIES/_TRUSTED_PROXY_DEPTH. No proxy is trusted by default, soX-Forwarded-Foris ignored and the client is the TCP peer; behind a proxy, list it or every client shares the proxy's IP.X-Forwarded-Protois only honored from a trusted proxy, so enforce-HTTPS behind a TLS-terminating proxy needs it listed tooOPENENV_GUARD_REDIS_URL, in-memory otherwise. The library's localhost default is disabled; an opt-in feature shouldn't require a running Redis/health,/schema,/metadataand the docs paths are excluded from detection checks so orchestrators can keep probing, but IP lists and rate limiting still apply on them (fastapi-guard'senforced_on_excluded_paths)Test Plan
pytest tests/core/test_guard_security.py: 23 passed on fastapi-guard 8.0.3 / guard-core 4.3.2 (the versions inuv.lock) and on 8.1.2 / 4.4.0 (latest on PyPI). The newtest_passive_mode_admits_while_redis_unreachablefails on the previous head (the handshake is closed,WebSocketDisconnect).Live check after startup, uvicorn 0.54.0, websockets client 15.0.1, real Redis behind a TCP proxy that I close (outage) and reopen (recovery),
/healthand a/wsreset at each step:Same rows on 8.0.3 / 4.3.2 and on 8.1.2 / 4.4.0.
usort, ruff format and ruff check clean on the changed files. On the whole tree, ruff format flags
src/openenv/core/harness/README.mdand usort flagstests/envs/test_grid_world.pyandtests/envs/test_julia_env.py, all from main; this PR doesn't touch them.I didn't rerun the full suite locally for this round: the merge only brings main, and the new commit touches
security.pyand its tests. fix(qed): keep spawned verifier imports lightweight #1491 and test(qed): cover worker import isolation and busy-worker recovery #1492, which fix thetest (3.11)qed timeout that was red on Add optional fastapi-guard security middleware for env servers (default off) #1361, are in the merge.Claude Code Review
N/A