Resolve go2rtc stream placeholders the same way everywhere

The go2rtc startup script expanded stream sources with str.format(),
which also supports attribute and index access, while the config API
check used substitute_frigate_vars and fell back to the raw string when a
placeholder could not be resolved. A placeholder the API check could not
resolve could therefore expand into a restricted source at startup.

The startup script now uses substitute_frigate_vars, like the rest of the
config, and the API check rejects a stream source whose placeholders
cannot be resolved. The startup script's own copy of the FRIGATE_* env
var map is removed; the shared one also honors CREDENTIALS_DIRECTORY.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Blake Blackshear
2026-10-14 08:45:52 -05:00
co-authored by Claude Opus 5.5
parent 21f5ab5a1e
commit ac0f39268c
3 changed files with 30 additions and 15 deletions
@@ -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."
)