diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc5fb40ce8..73710a82ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -398,6 +398,24 @@ jobs: docker exec frigate-rod /etc/s6-overlay/s6-rc.d/init-devices/run docker exec frigate-rod getfacl -p /dev/apex_9 | grep -q "user:frigate:rw-" docker rm -f frigate-rod + - name: "Assert switching that install to user: still starts" + run: | + # the config dir above now holds a go2rtc-owned go2rtc_homekit.yml, + # which user: keeps readable but not writable (no supplementary groups) + docker run -d --name frigate-rod-user --shm-size 256m \ + --read-only --tmpfs /tmp:rw,size=1g --tmpfs /run:exec,nosuid,nodev,mode=0755 \ + --user 1000:1000 \ + -v /tmp/frigate-config-rod:/config \ + -v /tmp/frigate-media-rod:/media/frigate \ + ${{ steps.setup.outputs.image-name }}-amd64 + up=0 + for i in $(seq 1 60); do + docker exec frigate-rod-user curl -fs http://127.0.0.1:5000/api/version && up=1 && break + sleep 5 + done + if [ "$up" -ne 1 ]; then echo "container did not survive the switch to user:"; docker logs frigate-rod-user; exit 1; fi + docker logs frigate-rod-user 2>&1 | grep -q "HomeKit pairing changes will not persist" + docker rm -f frigate-rod-user - name: Teardown if: always() run: docker rm -f frigate || true diff --git a/docker/main/rootfs/usr/local/go2rtc/prepare_homekit.py b/docker/main/rootfs/usr/local/go2rtc/prepare_homekit.py index cda82f08cf..17e61b2bee 100644 --- a/docker/main/rootfs/usr/local/go2rtc/prepare_homekit.py +++ b/docker/main/rootfs/usr/local/go2rtc/prepare_homekit.py @@ -66,7 +66,17 @@ def main() -> int: path = sys.argv[1] do_chown = "--chown" in sys.argv[2:] - fd = open_nofollow(path) + try: + fd = open_nofollow(path) + except PermissionError: + print( + f"[WARN] {path} is not writable by uid {os.geteuid()}, so HomeKit " + "pairing changes will not persist. It is owned by the go2rtc user " + "from an earlier run in the default mode. To fix, on the host run: " + f"chown {os.geteuid()}:{os.getegid()} /{os.path.basename(path)}" + ) + return 0 + try: content = os.read(fd, MAX_BYTES).decode("utf-8", "replace") normalized = normalize(content) diff --git a/docs/docs/configuration/non_root.md b/docs/docs/configuration/non_root.md index fe436db2d5..65cdefbc1a 100644 --- a/docs/docs/configuration/non_root.md +++ b/docs/docs/configuration/non_root.md @@ -313,7 +313,7 @@ To remove root from the container entirely, add Docker's `user:`: user: "1000:1000" # NOT compatible with PUID/PGID, see the run modes table ``` -Two things change. Every service then runs as that one uid, so go2rtc no longer gets its own restricted user. And the startup device grants can't run, because there is no root to run them, so pass your hardware with `group_add:` or a udev rule per [Manual setup](#manual-setup) instead. `/config` and `/media/frigate` have to be owned by that uid already, since Frigate never adjusts ownership in this mode. +Two things change. Every service then runs as that one uid, so go2rtc no longer gets its own restricted user. And the startup device grants can't run, because there is no root to run them, so pass your hardware with `group_add:` or a udev rule per [Manual setup](#manual-setup) instead. `/config` and `/media/frigate` have to be owned by that uid already, since Frigate never adjusts ownership in this mode. Switching an existing install over also leaves `/config/go2rtc_homekit.yml` owned by the go2rtc user, which this mode can't write; `chown` it to your uid or HomeKit pairing changes stop persisting. Frigate warns and starts either way. This mode can also take `cap_drop: [ALL]`, which the default mode cannot: starting as root needs `CAP_CHOWN` for the ownership sweep, `CAP_SETUID` and `CAP_SETGID` to drop to the runtime user, and `CAP_FOWNER` for the device grants.