From 5d81da8c00ca96643f2f1a6d5d15e5f2fdee1bcb Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sun, 18 Oct 2026 12:07:17 -0500 Subject: [PATCH] don't follow a planted symlink when writing /dev/shm/go2rtc.yaml After the first start the go2rtc user owned /dev/shm/go2rtc.yaml and could swap it for a dangling symlink. The run script's -f test was false for a dangling link, so the link survived, and root's create_config.py then created the link target and the chown handed it to go2rtc. The run script now removes any existing entry, create_config.py creates the file with O_EXCL and O_NOFOLLOW at mode 0640, and the chown uses -h. --- .../main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run | 4 ++-- .../main/rootfs/usr/local/go2rtc/create_config.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run index db11bc3576..73ffc9faa5 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run @@ -66,7 +66,7 @@ function set_libva_version() { set_libva_version -if [[ -f "/dev/shm/go2rtc.yaml" ]]; then +if [[ -e "/dev/shm/go2rtc.yaml" || -L "/dev/shm/go2rtc.yaml" ]]; then echo "[INFO] Removing stale config from last run..." rm /dev/shm/go2rtc.yaml fi @@ -88,7 +88,7 @@ fi readonly homekit_config_path="/config/go2rtc_homekit.yml" if [[ "$(id -u)" -eq 0 && "$runs_as_root" -eq 0 ]]; then python3 /usr/local/go2rtc/prepare_homekit.py "${homekit_config_path}" --chown - chown go2rtc:go2rtc /dev/shm/go2rtc.yaml 2>/dev/null || true + chown -h go2rtc:go2rtc /dev/shm/go2rtc.yaml 2>/dev/null || true else python3 /usr/local/go2rtc/prepare_homekit.py "${homekit_config_path}" fi diff --git a/docker/main/rootfs/usr/local/go2rtc/create_config.py b/docker/main/rootfs/usr/local/go2rtc/create_config.py index 51c155075c..1939b8d916 100644 --- a/docker/main/rootfs/usr/local/go2rtc/create_config.py +++ b/docker/main/rootfs/usr/local/go2rtc/create_config.py @@ -198,9 +198,13 @@ if config.get("birdseye", {}).get("restream", False): else: go2rtc_config["streams"] = {"birdseye": ffmpeg_cmd} -# Write go2rtc_config to /dev/shm/go2rtc.yaml -with open("/dev/shm/go2rtc.yaml", "w") as f: +# The run script removes the old file first. Exclusive create never follows a +# link the go2rtc user planted in sticky /dev/shm. 0640 because the config +# contains camera credentials. +fd = os.open( + "/dev/shm/go2rtc.yaml", + os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, + 0o640, +) +with os.fdopen(fd, "w") as f: yaml.dump(go2rtc_config, f) - -# config contains camera credentials; do not leave it world-readable -os.chmod("/dev/shm/go2rtc.yaml", 0o640)