#!/command/with-contenv bash # shellcheck shell=bash # Remap the frigate user to PUID/PGID and register EXTRA_GROUPS. # No-op when: started with --user (euid != 0), FRIGATE_RUN_AS_ROOT=true, # or PUID/PGID already match. FRIGATE_ROOT_SERVICES is validated here too. set -o errexit -o nounset -o pipefail if [[ "$(id -u)" -ne 0 ]]; then # Started with docker --user; the host owns UID mapping entirely. exit 0 fi if [[ "${FRIGATE_RUN_AS_ROOT:-false}" == "true" ]]; then if [[ -n "${FRIGATE_ROOT_SERVICES:-}" ]]; then echo "[INFO] FRIGATE_RUN_AS_ROOT=true: ignoring FRIGATE_ROOT_SERVICES" fi echo "[INFO] FRIGATE_RUN_AS_ROOT=true: skipping user remapping" exit 0 fi # a typo must fail the boot, not silently drop a service to non-root if [[ -n "${FRIGATE_ROOT_SERVICES:-}" ]]; then IFS=',' read -ra root_services <<< "${FRIGATE_ROOT_SERVICES}" for entry in "${root_services[@]}"; do entry="${entry//[[:space:]]/}" if [[ -z "$entry" ]]; then continue fi case "$entry" in frigate|go2rtc|nginx) ;; *) echo "[ERROR] FRIGATE_ROOT_SERVICES contains unknown service '${entry}'; valid names are frigate, go2rtc, nginx" >&2 exit 1 ;; esac done fi puid="${PUID:-1000}" pgid="${PGID:-1000}" if ! [[ "$puid" =~ ^[0-9]+$ && "$pgid" =~ ^[0-9]+$ ]]; then echo "[ERROR] PUID and PGID must be numeric, got '${puid}' and '${pgid}'" >&2 exit 1 fi # Remapping to 0 would make the frigate user root, so every service would keep # full privilege while reporting a successful migration. if [[ "$puid" -eq 0 || "$pgid" -eq 0 ]]; then echo "[ERROR] PUID/PGID 0 would run the services as root and defeat the privilege separation." >&2 echo "[ERROR] Set FRIGATE_RUN_AS_ROOT=true if you want to keep running as root." >&2 exit 1 fi # Colliding with the go2rtc ids would merge the two users and collapse the # separation between the main process and the network-facing restreamer. go2rtc_uid="$(id -u go2rtc)" go2rtc_gid="$(id -g go2rtc)" if [[ "$puid" -eq "$go2rtc_uid" || "$pgid" -eq "$go2rtc_gid" ]]; then echo "[ERROR] PUID/PGID must not equal the go2rtc service ids (${go2rtc_uid}:${go2rtc_gid})." >&2 exit 1 fi current_uid="$(id -u frigate)" current_gid="$(id -g frigate)" if [[ "$puid" != "$current_uid" || "$pgid" != "$current_gid" ]]; then if [[ ! -w /etc/passwd ]]; then echo "[ERROR] PUID/PGID remapping needs a writable /etc and is not compatible with read_only: true." >&2 echo "[ERROR] Either remove read_only and keep PUID, or drop PUID/PGID and use docker's user: ${puid}:${pgid} instead." >&2 echo "[ERROR] See https://docs.frigate.video/configuration/non_root for the compatibility matrix." >&2 exit 1 fi echo "[INFO] Remapping frigate user to ${puid}:${pgid}" groupmod -o -g "$pgid" frigate usermod -o -u "$puid" frigate fi # EXTRA_GROUPS: numeric host GIDs granting device access (e.g. host render/video) if [[ -n "${EXTRA_GROUPS:-}" ]]; then # groupadd and usermod -aG both write /etc/group. Checked up front so a # read-only rootfs reports the real problem instead of dying mid-loop. if [[ ! -w /etc/group ]]; then echo "[ERROR] EXTRA_GROUPS needs a writable /etc and is not compatible with read_only: true." >&2 echo "[ERROR] Use docker's group_add: with the same GIDs instead; it needs no writes inside the container." >&2 echo "[ERROR] See https://docs.frigate.video/configuration/non_root for the compatibility matrix." >&2 exit 1 fi for gid in ${EXTRA_GROUPS//,/ }; do if ! [[ "$gid" =~ ^[0-9]+$ ]] || [[ "$gid" -eq 0 ]]; then echo "[ERROR] EXTRA_GROUPS must be nonzero numeric GIDs, got '${gid}'" >&2 exit 1 fi if ! getent group "$gid" >/dev/null; then groupadd -o -g "$gid" "frigate-extra-${gid}" fi group_name="$(getent group "$gid" | cut -d: -f1)" usermod -aG "$group_name" frigate usermod -aG "$group_name" go2rtc echo "[INFO] Added frigate and go2rtc to supplementary group ${group_name} (gid ${gid})" done fi