|
22 | 22 |
|
23 | 23 | yaml = YAML() |
24 | 24 |
|
| 25 | +# Check if arbitrary exec sources are allowed (defaults to False for security) |
| 26 | +allow_arbitrary_exec = None |
| 27 | +if "GO2RTC_ALLOW_ARBITRARY_EXEC" in os.environ: |
| 28 | + allow_arbitrary_exec = os.environ.get("GO2RTC_ALLOW_ARBITRARY_EXEC") |
| 29 | +elif ( |
| 30 | + os.path.isdir("/run/secrets") |
| 31 | + and os.access("/run/secrets", os.R_OK) |
| 32 | + and "GO2RTC_ALLOW_ARBITRARY_EXEC" in os.listdir("/run/secrets") |
| 33 | +): |
| 34 | + allow_arbitrary_exec = ( |
| 35 | + Path(os.path.join("/run/secrets", "GO2RTC_ALLOW_ARBITRARY_EXEC")) |
| 36 | + .read_text() |
| 37 | + .strip() |
| 38 | + ) |
| 39 | +# check for the add-on options file |
| 40 | +elif os.path.isfile("/data/options.json"): |
| 41 | + with open("/data/options.json") as f: |
| 42 | + raw_options = f.read() |
| 43 | + options = json.loads(raw_options) |
| 44 | + allow_arbitrary_exec = options.get("go2rtc_allow_arbitrary_exec") |
| 45 | + |
| 46 | +ALLOW_ARBITRARY_EXEC = allow_arbitrary_exec is not None and str( |
| 47 | + allow_arbitrary_exec |
| 48 | +).lower() in ("true", "1", "yes") |
| 49 | + |
25 | 50 | FRIGATE_ENV_VARS = {k: v for k, v in os.environ.items() if k.startswith("FRIGATE_")} |
26 | 51 | # read docker secret files as env vars too |
27 | 52 | if os.path.isdir("/run/secrets"): |
|
109 | 134 | elif go2rtc_config["ffmpeg"].get("rtsp") is None: |
110 | 135 | go2rtc_config["ffmpeg"]["rtsp"] = rtsp_args |
111 | 136 |
|
112 | | -for name in go2rtc_config.get("streams", {}): |
| 137 | + |
| 138 | +def is_restricted_source(stream_source: str) -> bool: |
| 139 | + """Check if a stream source is restricted (echo, expr, or exec).""" |
| 140 | + return stream_source.strip().startswith(("echo:", "expr:", "exec:")) |
| 141 | + |
| 142 | + |
| 143 | +for name in list(go2rtc_config.get("streams", {})): |
113 | 144 | stream = go2rtc_config["streams"][name] |
114 | 145 |
|
115 | 146 | if isinstance(stream, str): |
116 | 147 | try: |
117 | | - go2rtc_config["streams"][name] = go2rtc_config["streams"][name].format( |
118 | | - **FRIGATE_ENV_VARS |
119 | | - ) |
| 148 | + formatted_stream = stream.format(**FRIGATE_ENV_VARS) |
| 149 | + if not ALLOW_ARBITRARY_EXEC and is_restricted_source(formatted_stream): |
| 150 | + print( |
| 151 | + f"[ERROR] Stream '{name}' uses a restricted source (echo/expr/exec) which is disabled by default for security. " |
| 152 | + f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources." |
| 153 | + ) |
| 154 | + del go2rtc_config["streams"][name] |
| 155 | + continue |
| 156 | + go2rtc_config["streams"][name] = formatted_stream |
120 | 157 | except KeyError as e: |
121 | 158 | print( |
122 | 159 | "[ERROR] Invalid substitution found, see https://docs.frigate.video/configuration/restream#advanced-restream-configurations for more info." |
123 | 160 | ) |
124 | 161 | sys.exit(e) |
125 | 162 |
|
126 | 163 | elif isinstance(stream, list): |
127 | | - for i, stream in enumerate(stream): |
| 164 | + filtered_streams = [] |
| 165 | + for i, stream_item in enumerate(stream): |
128 | 166 | try: |
129 | | - go2rtc_config["streams"][name][i] = stream.format(**FRIGATE_ENV_VARS) |
| 167 | + formatted_stream = stream_item.format(**FRIGATE_ENV_VARS) |
| 168 | + if not ALLOW_ARBITRARY_EXEC and is_restricted_source(formatted_stream): |
| 169 | + print( |
| 170 | + f"[ERROR] Stream '{name}' item {i + 1} uses a restricted source (echo/expr/exec) which is disabled by default for security. " |
| 171 | + f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources." |
| 172 | + ) |
| 173 | + continue |
| 174 | + |
| 175 | + filtered_streams.append(formatted_stream) |
130 | 176 | except KeyError as e: |
131 | 177 | print( |
132 | 178 | "[ERROR] Invalid substitution found, see https://docs.frigate.video/configuration/restream#advanced-restream-configurations for more info." |
133 | 179 | ) |
134 | 180 | sys.exit(e) |
135 | 181 |
|
| 182 | + if filtered_streams: |
| 183 | + go2rtc_config["streams"][name] = filtered_streams |
| 184 | + else: |
| 185 | + print( |
| 186 | + f"[ERROR] Stream '{name}' was removed because all sources were restricted (echo/expr/exec). " |
| 187 | + f"Set GO2RTC_ALLOW_ARBITRARY_EXEC=true to enable arbitrary exec sources." |
| 188 | + ) |
| 189 | + del go2rtc_config["streams"][name] |
| 190 | + |
136 | 191 | # add birdseye restream stream if enabled |
137 | 192 | if config.get("birdseye", {}).get("restream", False): |
138 | 193 | birdseye: dict[str, Any] = config.get("birdseye") |
|
0 commit comments