Repository navigation
fix(service-auth): backend→SLM WS 403 — route direct-uvicorn vs nginx path correctly + reject diagnostics (#10459) - #10522
Merged
Merged
Conversation
…ong path prefix for direct-uvicorn URLs (#10459) Root cause: `_ws_connect_and_listen` used `_is_loopback_target` to choose between `/api/ws/events` (direct uvicorn) and `/slm/api/ws/events` (nginx). In Docker Compose `AUTOBOT_SLM_HOST=autobot-slm` is not a loopback address, so the nginx-prefix path `/slm/api/ws/events` was used against direct uvicorn at `:8000`. Starlette finds no `/slm/` route → sends WebSocketClose before accept → uvicorn converts that to HTTP 403 (uvicorn ws impl line 292). This explains the flood of 'server rejected WebSocket connection: HTTP 403' in the backend log. Fix: replace `_is_loopback_target` with `_is_direct_uvicorn_url` which correctly identifies direct-uvicorn connections by loopback hostname OR by plain HTTP on a non-standard port (not 80/443). nginx always serves on port 443 (HTTPS) or 80 (HTTP standard), so plain HTTP on `:8000` means uvicorn regardless of hostname. Covered cases: - http://127.0.0.1:8000 → /api/ws/events (co-located bare-metal, unchanged) - http://autobot-slm:8000 → /api/ws/events (Docker Compose, was broken) - http://192.168.x.y:8000 → /api/ws/events (direct non-loopback uvicorn) - https://192.168.x.y → /slm/api/ws/events (nginx TLS, unchanged) - http://192.168.x.y:80 → /slm/api/ws/events (nginx HTTP, unchanged) Additional diagnostics: `_log_ws_reject_context` in SLM websocket.py logs origin/host/peer/sec-websocket-protocol at the point of rejection so any future environment-specific 403 can be traced without a live debugger. Also documents the accept-before-close requirement (uvicorn's HTTP 403 conversion) in the _authenticate_websocket_token docstring. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
Applied Black, isort, and autoflake to match code-quality checks. Triggered by workflow auto-fix-formatting.yml. Co-Authored-By: Paperclip <noreply@paperclip.ing>
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.
Thinking Path
#10400's 4001 secret-mismatch was fixed; this is the remaining facet — the backend→SLM WS handshake gets HTTP 403 before app auth. Traced the handshake path end-to-end.
What Changed
AUTOBOT_SLM_HOST=autobot-slm(not loopback), soslm_client._is_loopback_targetchose the nginx/slm/api/ws/eventsprefix path — but port 8000 is direct uvicorn, which has no/slm/route → starlette closes pre-accept()→ uvicorn maps that to HTTP 403.autobot-backend/services/slm_client.py— replaced_is_loopback_targetwith_is_direct_uvicorn_urlfor WS path selection (loopback host OR plain-HTTP on a non-80/443 port → direct uvicorn/api/ws/events; HTTPS/port-80 → nginx/slm/...). Covers Docker-Compose, co-located bare-metal, and proxied deploys.autobot-slm-backend/api/websocket.py—_log_ws_reject_context()logs path/origin/host/peer/subprotocol on every WS rejection so any residual 403 is diagnosable; documented the accept-before-close requirement.Verification
py_compileclean. Ruled out (not the cause): CORS/SecurityHeaders middleware (skip non-http scope), TrustedHost (not installed), forwarded-allow-ips, nginx auth_request.Model Used
Claude Opus 4.8 (senior-backend-engineer subagent).