From f95785c2a9c0c7ad07a17503ec5e9d7c8ad587e2 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sun, 23 Aug 2026 17:59:19 -0500 Subject: [PATCH] Run nginx as the frigate user with writable state in /tmp/nginx --- .github/workflows/ci.yml | 2 +- .../etc/s6-overlay/s6-rc.d/certsync/run | 2 +- .../etc/s6-overlay/s6-rc.d/nginx/data/check | 2 +- .../rootfs/etc/s6-overlay/s6-rc.d/nginx/run | 32 +++++++++++++++---- .../rootfs/usr/local/nginx/conf/nginx.conf | 14 +++++++- 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 076b6e32d4..5f47b46ab7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,7 +91,7 @@ jobs: echo "response carries frame-ancestors, which breaks cross-origin iframe embedding" exit 1 fi - docker exec frigate /usr/local/nginx/sbin/nginx -t + docker exec frigate /usr/local/nginx/sbin/nginx -t -c /tmp/nginx/conf/nginx.conf docker exec frigate stat -c %a /etc/letsencrypt/live/frigate/privkey.pem | grep -qx 600 docker exec frigate stat -c %a /dev/shm/go2rtc.yaml | grep -qx 640 - name: Assert PUID/PGID remapping diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/certsync/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/certsync/run index b834c09bbf..cee20175a3 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/certsync/run +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/certsync/run @@ -49,7 +49,7 @@ do then echo "[INFO] Reloading nginx to refresh TLS certificate" echo "$lefile: $leprint" - /usr/local/nginx/sbin/nginx -s reload + /usr/local/nginx/sbin/nginx -c /tmp/nginx/conf/nginx.conf -s reload fi sleep 60 diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/data/check b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/data/check index 8307a79569..85639f86d6 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/data/check +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/data/check @@ -2,4 +2,4 @@ set -e # Wait for PID file to exist. -while ! test -f /run/nginx.pid; do sleep 1; done \ No newline at end of file +while ! test -f /tmp/nginx/nginx.pid; do sleep 1; done diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run index 9a86e7c879..74b6f5124f 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run @@ -59,10 +59,14 @@ function set_worker_processes() { cpus=4 fi - # we need to catch any errors because sed will fail if user has bind mounted a custom nginx file - sed -i "s/worker_processes auto;/worker_processes ${cpus};/" /usr/local/nginx/conf/nginx.conf || true + sed -i "s/worker_processes auto;/worker_processes ${cpus};/" /tmp/nginx/conf/nginx.conf } +# copied whole so the conf tree's relative includes still resolve +mkdir -p /tmp/nginx/conf /tmp/nginx/client_body /tmp/nginx/proxy \ + /tmp/nginx/fastcgi /tmp/nginx/uwsgi /tmp/nginx/scgi +cp -r /usr/local/nginx/conf/. /tmp/nginx/conf/ + set_worker_processes # ensure the directory for ACME challenges exists @@ -87,15 +91,29 @@ nginx_settings=$(python3 /usr/local/nginx/get_nginx_settings.py) # build templates for optional FRIGATE_BASE_PATH environment variable echo "$nginx_settings" | \ tempio -template /usr/local/nginx/templates/base_path.gotmpl \ - -out /usr/local/nginx/conf/base_path.conf + -out /tmp/nginx/conf/base_path.conf # build templates for additional network settings echo "$nginx_settings" | \ tempio -template /usr/local/nginx/templates/listen.gotmpl \ - -out /usr/local/nginx/conf/listen.conf + -out /tmp/nginx/conf/listen.conf + +if [[ "$(id -u)" -eq 0 && "${FRIGATE_RUN_AS_ROOT:-false}" != "true" ]]; then + chown -R frigate:frigate /tmp/nginx + # only the self-signed key we just generated; user-mounted certs may be :ro + if [ -f "$letsencrypt_path/privkey.pem" ]; then + chown frigate:frigate "$letsencrypt_path/privkey.pem" "$letsencrypt_path/fullchain.pem" 2>/dev/null || true + fi +fi # Replace the bash process with the NGINX process, redirecting stderr to stdout exec 2>&1 -exec \ - s6-notifyoncheck -t 30000 -n 1 \ - nginx +if [[ "$(id -u)" -ne 0 || "${FRIGATE_RUN_AS_ROOT:-false}" == "true" ]]; then + exec \ + s6-notifyoncheck -t 30000 -n 1 \ + nginx -c /tmp/nginx/conf/nginx.conf +else + exec \ + s6-notifyoncheck -t 30000 -n 1 \ + s6-setuidgid frigate nginx -c /tmp/nginx/conf/nginx.conf +fi diff --git a/docker/main/rootfs/usr/local/nginx/conf/nginx.conf b/docker/main/rootfs/usr/local/nginx/conf/nginx.conf index f177ffd88a..8235392452 100644 --- a/docker/main/rootfs/usr/local/nginx/conf/nginx.conf +++ b/docker/main/rootfs/usr/local/nginx/conf/nginx.conf @@ -1,9 +1,15 @@ +# Copied to /tmp/nginx/conf at startup and loaded with -c from there. Relative +# includes resolve against the -c file, but every other path directive resolves +# against the compile-time --prefix, so non-include paths must stay absolute. + daemon off; +# Ignored by a non-root master; required by FRIGATE_RUN_AS_ROOT so workers +# stay root instead of the compiled-in default user user root; worker_processes auto; error_log /dev/stdout warn; -pid /var/run/nginx.pid; +pid /tmp/nginx/nginx.pid; events { worker_connections 1024; @@ -13,6 +19,12 @@ http { map_hash_bucket_size 256; server_tokens off; + client_body_temp_path /tmp/nginx/client_body; + proxy_temp_path /tmp/nginx/proxy; + fastcgi_temp_path /tmp/nginx/fastcgi; + uwsgi_temp_path /tmp/nginx/uwsgi; + scgi_temp_path /tmp/nginx/scgi; + include mime.types; default_type application/octet-stream;