Restrict go2rtc ffmpeg and ngrok options without arbitrary exec (#24709)
CI / AMD64 Build (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

* restrict go2rtc ffmpeg and ngrok options without arbitrary exec

The GO2RTC_ALLOW_ARBITRARY_EXEC checks only looked at go2rtc.streams, so an admin could save a config with a custom ffmpeg.bin or ngrok.command and run any command as the go2rtc user after a restart. go2rtc builds every ffmpeg: source into a command that starts with ffmpeg.bin, and it runs ngrok.command at startup. A custom template also brought back arbitrary ffmpeg arguments, since a stream can reference any template key with #video=<name>.

config/save and config/set now reject any added or changed key under go2rtc.ffmpeg or go2rtc.ngrok unless arbitrary exec is enabled. Keys already in the saved config are left alone, the same as stream sources. At startup, ffmpeg.bin is always Frigate's ffmpeg and ngrok is dropped unless arbitrary exec is enabled. Templates already in the config file, such as the documented vaapi global, keep working.

* reject ffmpeg paths with spaces, quotes or shell characters

ffmpeg.path was used as-is when absolute, and several ffmpeg commands are built as strings and split on spaces before running. A path like `/bin/sh -c <cmd>;#` ran a shell command instead of ffmpeg in the preview and export processes, and in go2rtc through the birdseye restream exec source. The path is now validated as a version alias or an absolute path of plain characters, so config/save and config/set reject an unsafe value. resolve_ffmpeg_path falls back to the default ffmpeg for one, and the go2rtc startup script prints an error.
This commit is contained in:
Josh Hawkins
2026-10-18 06:41:38 -10:00
committed by GitHub
parent aa42b17911
commit cf418dd6b6
8 changed files with 328 additions and 35 deletions
+68
View File
@@ -1103,6 +1103,74 @@ def find_restricted_go2rtc_stream_changes(
return issues
# go2rtc runs `ffmpeg.bin` and `ngrok.command`, and every other `ffmpeg` key is a
# template of ffmpeg arguments that `#video=<name>` and friends can reference
_GO2RTC_EXEC_SECTIONS = ("ffmpeg", "ngrok")
def find_restricted_go2rtc_exec_changes(
old_go2rtc: dict[str, Any] | None, new_go2rtc: dict[str, Any] | None
) -> list[str]:
"""Return the go2rtc ffmpeg and ngrok keys added or changed through the API.
These are only accepted with arbitrary exec enabled. Keys already in the
saved config are left alone, the same as stream sources.
"""
if is_go2rtc_arbitrary_exec_allowed():
return []
old_go2rtc = old_go2rtc if isinstance(old_go2rtc, dict) else {}
new_go2rtc = new_go2rtc if isinstance(new_go2rtc, dict) else {}
changed: list[str] = []
for section in _GO2RTC_EXEC_SECTIONS:
old = old_go2rtc.get(section)
new = new_go2rtc.get(section)
if not isinstance(new, dict):
if new is not None and new != old:
changed.append(section)
continue
old = old if isinstance(old, dict) else {}
changed.extend(
f"{section}.{key}" for key, value in new.items() if old.get(key) != value
)
return changed
def apply_go2rtc_ffmpeg_config(
go2rtc_config: dict[str, Any], ffmpeg_path: str
) -> list[str]:
"""Set go2rtc's ffmpeg binary for startup.
Unless arbitrary exec is enabled, `ffmpeg.bin` is forced to Frigate's ffmpeg
and `ngrok` is removed. Templates in the saved config are kept.
Returns:
The keys that were overridden or removed
"""
allowed = is_go2rtc_arbitrary_exec_allowed()
restricted: list[str] = []
ffmpeg = go2rtc_config.get("ffmpeg")
if not isinstance(ffmpeg, dict):
ffmpeg = go2rtc_config["ffmpeg"] = {}
if ffmpeg.get("bin") is None:
ffmpeg["bin"] = ffmpeg_path
elif ffmpeg["bin"] != ffmpeg_path and not allowed:
restricted.append("ffmpeg.bin")
ffmpeg["bin"] = ffmpeg_path
if "ngrok" in go2rtc_config and not allowed:
restricted.append("ngrok")
del go2rtc_config["ngrok"]
return restricted
def ffprobe_stream(ffmpeg, path: str, detailed: bool = False) -> sp.CompletedProcess:
"""Run ffprobe on stream."""
clean_path = escape_special_characters(path)