mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-21 14:12:47 +03:00
CI / AMD64 Build (push) Canceled after 0s
CI / AMD64 Smoke Test (push) Canceled after 0s
CI / ARM Build (push) Canceled after 0s
CI / Jetson Jetpack 6 (push) Canceled after 0s
CI / AMD64 Extra Build (push) Canceled after 0s
CI / ARM Extra Build (push) Canceled after 0s
CI / Synaptics Build (push) Canceled after 0s
CI / Assemble and push default build (push) Canceled after 0s
* reject cross-origin websocket handshakes Neither /ws nor the jsmpeg websockets checked Origin, and the internal port grants admin to everyone, so any page a LAN user visited could open ws://frigate:5000/ws and send commands or read streams. Both servers now reject a browser handshake whose Origin host matches neither Host nor X-Forwarded-Host, and allow handshakes without an Origin for non-browser clients. nginx now forwards an outer proxy's X-Forwarded-Host instead of overwriting it, so proxies that rewrite Host and the Home Assistant integration's proxy still match. * apply runtime role changes to jsmpeg streams The output process never subscribed to config/auth, so role edits from the Roles UI (requires_restart 0) did not reach it and jsmpeg kept streaming cameras that had been removed from a role until restart. The output process now replaces its auth config on config/auth updates, which the camera and birdseye broadcast threads read through the shared config. * tidy origin check docstring and auth update comment * reject cross-origin go2rtc stream requests in /auth The go2rtc websockets and the webrtc offer POST still accepted any Origin, since go2rtc runs with origin "*" and /auth grants admin on the internal port, so a page on another site could pull live video through a LAN user's browser. nginx now passes Origin, Host, X-Forwarded-Host and Upgrade to /auth, which rejects a websocket upgrade or go2rtc stream request with a foreign Origin before the internal-port early return. The hostname rule moves to frigate/util/origin.py so /auth and the ws4py servers share it. * set Host in the nginx proxy doc examples * drain all queued auth updates in the output process * note that cards must stream through the integration proxy * strip the query before matching go2rtc paths in the origin check
66 lines
2.5 KiB
Python
66 lines
2.5 KiB
Python
"""Tests for JSMPEG websocket authorization."""
|
|
|
|
import unittest
|
|
from types import SimpleNamespace
|
|
|
|
from frigate.config import AuthConfig, FrigateConfig
|
|
from frigate.output.ws_auth import ws_has_camera_access
|
|
|
|
|
|
class TestWsHasCameraAccess(unittest.TestCase):
|
|
def setUp(self):
|
|
self.config = FrigateConfig(
|
|
mqtt={"host": "mqtt"},
|
|
auth={"roles": {"limited_user": ["front_door"]}},
|
|
cameras={
|
|
"front_door": {
|
|
"ffmpeg": {
|
|
"inputs": [
|
|
{"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]}
|
|
]
|
|
},
|
|
"detect": {"height": 1080, "width": 1920, "fps": 5},
|
|
},
|
|
"back_door": {
|
|
"ffmpeg": {
|
|
"inputs": [
|
|
{"path": "rtsp://10.0.0.2:554/video", "roles": ["detect"]}
|
|
]
|
|
},
|
|
"detect": {"height": 1080, "width": 1920, "fps": 5},
|
|
},
|
|
},
|
|
)
|
|
|
|
def _make_ws(self, role: str):
|
|
return SimpleNamespace(environ={"HTTP_REMOTE_ROLE": role})
|
|
|
|
def test_restricted_role_only_gets_allowed_camera(self):
|
|
ws = self._make_ws("limited_user")
|
|
self.assertTrue(ws_has_camera_access(ws, "front_door", self.config))
|
|
self.assertFalse(ws_has_camera_access(ws, "back_door", self.config))
|
|
|
|
def test_unrestricted_role_can_access_any_camera(self):
|
|
ws = self._make_ws("viewer")
|
|
self.assertTrue(ws_has_camera_access(ws, "front_door", self.config))
|
|
self.assertTrue(ws_has_camera_access(ws, "back_door", self.config))
|
|
|
|
def test_birdseye_requires_unrestricted_access(self):
|
|
self.assertTrue(
|
|
ws_has_camera_access(self._make_ws("admin"), "birdseye", self.config)
|
|
)
|
|
self.assertTrue(
|
|
ws_has_camera_access(self._make_ws("viewer"), "birdseye", self.config)
|
|
)
|
|
self.assertFalse(
|
|
ws_has_camera_access(self._make_ws("limited_user"), "birdseye", self.config)
|
|
)
|
|
|
|
def test_replaced_auth_config_revokes_camera(self):
|
|
ws = self._make_ws("limited_user")
|
|
self.assertTrue(ws_has_camera_access(ws, "front_door", self.config))
|
|
|
|
self.config.auth = AuthConfig(roles={"limited_user": ["back_door"]})
|
|
self.assertFalse(ws_has_camera_access(ws, "front_door", self.config))
|
|
self.assertTrue(ws_has_camera_access(ws, "back_door", self.config))
|