diff --git a/frigate/config/env.py b/frigate/config/env.py index 209dda67bf..79d85cfab0 100644 --- a/frigate/config/env.py +++ b/frigate/config/env.py @@ -1,3 +1,4 @@ +import logging import os import re from pathlib import Path @@ -5,6 +6,8 @@ from typing import Annotated from pydantic import AfterValidator, ValidationInfo +logger = logging.getLogger(__name__) + FRIGATE_ENV_VARS = {k: v for k, v in os.environ.items() if k.startswith("FRIGATE_")} secrets_dir = os.environ.get("CREDENTIALS_DIRECTORY", "/run/secrets") # read secret files as env vars too @@ -16,6 +19,10 @@ if os.path.isdir(secrets_dir) and os.access(secrets_dir, os.R_OK): ) +# Security overrides that must come from the container environment, which is +# controlled by the operator, rather than from the config, which admins can edit +_PROTECTED_ENV_VARS = frozenset({"GO2RTC_ALLOW_ARBITRARY_EXEC"}) + # Matches a FRIGATE_* identifier following an opening brace. _FRIGATE_IDENT_RE = re.compile(r"FRIGATE_[A-Za-z0-9_]+") @@ -95,6 +102,13 @@ EnvString = Annotated[str, AfterValidator(validate_env_string)] def validate_env_vars(v: dict[str, str], info: ValidationInfo) -> dict[str, str]: if isinstance(info.context, dict) and info.context.get("install", False): for k, val in v.items(): + if k.upper() in _PROTECTED_ENV_VARS: + logger.warning( + "Ignoring %s in environment_vars, it must be set in the container environment", + k, + ) + continue + os.environ[k] = val if k.startswith("FRIGATE_"): FRIGATE_ENV_VARS[k] = val diff --git a/frigate/test/test_env.py b/frigate/test/test_env.py index be7f51df43..d91e18c0ca 100644 --- a/frigate/test/test_env.py +++ b/frigate/test/test_env.py @@ -228,6 +228,26 @@ class TestEnvVars(unittest.TestCase): result = validate_env_string("{FRIGATE_BROKER}") self.assertEqual(result, "mqtt.local") + def test_install_ignores_go2rtc_exec_override(self): + """The go2rtc exec override cannot be enabled from the config.""" + from frigate.util.services import is_go2rtc_arbitrary_exec_allowed + + os.environ.pop("GO2RTC_ALLOW_ARBITRARY_EXEC", None) + ctx = self._make_context(install=True) + with patch("frigate.util.services.os.path.isfile", return_value=False): + for key in ("GO2RTC_ALLOW_ARBITRARY_EXEC", "go2rtc_allow_arbitrary_exec"): + validate_env_vars({key: "true", "OTHER_VAR": "value"}, ctx) + self.assertNotIn(key, os.environ) + self.assertFalse(is_go2rtc_arbitrary_exec_allowed()) + self.assertEqual(os.environ.get("OTHER_VAR"), "value") + + def test_install_keeps_container_go2rtc_exec_override(self): + """An override set in the container environment is left untouched.""" + os.environ["GO2RTC_ALLOW_ARBITRARY_EXEC"] = "true" + ctx = self._make_context(install=True) + validate_env_vars({"GO2RTC_ALLOW_ARBITRARY_EXEC": "false"}, ctx) + self.assertEqual(os.environ.get("GO2RTC_ALLOW_ARBITRARY_EXEC"), "true") + if __name__ == "__main__": unittest.main()