mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-20 21:52:50 +03:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39567b37eb |
@@ -115,7 +115,7 @@ for name in list(go2rtc_config.get("streams", {})):
|
|||||||
formatted_stream = stream.format(**FRIGATE_ENV_VARS)
|
formatted_stream = stream.format(**FRIGATE_ENV_VARS)
|
||||||
if is_restricted_go2rtc_source(formatted_stream):
|
if is_restricted_go2rtc_source(formatted_stream):
|
||||||
print(
|
print(
|
||||||
f"[ERROR] Stream '{name}' uses a restricted source (echo/expr/exec) which is disabled by default for security. "
|
f"[ERROR] Stream '{name}' uses a restricted source (echo/expr/exec or ${{VAR}}) which is disabled by default for security. "
|
||||||
f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources."
|
f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources."
|
||||||
)
|
)
|
||||||
del go2rtc_config["streams"][name]
|
del go2rtc_config["streams"][name]
|
||||||
@@ -134,7 +134,7 @@ for name in list(go2rtc_config.get("streams", {})):
|
|||||||
formatted_stream = stream_item.format(**FRIGATE_ENV_VARS)
|
formatted_stream = stream_item.format(**FRIGATE_ENV_VARS)
|
||||||
if is_restricted_go2rtc_source(formatted_stream):
|
if is_restricted_go2rtc_source(formatted_stream):
|
||||||
print(
|
print(
|
||||||
f"[ERROR] Stream '{name}' item {i + 1} uses a restricted source (echo/expr/exec) which is disabled by default for security. "
|
f"[ERROR] Stream '{name}' item {i + 1} uses a restricted source (echo/expr/exec or ${{VAR}}) which is disabled by default for security. "
|
||||||
f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources."
|
f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources."
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
@@ -150,7 +150,7 @@ for name in list(go2rtc_config.get("streams", {})):
|
|||||||
go2rtc_config["streams"][name] = filtered_streams
|
go2rtc_config["streams"][name] = filtered_streams
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
f"[ERROR] Stream '{name}' was removed because all sources were restricted (echo/expr/exec). "
|
f"[ERROR] Stream '{name}' was removed because all sources were restricted (echo/expr/exec or ${{VAR}}). "
|
||||||
f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources."
|
f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources."
|
||||||
)
|
)
|
||||||
del go2rtc_config["streams"][name]
|
del go2rtc_config["streams"][name]
|
||||||
|
|||||||
@@ -221,6 +221,8 @@ Any `#` parameter on a bare `rtsp://` source disables the backchannel unless the
|
|||||||
|
|
||||||
For security reasons, the `echo:`, `expr:`, and `exec:` stream sources are disabled by default in go2rtc. These sources allow arbitrary command execution and can pose security risks if misconfigured.
|
For security reasons, the `echo:`, `expr:`, and `exec:` stream sources are disabled by default in go2rtc. These sources allow arbitrary command execution and can pose security risks if misconfigured.
|
||||||
|
|
||||||
|
go2rtc's `${VAR:default}` variable syntax is also disabled by default in stream sources, since its default can name one of those sources. Use Frigate's [`{FRIGATE_*}` substitution](./config.md#environment-variable-substitution) instead.
|
||||||
|
|
||||||
If you attempt to use these sources in your configuration, the streams will be removed and an error message will be printed in the logs.
|
If you attempt to use these sources in your configuration, the streams will be removed and an error message will be printed in the logs.
|
||||||
|
|
||||||
To enable these sources, you must set the environment variable `GO2RTC_ALLOW_ARBITRARY_EXEC=true`. This can be done in your Docker Compose file or container environment:
|
To enable these sources, you must set the environment variable `GO2RTC_ALLOW_ARBITRARY_EXEC=true`. This can be done in your Docker Compose file or container environment:
|
||||||
|
|||||||
@@ -313,3 +313,27 @@ class TestConfigGo2rtcStreamRestrictions(BaseTestHttp):
|
|||||||
)
|
)
|
||||||
assert resp.status_code == 400
|
assert resp.status_code == 400
|
||||||
assert self._read_config() == before
|
assert self._read_config() == before
|
||||||
|
|
||||||
|
# --- go2rtc ${VAR} expansion ---
|
||||||
|
|
||||||
|
def test_set_rejects_var_expansion_source(self):
|
||||||
|
before = self._read_config()
|
||||||
|
resp = self._set_streams({"new_stream": "${NOPE:exec:touch /tmp/pwned}"})
|
||||||
|
assert resp.status_code == 400
|
||||||
|
assert "Restricted go2rtc stream source" in resp.json()["message"]
|
||||||
|
assert self._read_config() == before
|
||||||
|
|
||||||
|
def test_set_rejects_escaped_var_expansion_source(self):
|
||||||
|
before = self._read_config()
|
||||||
|
resp = self._set_streams({"new_stream": "${{NOPE:exec:id}}"})
|
||||||
|
assert resp.status_code == 400
|
||||||
|
assert "Restricted go2rtc stream source" in resp.json()["message"]
|
||||||
|
assert self._read_config() == before
|
||||||
|
|
||||||
|
def test_set_allows_var_expansion_source_when_override_set(self):
|
||||||
|
with patch(
|
||||||
|
"frigate.util.services.is_go2rtc_arbitrary_exec_allowed",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
resp = self._set_streams({"new_stream": "${NOPE:exec:id}"})
|
||||||
|
assert resp.status_code == 200, resp.text
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from frigate.util.services import apply_go2rtc_ffmpeg_config
|
from frigate.util.services import (
|
||||||
|
apply_go2rtc_ffmpeg_config,
|
||||||
|
is_restricted_go2rtc_api_source,
|
||||||
|
is_restricted_go2rtc_source,
|
||||||
|
)
|
||||||
|
|
||||||
FFMPEG_PATH = "/usr/lib/ffmpeg/7.0/bin/ffmpeg"
|
FFMPEG_PATH = "/usr/lib/ffmpeg/7.0/bin/ffmpeg"
|
||||||
|
|
||||||
@@ -47,5 +51,39 @@ class TestApplyGo2rtcFfmpegConfig(unittest.TestCase):
|
|||||||
assert config["ngrok"] == {"command": "ngrok tcp 8555"}
|
assert config["ngrok"] == {"command": "ngrok tcp 8555"}
|
||||||
|
|
||||||
|
|
||||||
|
class TestRestrictedGo2rtcVarExpansion(unittest.TestCase):
|
||||||
|
"""go2rtc expands `${VAR:default}` over the raw config before parsing, and
|
||||||
|
the default can name an exec source, so a `${` source is restricted."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
exec_patch = patch(
|
||||||
|
"frigate.util.services.is_go2rtc_arbitrary_exec_allowed",
|
||||||
|
return_value=False,
|
||||||
|
)
|
||||||
|
self.mock_exec_allowed = exec_patch.start()
|
||||||
|
self.addCleanup(exec_patch.stop)
|
||||||
|
|
||||||
|
def test_var_expansion_source_is_restricted(self):
|
||||||
|
for source in (
|
||||||
|
"${NOPE:exec:touch /tmp/x}",
|
||||||
|
"rtsp://host/${CAM}",
|
||||||
|
" ${NOPE:exec:id}",
|
||||||
|
):
|
||||||
|
self.assertTrue(is_restricted_go2rtc_source(source), source)
|
||||||
|
self.assertTrue(is_restricted_go2rtc_api_source(source), source)
|
||||||
|
|
||||||
|
def test_plain_sources_are_not_restricted(self):
|
||||||
|
for source in (
|
||||||
|
"rtsp://user:pass@host/stream",
|
||||||
|
"ffmpeg:front#video=h264#height=720",
|
||||||
|
):
|
||||||
|
self.assertFalse(is_restricted_go2rtc_source(source), source)
|
||||||
|
self.assertFalse(is_restricted_go2rtc_api_source(source), source)
|
||||||
|
|
||||||
|
def test_var_expansion_allowed_when_override_set(self):
|
||||||
|
self.mock_exec_allowed.return_value = True
|
||||||
|
self.assertFalse(is_restricted_go2rtc_source("${NOPE:exec:id}"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -980,9 +980,15 @@ def is_go2rtc_arbitrary_exec_allowed() -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def is_restricted_go2rtc_source(stream_source: str) -> bool:
|
def is_restricted_go2rtc_source(stream_source: str) -> bool:
|
||||||
"""Check if a stream source is a restricted type (echo, expr, or exec)
|
"""Check if a stream source is restricted and the
|
||||||
and the GO2RTC_ALLOW_ARBITRARY_EXEC override is not set."""
|
GO2RTC_ALLOW_ARBITRARY_EXEC override is not set.
|
||||||
if not stream_source.strip().startswith(("echo:", "expr:", "exec:")):
|
|
||||||
|
Restricted sources are the echo, expr and exec schemes, and any source
|
||||||
|
using go2rtc's `${VAR:default}` expansion, which go2rtc runs over the raw
|
||||||
|
config before parsing and whose default can name an exec source.
|
||||||
|
"""
|
||||||
|
source = stream_source.strip()
|
||||||
|
if not source.startswith(("echo:", "expr:", "exec:")) and "${" not in source:
|
||||||
return False
|
return False
|
||||||
return not is_go2rtc_arbitrary_exec_allowed()
|
return not is_go2rtc_arbitrary_exec_allowed()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user