fix(linux/build): run derivative-maker as unprivileged builder user (M1.1)
Some checks failed
Build SilverMetal Linux ISO (reproducibility-gated) / build-and-verify (push) Failing after 1m14s
Some checks failed
Build SilverMetal Linux ISO (reproducibility-gated) / build-and-verify (push) Failing after 1m14s
Run #4251 advanced past checkout and into derivative-maker, then died immediately: ERROR: This must NOT be run as root (sudo)! ERROR: Exiting ./derivative-maker with non-zero exit code 1. Errors Detected: 0. Execution Time: 00:00:00. Kicksecure's derivative-maker explicitly refuses to run as root — it expects a regular user with passwordless sudo and uses sudo internally for the privileged operations (debootstrap, mksquashfs, chroot mounts). Our minimal debian-slim builder image had a `builder` user (uid 1000) but no sudo, no sudoers entry, and the container ran as root. Aligns with the upstream Kicksecure container pattern at linux/build/derivative-maker/docker/derivative-maker-docker-setup (uses USER=user with `${USER} ALL=(ALL) NOPASSWD:ALL`). Changes: - Dockerfile.builder: install `sudo` (and `fakeroot` while we're here — upstream sanity-tests pulls this in via apt at build time, but having it baked avoids a snapshot.debian.org round-trip every run); add passwordless sudoers entry for builder; correct the misleading comment that claimed root was needed. - New scripts/build-inner.sh: the inner derivative-maker invocation pulled out of build.sh's heredoc. Once we needed to drop privileges via runuser, the nested-heredoc / nested-quoting situation became unmaintainable; a regular script with normal quoting is far cleaner. - build.sh: inner heredoc now just chowns the workspace to builder and runuser's into build-inner.sh. ${REPO_ROOT} and ${BUILD_DIR} continue to be forwarded into the container via -e. - build.sh: BUILDER_IMAGE digest re-pinned to sha256:f8f0db37…1bedc (rebuilt and pushed natively on 10.0.0.51 — never on the WSL/aarch64 dev box, see reference_silvermetal_runner.md memory). Verified: bash -n on both scripts; image builds and pushes cleanly. Pushing this commit triggers a fresh CI run that will exercise it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,7 @@ RUN set -eux; \
|
||||
debootstrap \
|
||||
diffoscope-minimal \
|
||||
dosfstools \
|
||||
fakeroot \
|
||||
git \
|
||||
gnupg \
|
||||
isolinux \
|
||||
@@ -61,15 +62,21 @@ RUN set -eux; \
|
||||
reprepro \
|
||||
rsync \
|
||||
squashfs-tools \
|
||||
sudo \
|
||||
syslinux-common \
|
||||
xorriso; \
|
||||
apt-get clean; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Non-root user for the parts of the build that don't need privilege.
|
||||
# live-build itself still needs root inside the container for chroot/mount,
|
||||
# so build.sh runs the container as root; this user exists for diagnostic
|
||||
# tooling and matches uid 1000 to play nicely with bind mounts.
|
||||
RUN useradd --uid 1000 --create-home --shell /bin/bash builder
|
||||
# Non-root user for derivative-maker.
|
||||
# Kicksecure's derivative-maker explicitly refuses to run as root and uses
|
||||
# sudo internally for its privileged operations (debootstrap, mksquashfs,
|
||||
# chroot mounts). build.sh chowns the workspace to this user inside the
|
||||
# container, then runuser's to it before invoking derivative-maker.
|
||||
# uid 1000 is conventional and plays nicely with bind mounts of files
|
||||
# created by other Linux tools.
|
||||
RUN useradd --uid 1000 --create-home --shell /bin/bash builder \
|
||||
&& echo 'builder ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/builder \
|
||||
&& chmod 440 /etc/sudoers.d/builder
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
48
linux/build/scripts/build-inner.sh
Normal file
48
linux/build/scripts/build-inner.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# SilverMetal Linux — inner build step.
|
||||
#
|
||||
# Runs *inside* the silvermetal-builder container, as the unprivileged
|
||||
# `builder` user. build.sh sets up the container, chowns the workspace,
|
||||
# and runuser's into here. derivative-maker takes it from there and uses
|
||||
# sudo internally for its privileged operations.
|
||||
#
|
||||
# Why this is its own file:
|
||||
# The previous incarnation lived as a heredoc inside build.sh's docker
|
||||
# run command. Once we needed to drop privileges from root to builder,
|
||||
# the nested-heredoc / nested-quoting situation became unreadable; a
|
||||
# plain script with normal quoting is far easier to maintain.
|
||||
#
|
||||
# Required env vars (set by build.sh and forwarded into the container):
|
||||
# REPO_ROOT — absolute path to the SilverMetal repo root
|
||||
# BUILD_DIR — where to drop the resulting *.iso and manifests
|
||||
# SOURCE_DATE_EPOCH — reproducibility timestamp (forwarded to live-build)
|
||||
# SNAPSHOT_TIMESTAMP — apt snapshot pin (forwarded to live-build)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${REPO_ROOT:?REPO_ROOT must be set}"
|
||||
: "${BUILD_DIR:?BUILD_DIR must be set}"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${REPO_ROOT}/linux/build/config/silvermetal-base.conf"
|
||||
|
||||
cd "${REPO_ROOT}/linux/build/derivative-maker"
|
||||
|
||||
./derivative-maker \
|
||||
--build \
|
||||
--target "${DERIVATIVE_BUILD_TARGET}" \
|
||||
--flavour "${DERIVATIVE_FLAVOUR}" \
|
||||
--arch "${DERIVATIVE_TARGET_ARCH}" \
|
||||
--dist "${DERIVATIVE_DIST}" \
|
||||
--config "${REPO_ROOT}/linux/build/config/silvermetal-base.conf"
|
||||
|
||||
# derivative-maker writes into its own build/ tree; collect into BUILD_DIR.
|
||||
# Exact upstream output paths can shift between tags — keep this tolerant.
|
||||
# Anything matching *.iso under the tree is what we want.
|
||||
find . -maxdepth 6 -type f -name "*.iso" -print0 \
|
||||
| xargs -0 -I{} cp -av "{}" "${BUILD_DIR}/"
|
||||
|
||||
# Manifest of file metadata that lives inside the ISO. Useful when
|
||||
# diagnosing reproducibility regressions without re-extracting.
|
||||
find . -maxdepth 6 -type f -name "*.manifest" -print0 \
|
||||
| xargs -0 -I{} cp -av "{}" "${BUILD_DIR}/" 2>/dev/null || true
|
||||
@@ -32,7 +32,7 @@ cd "${REPO_ROOT}"
|
||||
# outside the LAN — it's the entry that fleet-wide /etc/docker/daemon.json
|
||||
# registers as an insecure-registry. The host-style "docker-registry:5000"
|
||||
# is *not* DNS-resolvable; do not use it.
|
||||
BUILDER_IMAGE="${BUILDER_IMAGE:-docker-registry.silverlabs.uk/silvermetal-builder@sha256:9e7161f9f180483f434074d7f32c27c907955232bd0c44efe6dc0ee1d9e56ae0}"
|
||||
BUILDER_IMAGE="${BUILDER_IMAGE:-docker-registry.silverlabs.uk/silvermetal-builder@sha256:f8f0db3756df220d3de79371054fd43cf7f824ad27d9900328fef5723821bedc}"
|
||||
|
||||
if [[ "${BUILDER_IMAGE}" != *"@sha256:"* ]]; then
|
||||
echo "build.sh: BUILDER_IMAGE must be pinned by digest, got: ${BUILDER_IMAGE}" >&2
|
||||
@@ -121,30 +121,13 @@ docker run --rm --privileged \
|
||||
-w "${REPO_ROOT}" \
|
||||
"${BUILDER_IMAGE}" \
|
||||
bash -euo pipefail -c '
|
||||
# shellcheck disable=SC1091
|
||||
source "${REPO_ROOT}/linux/build/config/silvermetal-base.conf"
|
||||
|
||||
cd "${REPO_ROOT}/linux/build/derivative-maker"
|
||||
|
||||
./derivative-maker \
|
||||
--build \
|
||||
--target "${DERIVATIVE_BUILD_TARGET}" \
|
||||
--flavour "${DERIVATIVE_FLAVOUR}" \
|
||||
--arch "${DERIVATIVE_TARGET_ARCH}" \
|
||||
--dist "${DERIVATIVE_DIST}" \
|
||||
--config "${REPO_ROOT}/linux/build/config/silvermetal-base.conf"
|
||||
|
||||
# derivative-maker writes into its own build/ dir; collect into
|
||||
# BUILD_DIR. Exact upstream output paths can shift between tags —
|
||||
# keep this tolerant. Anything matching *.iso under the tree is
|
||||
# what we want.
|
||||
find . -maxdepth 6 -type f -name "*.iso" -print0 \
|
||||
| xargs -0 -I{} cp -av "{}" "${BUILD_DIR}/"
|
||||
|
||||
# Manifest of file metadata that lives inside the ISO. Useful when
|
||||
# diagnosing reproducibility regressions without re-extracting.
|
||||
find . -maxdepth 6 -type f -name "*.manifest" -print0 \
|
||||
| xargs -0 -I{} cp -av "{}" "${BUILD_DIR}/" 2>/dev/null || true
|
||||
# derivative-maker refuses to run as root (it uses sudo internally
|
||||
# for the privileged ops). Hand the workspace ownership to the
|
||||
# unprivileged builder user (uid 1000, created in the Dockerfile
|
||||
# with passwordless sudo), then drop privs and let build-inner.sh
|
||||
# do the actual work.
|
||||
chown -R builder:builder "${REPO_ROOT}" "${BUILD_DIR}"
|
||||
runuser -u builder -- "${REPO_ROOT}/linux/build/scripts/build-inner.sh"
|
||||
' || { echo "build.sh: derivative-maker failed"; exit 3; }
|
||||
|
||||
# --- Hash artefacts ---------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user