Repository: blakeblackshear/frigate

Issue:
 Support for Podman Secrets or File-based Secret Management
**Describe what you are trying to accomplish and why in non-technical terms**
I aim to securely manage sensitive credentials such as passwords within the Frigate environment while running it on RHEL 9 within a Podman container.

**Describe the solution you'd like**
Implementing a feature within Frigate to natively support Podman secrets or, alternatively, reading secrets from a file would be ideal. This would allow for a more secure handling of sensitive information without hard-coding them into configuration files or environment variables.

**Describe alternatives you've considered**
I have crafted a workaround by creating a script that reads the secret from a file and sets it as an environment variable. This script is then set as the entry point of the Frigate container. While this workaround is functional, it is less than ideal from a security and simplicity standpoint.

```
#!/bin/bash
export FRIGATE_RTSP_PASSWORD=$(cat /run/secrets/FRIGATE_RTSP_SECRET)
exec /init "$@"
```

**Additional context**
I am running Frigate version 0.12.1-367d724 on a RHEL 9 virtual host with Podman version 4.4.1 and Podman-Compose version 1.0.6. The host kernel version is 5.14.0-284.30.1.el9_2.x86_64. This setup is managed through a docker-compose.yml file utilizing Podman-Compose to create the container. The aim is to have a more secure way of handling sensitive credentials while maintaining the ease of use and not compromising security. The current workaround, although functional, requires additional scripting and is not as straightforward or secure as a native secret handling feature within Frigate. 


Discussion:

Resolving patch:
diff --git a/docker/main/rootfs/usr/local/go2rtc/create_config.py b/docker/main/rootfs/usr/local/go2rtc/create_config.py
--- a/docker/main/rootfs/usr/local/go2rtc/create_config.py
+++ b/docker/main/rootfs/usr/local/go2rtc/create_config.py
@@ -3,6 +3,7 @@
 import json
 import os
 import sys
+from pathlib import Path
 
 import yaml
 
@@ -16,6 +17,14 @@
 
 
 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()
+
 config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
 
 # Check if we can use .yaml instead of .yml
diff --git a/frigate/config.py b/frigate/config.py
--- a/frigate/config.py
+++ b/frigate/config.py
@@ -5,6 +5,7 @@
 import logging
 import os
 from enum import Enum
+from pathlib import Path
 from typing import Dict, List, Optional, Tuple, Union
 
 import matplotlib.pyplot as plt
@@ -47,6 +48,13 @@
 # DEFAULT_TIME_FORMAT = "%d.%m.%Y %H:%M:%S"
 
 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()
 
 DEFAULT_TRACKED_OBJECTS = ["person"]
 DEFAULT_LISTEN_AUDIO = ["bark", "fire_alarm", "scream", "speech", "yell"]
diff --git a/frigate/plus.py b/frigate/plus.py
--- a/frigate/plus.py
+++ b/frigate/plus.py
@@ -3,6 +3,7 @@
 import logging
 import os
 import re
+from pathlib import Path
 from typing import Any, List
 
 import cv2
@@ -36,6 +37,10 @@ def __init__(self) -> None:
         self.key = None
         if PLUS_ENV_VAR in os.environ:
             self.key = os.environ.get(PLUS_ENV_VAR)
+        elif os.path.isdir("/run/secrets") and PLUS_ENV_VAR in os.listdir(
+            "/run/secrets"
+        ):
+            self.key = Path(os.path.join("/run/secrets", PLUS_ENV_VAR)).read_text()
         # check for the addon options file
         elif os.path.isfile("/data/options.json"):
             with open("/data/options.json") as f:


Test patch: