From 72183983b0694ceedca3f0f6214bdb2df46f265c Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sun, 18 Oct 2026 12:06:35 -0500 Subject: [PATCH] keep /config out of the root environment in the frigate run script The script exported HOME=/config and prepended /config/.local/lib to LD_LIBRARY_PATH while still root, then ran python3, ffmpeg, grep, and opt_in_out as root. The runtime user owns /config, so it could plant a .pth file in the user site-packages or a library on the loader path and get code run as root on the next start. Root helpers now run with root's environment (and python3 -I), and HOME, LD_LIBRARY_PATH, and PATH reach only the dropped process through env on the s6-setuidgid line. opt_in_out runs as the runtime user, so the openvino marker no longer gets written into /config by root. --- .../rootfs/etc/s6-overlay/s6-rc.d/frigate/run | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run index 32d26288d1..fd06ecdb1d 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run @@ -11,30 +11,14 @@ if [[ "$(id -u)" -eq 0 ]]; then fi fi -# /root survives s6-setuidgid and breaks cache writes after the drop; set -# before opt_in_out so the opt-out marker lands where the service will look -if [[ "$runs_as_root" -eq 0 ]]; then - export HOME=/config -fi - -# detector runtimes installed at first start (pip install --user) live under -# $HOME/.local; the dynamic loader only reads LD_LIBRARY_PATH at exec time -export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${HOME}/.local/lib" -export PATH="${PATH}:${HOME}/.local/bin" - -# opt out of openvino telemetry -if [ -e /usr/local/bin/opt_in_out ]; then - /usr/local/bin/opt_in_out --opt_out > /dev/null 2>&1 -fi - -# Logs should be sent to stdout so that s6 can collect them - # Tell S6-Overlay not to restart this service s6-svc -O . +# Runs as root before any /config path enters the environment; -I skips the +# user site-packages under $HOME function set_libva_version() { local ffmpeg_path - ffmpeg_path=$(python3 /usr/local/ffmpeg/get_ffmpeg_path.py) + ffmpeg_path=$(python3 -I /usr/local/ffmpeg/get_ffmpeg_path.py) LIBAVFORMAT_VERSION_MAJOR=$("$ffmpeg_path" -version | grep -Po "libavformat\W+\K\d+") export LIBAVFORMAT_VERSION_MAJOR } @@ -42,14 +26,30 @@ function set_libva_version() { echo "[INFO] Preparing Frigate..." set_libva_version +# HOME=/root survives s6-setuidgid, so the drop sets HOME=/config. The runtime +# user owns /config, so these paths go only to the service process, never to +# root. Detector runtimes installed at first start (pip install --user) live +# under $HOME/.local. +run_as=() +runtime_home="${HOME}" +if [[ "$(id -u)" -eq 0 && "$runs_as_root" -eq 0 ]]; then + run_as=(s6-setuidgid frigate) + runtime_home=/config +fi +run_as+=(env + "HOME=${runtime_home}" + "LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}${runtime_home}/.local/lib" + "PATH=${PATH}:${runtime_home}/.local/bin") + +# opt out of openvino telemetry; the marker lands under the service's HOME +if [ -e /usr/local/bin/opt_in_out ]; then + "${run_as[@]}" /usr/local/bin/opt_in_out --opt_out > /dev/null 2>&1 +fi + echo "[INFO] Starting Frigate..." cd /opt/frigate || echo "[ERROR] Failed to change working directory to /opt/frigate" # Replace the bash process with the Frigate process, redirecting stderr to stdout exec 2>&1 -if [[ "$(id -u)" -ne 0 || "$runs_as_root" -eq 1 ]]; then - exec python3 -u -m frigate -else - exec s6-setuidgid frigate python3 -u -m frigate -fi +exec "${run_as[@]}" python3 -u -m frigate