diff --git a/docker/main/rootfs/usr/local/go2rtc/create_config.py b/docker/main/rootfs/usr/local/go2rtc/create_config.py index 70cb744f13..4390ce33e0 100644 --- a/docker/main/rootfs/usr/local/go2rtc/create_config.py +++ b/docker/main/rootfs/usr/local/go2rtc/create_config.py @@ -3,7 +3,6 @@ import json import os import sys -from pathlib import Path from typing import Any from ruamel.yaml import YAML @@ -25,15 +24,6 @@ sys.path.remove("/opt/frigate") yaml = YAML() -FRIGATE_ENV_VARS = {k: v for k, v in os.environ.items() if k.startswith("FRIGATE_")} -# read docker secret files as env vars too -if os.path.isdir("/run/secrets"): - for secret_file in os.listdir("/run/secrets"): - if secret_file.startswith("FRIGATE_"): - FRIGATE_ENV_VARS[secret_file] = ( - Path(os.path.join("/run/secrets", secret_file)).read_text().strip() - ) - config_file = find_config_file() try: @@ -113,7 +103,7 @@ for name in list(go2rtc_config.get("streams", {})): if isinstance(stream, str): try: - formatted_stream = stream.format(**FRIGATE_ENV_VARS) + formatted_stream = substitute_frigate_vars(stream) if is_restricted_go2rtc_source(formatted_stream): print( f"[ERROR] Stream '{name}' uses a restricted source (echo/expr/exec) which is disabled by default for security. " @@ -122,7 +112,7 @@ for name in list(go2rtc_config.get("streams", {})): del go2rtc_config["streams"][name] continue go2rtc_config["streams"][name] = formatted_stream - except KeyError as e: + except (KeyError, ValueError) as e: print( "[ERROR] Invalid substitution found, see https://docs.frigate.video/configuration/restream#advanced-restream-configurations for more info." ) @@ -132,7 +122,7 @@ for name in list(go2rtc_config.get("streams", {})): filtered_streams = [] for i, stream_item in enumerate(stream): try: - formatted_stream = stream_item.format(**FRIGATE_ENV_VARS) + formatted_stream = substitute_frigate_vars(stream_item) if is_restricted_go2rtc_source(formatted_stream): print( f"[ERROR] Stream '{name}' item {i + 1} uses a restricted source (echo/expr/exec) which is disabled by default for security. " @@ -141,7 +131,7 @@ for name in list(go2rtc_config.get("streams", {})): continue filtered_streams.append(formatted_stream) - except KeyError as e: + except (KeyError, ValueError) as e: print( "[ERROR] Invalid substitution found, see https://docs.frigate.video/configuration/restream#advanced-restream-configurations for more info." ) diff --git a/frigate/test/http_api/test_http_config_go2rtc_streams.py b/frigate/test/http_api/test_http_config_go2rtc_streams.py index 88ecd68d91..7861488046 100644 --- a/frigate/test/http_api/test_http_config_go2rtc_streams.py +++ b/frigate/test/http_api/test_http_config_go2rtc_streams.py @@ -7,6 +7,7 @@ from unittest.mock import patch import ruamel.yaml +from frigate.config.env import FRIGATE_ENV_VARS from frigate.models import Event, Recordings, ReviewSegment from frigate.test.http_api.base_http_test import AuthTestClient, BaseTestHttp @@ -167,3 +168,24 @@ class TestConfigGo2rtcStreamRestrictions(BaseTestHttp): assert resp.status_code == 400 assert "Restricted go2rtc stream source" in resp.json()["message"] assert self._read_config() == before + + # --- {FRIGATE_*} placeholders resolve the same way as at go2rtc startup --- + + def test_set_rejects_unresolvable_placeholder(self): + before = self._read_config() + with patch.dict(FRIGATE_ENV_VARS, {"FRIGATE_RTSP_USER": "user"}): + for src in ( + "{FRIGATE_RTSP_USER!r}fmpeg:front_door#raw=-vn", + "{FRIGATE_RTSP_USER:>5}", + "rtsp://{FRIGATE_UNDEFINED}@10.0.0.2:554/video", + ): + resp = self._set_streams({"new_stream": [src]}) + assert resp.status_code == 400, src + assert self._read_config() == before + + def test_set_allows_resolvable_placeholder(self): + src = "rtsp://{FRIGATE_RTSP_USER}@10.0.0.2:554/video" + with patch.dict(FRIGATE_ENV_VARS, {"FRIGATE_RTSP_USER": "user"}): + resp = self._set_streams({"new_stream": [src]}) + assert resp.status_code == 200, resp.text + assert src in self._read_config() diff --git a/frigate/util/services.py b/frigate/util/services.py index 36691e5e18..c460a71f99 100644 --- a/frigate/util/services.py +++ b/frigate/util/services.py @@ -1073,10 +1073,13 @@ def find_restricted_go2rtc_stream_changes( restricted.append(name) break + # go2rtc startup resolves sources with the same substitution, and + # refuses to start on one it cannot resolve try: resolved_source = substitute_frigate_vars(source) except (KeyError, ValueError): - resolved_source = source + restricted.append(name) + break if is_restricted_go2rtc_api_source( source