Label the filesystem for SELinux at build time
All checks were successful
build image / build (push) Successful in 25m5s

The image booted. Kernel came up, the device tree loaded — UFS, display,
WiFi and IPA all probed as platform devices — framebuffer console came up, root
mounted off USB and systemd started. Then:

    systemd[1]: Unable to fix SELinux security context of /dev/tty..: Permission denied
    (x hundreds)
    systemd[1]: Too many messages being logged to kmsg, ignoring
    [!!!!!!] Failed to allocate manager object.

The filesystem had no SELinux labels. mke2fs -d carries security.* xattrs
across faithfully, but nothing had ever set them: the tree came from dnf, not
from a running SELinux system. I had relied on /.autorelabel, which cannot
work here — PID 1 dies long before anything acts on the flag.

Label the tree with setfiles instead, after the bind mounts are torn down (or
it would walk the builder's /proc) and before /boot is split out, so /boot's
files are labelled along with everything else. Verified in a privileged
container beforehand that security.selinux xattrs can actually be written
through a bind mount, rather than assuming it.

I had listed policycoreutils in the gongfoo build base for exactly this and
then never called setfiles. It is now also in stage2's fallback toolchain, so
the stock-Fedora path works too.

Ship permissive regardless. The labels make enforcing viable, but the failure
mode is unusually punishing — no login prompt, no shell, nothing to repair from
— and on a machine this awkward to reach that is not a default worth choosing.
SELINUX_MODE in config/device.env flips it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XWRjNJMistCy6ngXH5aJLS
This commit is contained in:
2026-07-27 18:05:28 +03:00
parent 2243166642
commit 482c5d9c9a
3 changed files with 89 additions and 10 deletions

View File

@@ -73,13 +73,14 @@ find "$WORK" -mindepth 1 -maxdepth 1 \
# becomes a no-op. Only a stock Fedora image pays for it.
# ---------------------------------------------------------------------------
if command -v mke2fs >/dev/null && command -v mcopy >/dev/null \
&& command -v sgdisk >/dev/null && command -v zstd >/dev/null; then
&& command -v sgdisk >/dev/null && command -v setfiles >/dev/null; then
echo "build tooling already present in the container image"
else
log "Installing build tooling into the container"
dnf -y install --setopt=install_weak_deps=False \
--setopt=cachedir="$DNF_CACHE" --setopt=keepcache=1 \
e2fsprogs dosfstools mtools gdisk util-linux rsync zstd findutils \
policycoreutils \
>/dev/null
fi
@@ -261,9 +262,8 @@ printf 'root=UUID=%s ro %s\n' "$ROOT_UUID" "$DEVICE_CMDLINE" \
ln -sf ../run/systemd/resolve/stub-resolv.conf "$ROOTFS/etc/resolv.conf"
echo "$DEVICE_NAME" > "$ROOTFS/etc/hostname"
# mke2fs -d does not reliably carry SELinux labels across, and the builder has
# no policy loaded anyway. Relabel on first boot.
: > "$ROOTFS/.autorelabel"
# SELinux labelling happens later, once the bind mounts are gone — see
# "Labelling the filesystem for SELinux" below.
# ---------------------------------------------------------------------------
log "Configuring the target system"
@@ -399,6 +399,47 @@ for d in dev/pts dev sys proc; do
fi
done
# ---------------------------------------------------------------------------
log "Labelling the filesystem for SELinux"
# ---------------------------------------------------------------------------
# mke2fs -d builds the filesystem from a directory tree and carries security.*
# xattrs across, but nothing has set them: the tree came out of dnf, not out of
# a running SELinux system. Boot an unlabelled root and systemd cannot set a
# context on anything under /dev, logs a screenful of "Permission denied", and
# dies with "Failed to allocate manager object". /.autorelabel does not rescue
# it — PID 1 never survives long enough to act on the flag.
#
# So label it here, after the bind mounts are gone (or setfiles would walk the
# builder's /proc) and before /boot is split out, so /boot's files are labelled
# with everything else.
FILE_CONTEXTS="$ROOTFS/etc/selinux/targeted/contexts/files/file_contexts"
LABELLED=0
if [ -f "$FILE_CONTEXTS" ] && command -v setfiles >/dev/null; then
if setfiles -F -r "$ROOTFS" "$FILE_CONTEXTS" "$ROOTFS"; then
LABELLED=1
echo "filesystem labelled"
else
echo "warning: setfiles failed — falling back to a first-boot relabel" >&2
fi
else
echo "warning: no SELinux policy or no setfiles in this container" >&2
fi
mkdir -p "$ROOTFS/etc/selinux"
cat > "$ROOTFS/etc/selinux/config" <<EOF
# SELINUXTYPE= can take one of these values: targeted, minimum, mls
SELINUXTYPE=targeted
SELINUX=${SELINUX_MODE}
EOF
if [ "$LABELLED" = 1 ]; then
# Already labelled, so skip the first-boot relabel — it is several minutes
# of USB-speed I/O to reproduce what we just did.
rm -f "$ROOTFS/.autorelabel"
else
: > "$ROOTFS/.autorelabel"
fi
mv "$ROOTFS/boot" "$WORK/boot"
mkdir -p "$ROOTFS/boot"
rm -rf "$WORK/boot/efi"