Repository navigation
feat(signalr): opt-in SignalR/WebSocket passthrough for proxied arrs (#91) - #124
bakerboy448 wants to merge 1 commit into
Conversation
|
Right: auth runs once, at connect. Turning SignalR off (or deleting the app/key) blocks new connections, but an open socket stays up until it drops. The reconnect re-negotiates, so it gets 401 then. Restart the container to cut it now. Options if we want it to bite sooner:
No native nginx setting caps a websocket's lifetime, and |
|
Good call. The auth subrequest itself is always a GET, so the method is now passed explicitly: Tested live against Sonarr with a key allowing only
Also added the standard header to |
The test is not allowing post or get and making sure it properly 401s Edit: Squash it after you test that and it passes so i can re-pull the PR and give it a once over again |
Adds an nginx /<base>/signalr/ location that WebSocket-proxies to the arr, gated by an auth_request to signalr-auth.php. The script applies the same scoped-key lookup, endpoint and method allow-list, logging, and usage accounting as api/index.php, then hands nginx the backend URL, endpoint, and the real arr key as headers, so the scoped key never reaches the arr. - apikey and access_token are read from the original query string and removed before proxying - apps with redactions are refused (the stream cannot be field-redacted) - only real websocket Upgrade requests are forwarded; X-Accel-Redirect is ignored on every fastcgi location - 10 concurrent connections per client address (429 over the cap) - disallowed endpoint or method returns 401 - Bazarr templates for Radarr and Sonarr include the SignalR endpoints Closes #91
|
Good catch. A SignalR request for a disallowed endpoint or method now returns 401 (the method case was 403). The branch is squashed to one commit. I tested against a real Sonarr behind a request recorder, so I could confirm the arr saw only the allowed requests:
|
Closes #91. Lets apps like Bazarr use the arr's SignalR live updates through starrproxy.
TLDR
/signalr/messagesGET,/signalr/messages/negotiatePOST). Turn them on per app or template like anything else. Bazarr templates have them on.Limits
/etc/hosts,localhost, host networking).How it works and test results
Starr.phpadds the two endpoints.signalr-auth.php(outside the web root) does the check. nginxauth_requestto internal/signalr-auth, then proxies and upgrades.Upgrade: websocketis forwarded.X-Accel-Redirectis ignored everywhere.auth-granted, counted asallowed.Testing
Head 1d7251b (squashed) re-test against real Sonarr behind a request recorder. A disallowed SignalR endpoint or method returns 401; the arr sees only allowed calls:
system/statusphp -landnginx -tpass.Head 9b5cd89 (rebased onto develop 8bc0d68). The image was built from a clean tree at that commit with a fresh
/configand run on a throwaway docker network with generated keys; no production containers or keys. Backends: real Sonarr (linuxserver latest) x3, plus a recording echo upstream registered as a Sonarr backend to show what reaches the arr. Result: PR plan 60 / 60 PASS, upstream smoke and route-order checks 29 / 29 PASS.php -lonsignalr-auth.phpandStarr.php, andnginx -ton the rendered config, pass in the image.X-Api-Key), websocket upgrade with SignalR handshake (101) against real Sonarr%2fvariants,HEAD/OPTIONS, uppercase pathAuthorization: Beareralone/signalr-auth,/signalr-auth.php,/signalr/auth.phpX-Starr-*header or real key in any client-visible response (all captured responses checked)ApiKey, encoded param name,ACCESS_TOKEN, duplicate params, withAuthorizationX-Api-Key, noAuthorizationUpgrade: h2c,websocket, h2cUpgradeforwarded;WebSocketforwardswebsocketauth-granted, not200X-Accel-Redirectreached through?endpoint=on/feed/,/backup/,/api//feed/: nginx follows the redirect (401)nginx -T: everyfastcgi_passlocation (5) hasfastcgi_ignore_headers X-Accel-Redirect/<base>/signalr/.../{api,feed,backup}differs, denied either way (JSON 404 now, 401 if moved); arr never reached. Kept after the catch-all/api,/feed,/backup,/sonarr/api,/x/feed,/x/backup,/a/b/api;/still the UIaddstarron a new (4th) backend: app gets that backend'sstarr_idtagGET and POST,importlistexclusionand/pagedagainst real Sonarr;config/hostdeniedsession.upload_progress.enabledOff,memory_limit-1,post_max_size8M,fatal_error_backtracesOff; nginxclient_max_body_size 8M(9 MiB body: 413)Not covered on this head: the Bazarr client itself (an earlier revision was tested with Bazarr 1.6.2 against Sonarr and Radarr), Radarr, UrlBase, TLS upstreams, legacy SignalR, and the admin UI.
🤖 Generated with Claude Code