Files
Anthias/docker/Dockerfile.base.j2
Viktor Petersson e7f34b27e2 fix(docker): pin viewer UID/GID across all images for deterministic ownership (#2958)
- Create the `viewer` user with a fixed UID/GID (1000) in the shared
  Dockerfile.base.j2 so it exists, and resolves identically, in the
  viewer, server and celery images.
- Drop the implicit `useradd -g video viewer` from the viewer image
  (it picked the next free uid per image and was absent from
  server/celery), keeping `video` as a supplementary group.

Without a pinned id, ownership of /data/.anthias (shared across the
containers) was non-deterministic, so a `chown viewer …` in one
container and the uid a file was written as in another could disagree —
a root cause behind the upgraded-device config-permission crash-loop.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 13:00:49 +02:00

81 lines
4.2 KiB
Django/Jinja

FROM {{ base_image }}:{{ base_image_tag }}
{% if is_legacy_pi_armhf %}
# 32-bit Pi boards (pi2, pi3) need libraspberrypi0 (legacy Broadcom
# userland: libbcm_host, libmmal, libvchiq_arm) for the Qt 5 webview.
# Trixie's archive.raspberrypi.org/main no longer ships it (replaced
# by raspi-utils, which doesn't cover Qt's link path), so we pull it
# from archive.raspbian.org's `firmware` component instead (verified:
# libraspberrypi0 ships there on trixie/armhf, not in `rpi`).
#
# Trixie's apt uses Sequoia (sqv) for signature verification and rejects
# the standalone .public.key / .gpg.key files because their User ID
# self-signatures are SHA-1, deprecated since 2026-02-01. The .deb
# keyring packages in each archive ship freshly-bound keys that pass
# the new policy — install those instead.
#
# Sources are written as Deb822 (.sources) entries — the format Trixie
# prefers (its own /etc/apt/sources.list.d/debian.sources uses it).
# Lets us pin Architectures: armhf so apt doesn't query the Pi mirrors
# for arches they don't ship.
#
# Keyring .deb downloads and Deb822 URIs use HTTPS (both archives serve
# it cleanly). The .deb hashes are pinned to a known-good SHA256 because
# the .deb is the trust anchor for everything that follows: a tampered
# keyring would invalidate the chain even with TLS.
{% if disable_cache_mounts %}
RUN \
{% else %}
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
{% endif %}
apt-get update && \
apt-get install -y --no-install-recommends ca-certificates curl && \
mkdir -p /tmp/keyrings && \
curl --proto '=https' --tlsv1.2 -fsSL -o /tmp/keyrings/raspberrypi-archive-keyring.deb \
https://archive.raspberrypi.org/debian/pool/main/r/raspberrypi-archive-keyring/raspberrypi-archive-keyring_2025.1+rpt1_all.deb && \
curl --proto '=https' --tlsv1.2 -fsSL -o /tmp/keyrings/raspbian-archive-keyring.deb \
https://archive.raspbian.org/raspbian/pool/main/r/raspbian-archive-keyring/raspbian-archive-keyring_20120528.4_all.deb && \
echo '2e727149d7acb8cc7f604e66d0049161039c8aa1eaf1175e54f9e69d963d60e4 /tmp/keyrings/raspberrypi-archive-keyring.deb' | sha256sum -c - && \
echo 'eb2bc175ecfad128ece8222b42eefabd0a2846afd14f3af04364f4a047cbc88f /tmp/keyrings/raspbian-archive-keyring.deb' | sha256sum -c - && \
dpkg-deb -x /tmp/keyrings/raspberrypi-archive-keyring.deb / && \
dpkg-deb -x /tmp/keyrings/raspbian-archive-keyring.deb / && \
rm -rf /tmp/keyrings && \
printf 'Types: deb\nURIs: https://archive.raspberrypi.org/debian\nSuites: trixie\nComponents: main\nArchitectures: armhf\nSigned-By: /usr/share/keyrings/raspberrypi-archive-keyring.gpg\n' \
> /etc/apt/sources.list.d/raspi.sources && \
printf 'Types: deb\nURIs: https://archive.raspbian.org/raspbian\nSuites: trixie\nComponents: firmware\nArchitectures: armhf\nSigned-By: /usr/share/keyrings/raspbian-archive-keyring.gpg\n' \
> /etc/apt/sources.list.d/raspbian-firmware.sources
{% endif %}
{% if disable_cache_mounts %}
RUN \
{% else %}
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
{% endif %}
apt-get update && \
apt-get -y install --no-install-recommends \
{% for dependency in base_apt_dependencies %}
{% if not loop.last %}
{{ dependency }} \
{% else %}
{{ dependency }}
{% endif %}
{% endfor %}
{% include 'labels.j2' %}
# Pin the unprivileged runtime user to a fixed UID/GID so ownership of
# files under /data — notably ~/.anthias, the config dir shared by the
# viewer, server and celery containers — is identical in every image.
# Without an explicit id `useradd` picks the next free uid per image (it
# happened to land on 1000), so the uid a file is written as in one
# container and a `chown viewer …` in another could silently disagree —
# the class of bug behind the upgraded-device config-permission
# crash-loop. The viewer process drops to this user; server/celery run as
# root but the user must still exist and resolve to the same id for chowns
# to be deterministic wherever they run. `video` (the DRM/ALSA device
# nodes the viewer opens) is kept as a supplementary group.
ARG ANTHIAS_UID=1000
ARG ANTHIAS_GID=1000
RUN groupadd -g ${ANTHIAS_GID} viewer && \
useradd -u ${ANTHIAS_UID} -g viewer -G video viewer