Files
frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/certsync/run
T
Josh HawkinsandNicolas Mowen 41bc1a5844 Container security hardening (phase 4) (#24140)
* Support read-only rootfs with self-signed certs in /config/tls

* Support read-only rootfs in s6 and pre-compile bytecode

* Assert read-only rootfs support in CI

* Document hardened read-only deployment

* keep certsync's cert selection identical to nginx's

* note the uid trade-off in user: mode

* fail fast when EXTRA_GROUPS or a missing media volume meets read_only

* keep nosuid and nodev on the /run tmpfs

* support read_only in the default mode

* don't take go2rtc down when the homekit file isn't writable

* lead with the hardware consequence of switching to user:

* refuse to write TLS material through a symlink as root

* note that memryx writes models to the root filesystem

* certsync watches whichever cert path nginx loaded
2026-09-12 07:30:04 -06:00

86 lines
2.5 KiB
Plaintext
Executable File

#!/command/with-contenv bash
# shellcheck shell=bash
# Start the CERTSYNC service
set -o errexit -o nounset -o pipefail
# Logs should be sent to stdout so that s6 can collect them
# Not `nginx -s reload`: that has root parse /tmp/nginx/conf, which the
# unprivileged nginx user can rewrite, and nginx chowns path directives on load.
function reload_nginx() {
local pid
if ! pid=$(cat /tmp/nginx/nginx.pid 2>/dev/null); then
echo "[ERROR] No nginx pid file found, not reloading"
return 0
fi
if [[ ! "$pid" =~ ^[0-9]+$ ]] || [[ "$(cat "/proc/${pid}/comm" 2>/dev/null)" != "nginx" ]]; then
echo "[ERROR] nginx pid file does not name a running nginx process, not reloading"
return 0
fi
kill -HUP "$pid"
}
echo "[INFO] Starting certsync..."
# Resolved once, and the condition must stay identical to the nginx run
# script's. Testing only fullchain.pem here would pick the mounted cert on a
# half-populated mount that nginx rejected, and the two fingerprints would then
# never agree, reloading nginx every cycle forever.
if [ -f /etc/letsencrypt/live/frigate/privkey.pem ] && [ -f /etc/letsencrypt/live/frigate/fullchain.pem ]; then
lefile="/etc/letsencrypt/live/frigate/fullchain.pem"
else
lefile="/config/tls/fullchain.pem"
fi
tls_enabled=`python3 /usr/local/nginx/get_nginx_settings.py | jq -r .tls.enabled`
listen_external_port=`python3 /usr/local/nginx/get_nginx_settings.py | jq -r .listen.external_port`
while true
do
if [[ "$tls_enabled" == 'false' ]]; then
sleep 9999
continue
fi
if [ ! -e $lefile ]
then
echo "[ERROR] TLS certificate does not exist: $lefile"
fi
leprint=`openssl x509 -in $lefile -fingerprint -noout 2>&1 || echo 'failed'`
case "$leprint" in
*Fingerprint*)
;;
*)
echo "[ERROR] Missing fingerprint from $lefile"
;;
esac
liveprint=`echo | openssl s_client -showcerts -connect 127.0.0.1:$listen_external_port 2>&1 | openssl x509 -fingerprint 2>&1 | grep -i fingerprint || echo 'failed'`
case "$liveprint" in
*Fingerprint*)
;;
*)
echo "[ERROR] Missing fingerprint from current nginx TLS cert"
;;
esac
if [[ "$leprint" != "failed" && "$liveprint" != "failed" && "$leprint" != "$liveprint" ]]
then
echo "[INFO] Reloading nginx to refresh TLS certificate"
echo "$lefile: $leprint"
reload_nginx
fi
sleep 60
done
exit 0