don't run a config ffmpeg.path as root at boot

The frigate and go2rtc run scripts probed "$ffmpeg_path" -version as root before dropping privileges, and resolve_ffmpeg_path only refused runtime-user-writable paths when frigate itself was a root service. In the default mode uid 1000 could set ffmpeg.path to a /config directory, plant bin/ffmpeg there, and get root on the next restart, and under FRIGATE_ROOT_SERVICES=go2rtc a root go2rtc used that path as its ffmpeg bin. The probe now runs as the service user whenever the service drops privileges, and resolve_ffmpeg_path refuses writable paths for whichever service will run the binary, returning the resolved path so a symlink can't be retargeted after the check. go2rtc falls back to the bundled build's libavformat version if its user can't run the custom build.
This commit is contained in:
Josh Hawkins
2026-11-07 12:32:27 -06:00
parent b10c8b7498
commit e04035dfa2
8 changed files with 169 additions and 32 deletions
@@ -14,11 +14,17 @@ fi
# Tell S6-Overlay not to restart this service
s6-svc -O .
# Runs as root with root's HOME and LD_LIBRARY_PATH; -I skips user site-packages
# The helper runs as root (-I skips user site-packages). The probe runs as the
# service user when the service drops privileges; a root probe relies on
# resolve_ffmpeg_path refusing runtime-user-writable paths.
function set_libva_version() {
local ffmpeg_path
ffmpeg_path=$(python3 -I /usr/local/ffmpeg/get_ffmpeg_path.py)
LIBAVFORMAT_VERSION_MAJOR=$("$ffmpeg_path" -version | grep -Po "libavformat\W+\K\d+")
local probe_as=()
if [[ "$(id -u)" -eq 0 && "$runs_as_root" -eq 0 ]]; then
probe_as=(s6-setuidgid frigate)
fi
ffmpeg_path=$(python3 -I /usr/local/ffmpeg/get_ffmpeg_path.py frigate)
LIBAVFORMAT_VERSION_MAJOR=$("${probe_as[@]}" "$ffmpeg_path" -version | grep -Po "libavformat\W+\K\d+")
export LIBAVFORMAT_VERSION_MAJOR
}
@@ -57,10 +57,19 @@ function get_ip_and_port_from_supervisor() {
export FRIGATE_GO2RTC_WEBRTC_CANDIDATE_INTERNAL="${ip_address}:${webrtc_port}"
}
# The probe runs as the service user when the service drops privileges; a root
# probe relies on resolve_ffmpeg_path refusing runtime-user-writable paths
function set_libva_version() {
local ffmpeg_path
ffmpeg_path=$(python3 -I /usr/local/ffmpeg/get_ffmpeg_path.py)
LIBAVFORMAT_VERSION_MAJOR=$("$ffmpeg_path" -version | grep -Po "libavformat\W+\K\d+")
local probe_as=()
if [[ "$(id -u)" -eq 0 && "$runs_as_root" -eq 0 ]]; then
probe_as=(s6-setuidgid go2rtc)
fi
ffmpeg_path=$(python3 -I /usr/local/ffmpeg/get_ffmpeg_path.py go2rtc)
if ! LIBAVFORMAT_VERSION_MAJOR=$("${probe_as[@]}" "$ffmpeg_path" -version | grep -Po "libavformat\W+\K\d+"); then
echo "[WARN] Could not read the libavformat version from ${ffmpeg_path} as the go2rtc user; using the bundled build's version"
LIBAVFORMAT_VERSION_MAJOR=$("${probe_as[@]}" "/usr/lib/ffmpeg/${DEFAULT_FFMPEG_VERSION}/bin/ffmpeg" -version | grep -Po "libavformat\W+\K\d+")
fi
export LIBAVFORMAT_VERSION_MAJOR
}
@@ -25,4 +25,5 @@ except FileNotFoundError:
config: dict[str, Any] = {}
path = config.get("ffmpeg", {}).get("path", "default")
print(resolve_ffmpeg_path(path, "ffmpeg"))
service = sys.argv[1] if len(sys.argv) > 1 else "frigate"
print(resolve_ffmpeg_path(path, "ffmpeg", service))
@@ -103,7 +103,7 @@ if not is_valid_ffmpeg_path(path):
print(
"[ERROR] ffmpeg path contains spaces, quotes or shell characters, using the default ffmpeg instead."
)
ffmpeg_path = resolve_ffmpeg_path(path, "ffmpeg")
ffmpeg_path = resolve_ffmpeg_path(path, "ffmpeg", "go2rtc")
for key in apply_go2rtc_ffmpeg_config(go2rtc_config, ffmpeg_path):
print(