Debian 13 runtime with restic 0.18.1 pinned outside the OS; restic assumptions re-proven; felhom-samba 1.2.0 (R-1105, R-972)
gates / gates (push) Successful in 49s

This commit is contained in:
2026-10-16 20:33:57 +02:00
parent 29a3ce8cf2
commit fbcc3cf042
47 changed files with 706 additions and 48 deletions
+54 -35
View File
@@ -40,54 +40,73 @@ RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build \
-o /build/felhom-controller \
./cmd/controller/
# --- Runtime stage ---
# The runtime stays on Debian 12 (its current point release, 12.13, with every fixed package) ON PURPOSE: Debian 13
# ships restic 0.18.0, and the off-site engine's measured behaviour is restic 0.14.0's (a missing source path is a
# warning, the --json progress shape, the forget day-buckets, restore without a lock — offbox_*.go). Moving to
# Debian 13 is its own change with that re-proof (R-1105). The binary itself is built on Go 1.27.2 above.
FROM debian:12.13-slim@sha256:67b30a61dc87758f0caf819646104f29ecbda97d920aaf5edc834128ac8493d3
# --- restic: the official release binary, pinned by version AND checksum (R-1105, v0.323.0) ---
# Never again „whatever the OS ships": Debian 12 → 13 would have swapped restic 0.14.0 → 0.18.0 under the off-site code
# without anyone choosing it. The version below is a decision; moving it is a re-proof (07-backup-architecture.md §6.8).
# Three checks, each fails the build: restic's SHA256SUMS carries a good signature from restic's release key (vendored
# in third_party/restic/, fingerprint CF8F 18F2 8445 7597 3F79 D4E1 91A6 868B D3F7 A907 — no keyserver at build
# time); the download matches the published sum; and it matches the sum pinned HERE, so even a re-signed release
# cannot swap the binary. Pinned by TestR1105_ResticIsPinnedOutsideTheOS.
FROM debian:13.7-slim@sha256:a29215f6a35e51e22adffa17f89e9d2ef06214e64a2bad10d765c46aea49f11f AS restic
ARG TARGETARCH
ARG RESTIC_VERSION=0.18.1
ARG RESTIC_SHA256_AMD64=680838f19d67151adba227e1570cdd8af12c19cf1735783ed1ba928bc41f363d
ARG RESTIC_SHA256_ARM64=87f53fddde38764095e9c058a3b31834052c37e5826d2acf34e18923c006bd45
COPY third_party/restic/restic-release-key.asc /tmp/restic-release-key.asc
RUN set -eu; \
apt-get update && apt-get install -y --no-install-recommends ca-certificates curl gnupg gpgv bzip2; \
case "${TARGETARCH}" in \
amd64) want="${RESTIC_SHA256_AMD64}" ;; \
arm64) want="${RESTIC_SHA256_ARM64}" ;; \
*) echo "no pinned restic checksum for architecture '${TARGETARCH}'" >&2; exit 1 ;; \
esac; \
f="restic_${RESTIC_VERSION}_linux_${TARGETARCH}.bz2"; \
url="https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}"; \
cd /tmp; \
curl -fsSL -o "$f" "$url/$f"; curl -fsSL -o SHA256SUMS "$url/SHA256SUMS"; curl -fsSL -o SHA256SUMS.asc "$url/SHA256SUMS.asc"; \
gpg --dearmor < /tmp/restic-release-key.asc > /tmp/restic-release-key.gpg; \
gpgv --keyring /tmp/restic-release-key.gpg SHA256SUMS.asc SHA256SUMS; \
grep " ${f}\$" SHA256SUMS | sha256sum -c -; \
echo "${want} ${f}" | sha256sum -c -; \
bunzip2 -c "$f" > /usr/local/bin/restic; chmod 0755 /usr/local/bin/restic; \
/usr/local/bin/restic version | grep -q "^restic ${RESTIC_VERSION} "
# Install runtime dependencies:
# - docker-cli: for "docker compose" commands
# - ca-certificates: for HTTPS (healthchecks pings, git)
# - postgresql-client: for pg_dump
# - default-mysql-client: for mysqldump
# - sqlite3: for SQLite backup
# - git: for stack sync from Gitea
# - curl: for health pings and debugging
# - util-linux: lsblk, blkid, mount (storage init)
# - fdisk: sfdisk (partition table writing — separate from util-linux on Debian)
# - e2fsprogs: mkfs.ext4 (filesystem formatting)
# - rsync: for data migration between storage paths
# - parted: partprobe (partition table re-read after sfdisk)
# - restic: encrypted off-box (NAS) backup over SFTP (Part B; version pinned by the Debian release)
# - openssh-client: restic's sftp backend shells out to `ssh` (the SFTP transport); also ssh-copy-id/sftp
# for the SLICE-2 offsite apply-bridge key install
# - sshpass: feeds the one-time storage-box password to ssh-copy-id -s -f (offsite apply-bridge)
# R-972: `apt-get upgrade` first — the Debian security fixes newer than the base image's point release (gnutls, pcre2,
# libcap2 were fixable on 12.13 on 2026-10-16). Within Debian 12 only, so restic stays 0.14.0.
# --- Runtime stage ---
# Debian 13 (R-1105, v0.323.0), pinned by point release AND digest. It waited on Debian 12 until the off-site code's
# restic assumptions were re-proven on 0.18 (07-backup-architecture.md §6.8); restic itself no longer comes from here.
FROM debian:13.7-slim@sha256:a29215f6a35e51e22adffa17f89e9d2ef06214e64a2bad10d765c46aea49f11f
# Install runtime dependencies — each one is a program the controller binary runs (R-1105 re-checked every exec):
# - docker-cli + compose plugin: every stack operation (below)
# - ca-certificates: for HTTPS (healthchecks pings, git, the hub)
# - curl: the image's HEALTHCHECK, and the docker repo key below
# - gnupg: dearmors docker's repo key below
# - git: catalog sync
# - util-linux: findmnt (mount reads)
# - rsync: data migration between storage paths
# - openssh-client: restic's sftp/rclone transports shell out to `ssh`; the off-site key probe
# v0.323.0 (R-1105) REMOVED, because the binary never runs them: postgresql-client and default-mysql-client (every
# dump and import runs INSIDE the app's own database container — `docker exec`, appbackup/dbdump.go), sqlite3,
# fdisk, e2fsprogs, parted (disk work moved to the host agent in v0.37.0), sshpass (the ssh-copy-id key install it
# fed was replaced by the hub's key registrar in v0.289.0). Fewer packages, fewer known holes.
RUN apt-get update && apt-get upgrade -y --no-install-recommends && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
gnupg \
git \
postgresql-client \
default-mysql-client \
sqlite3 \
util-linux \
fdisk \
e2fsprogs \
rsync \
parted \
restic \
openssh-client \
sshpass \
&& rm -rf /var/lib/apt/lists/*
# Install docker-cli (without daemon)
COPY --from=restic /usr/local/bin/restic /usr/local/bin/restic
# Install docker-cli (without daemon). The repo's codename comes from the image's own /etc/os-release, never a
# hard-coded name (a hard-coded `bookworm` would have kept Debian 12's packages on a Debian 13 base).
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker.gpg \
&& . /etc/os-release \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] \
https://download.docker.com/linux/debian bookworm stable" > /etc/apt/sources.list.d/docker.list \
https://download.docker.com/linux/debian ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends docker-ce-cli docker-compose-plugin \
&& rm -rf /var/lib/apt/lists/*