diff --git a/docker/main/rootfs/usr/local/go2rtc/create_config.py b/docker/main/rootfs/usr/local/go2rtc/create_config.py index 1b44a8067..8b8aef6d9 100644 --- a/docker/main/rootfs/usr/local/go2rtc/create_config.py +++ b/docker/main/rootfs/usr/local/go2rtc/create_config.py @@ -22,6 +22,11 @@ sys.path.remove("/opt/frigate") yaml = YAML() +# Check if arbitrary exec sources are allowed (defaults to False for security) +ALLOW_ARBITRARY_EXEC = os.environ.get( + "GO2RTC_ALLOW_ARBITRARY_EXEC", "false" +).lower() in ("true", "1", "yes") + 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"): @@ -109,14 +114,26 @@ if LIBAVFORMAT_VERSION_MAJOR < 59: elif go2rtc_config["ffmpeg"].get("rtsp") is None: go2rtc_config["ffmpeg"]["rtsp"] = rtsp_args -for name in go2rtc_config.get("streams", {}): + +def is_restricted_source(stream_source: str) -> bool: + """Check if a stream source is restricted (echo, expr, or exec).""" + return stream_source.strip().startswith(("echo:", "expr:", "exec:")) + + +for name in list(go2rtc_config.get("streams", {})): stream = go2rtc_config["streams"][name] if isinstance(stream, str): try: - go2rtc_config["streams"][name] = go2rtc_config["streams"][name].format( - **FRIGATE_ENV_VARS - ) + formatted_stream = stream.format(**FRIGATE_ENV_VARS) + if not ALLOW_ARBITRARY_EXEC and is_restricted_source(formatted_stream): + print( + f"[ERROR] Stream '{name}' uses a restricted source (echo/expr/exec) which is disabled by default for security. " + f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources." + ) + del go2rtc_config["streams"][name] + continue + go2rtc_config["streams"][name] = formatted_stream except KeyError as e: print( "[ERROR] Invalid substitution found, see https://docs.frigate.video/configuration/restream#advanced-restream-configurations for more info." @@ -124,15 +141,33 @@ for name in go2rtc_config.get("streams", {}): sys.exit(e) elif isinstance(stream, list): - for i, stream in enumerate(stream): + filtered_streams = [] + for i, stream_item in enumerate(stream): try: - go2rtc_config["streams"][name][i] = stream.format(**FRIGATE_ENV_VARS) + formatted_stream = stream_item.format(**FRIGATE_ENV_VARS) + if not ALLOW_ARBITRARY_EXEC and is_restricted_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. " + f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources." + ) + continue + + filtered_streams.append(formatted_stream) except KeyError as e: print( "[ERROR] Invalid substitution found, see https://docs.frigate.video/configuration/restream#advanced-restream-configurations for more info." ) sys.exit(e) + if filtered_streams: + go2rtc_config["streams"][name] = filtered_streams + else: + print( + f"[ERROR] Stream '{name}' was removed because all sources were restricted (echo/expr/exec). " + f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources." + ) + del go2rtc_config["streams"][name] + # add birdseye restream stream if enabled if config.get("birdseye", {}).get("restream", False): birdseye: dict[str, Any] = config.get("birdseye") diff --git a/docs/docs/configuration/restream.md b/docs/docs/configuration/restream.md index 9b93a60eb..d6a623ccb 100644 --- a/docs/docs/configuration/restream.md +++ b/docs/docs/configuration/restream.md @@ -185,10 +185,35 @@ In this configuration: - `front_door` stream is used by Frigate for viewing, recording, and detection. The `#backchannel=0` parameter prevents go2rtc from establishing the audio output backchannel, so it won't block two-way talk access. - `front_door_twoway` stream is used for two-way talk functionality. This stream can be used by Frigate's WebRTC viewer when two-way talk is enabled, or by other applications (like Home Assistant Advanced Camera Card) that need access to the camera's audio output channel. +## Security: Restricted Stream Sources + +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. + +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: + +```yaml +environment: + - GO2RTC_ALLOW_ARBITRARY_EXEC=true +``` + +:::warning + +Enabling arbitrary exec sources allows execution of arbitrary commands through go2rtc stream configurations. Only enable this if you understand the security implications and trust all sources of your configuration. + +::: + ## Advanced Restream Configurations The [exec](https://github.com/AlexxIT/go2rtc/tree/v1.9.10#source-exec) source in go2rtc can be used for custom ffmpeg commands. An example is below: +:::warning + +The `exec:`, `echo:`, and `expr:` sources are disabled by default for security. You must set `GO2RTC_ALLOW_ARBITRARY_EXEC=true` to use them. See [Security: Restricted Stream Sources](#security-restricted-stream-sources) for more information. + +::: + NOTE: The output will need to be passed with two curly braces `{{output}}` ```yaml