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

@@ -122,11 +122,40 @@ docs/ Installation, firmware, runner setup
- [docs/firmware.md](docs/firmware.md) — what needs extracting from Windows and why - [docs/firmware.md](docs/firmware.md) — what needs extracting from Windows and why
- [docs/runner-setup.md](docs/runner-setup.md) — one-time Gitea runner preparation - [docs/runner-setup.md](docs/runner-setup.md) — one-time Gitea runner preparation
## Boot status
Confirmed on hardware, from a USB stick:
- GRUB loads the kernel and the device tree
- The kernel comes up and probes the SDM850 — UFS controller, display
subsystem, WiFi and IPA all appear as platform devices, so
`DEVICE_CMDLINE` and the DTB are right
- The framebuffer console works (`simple-framebuffer`, 240x67)
- The root filesystem mounts and systemd starts
Not yet confirmed: reaching a login prompt, and anything past it. SELinux is
shipped permissive — see below.
### SELinux
The build labels the filesystem offline with `setfiles`, so enforcing should
work. It ships **permissive** anyway, because the failure mode is unusually
punishing: an unlabelled root takes PID 1 down with `Failed to allocate manager
object` before anything can be logged into and repaired, and `/.autorelabel`
cannot save it because nothing survives long enough to act on the flag.
Once the machine is up and you are happy with it:
```sh
sudo setenforce 1 # try it for this boot
sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
```
Or set `SELINUX_MODE=enforcing` in `config/device.env` and rebuild.
## Caveats ## Caveats
The build has not yet been confirmed to boot on real hardware. The device tree, The kernel command line and firmware layout are taken from Fedora's Snapdragon
kernel command line and firmware layout are taken from Fedora's Snapdragon WoA WoA documentation and the aarch64-laptops project; the parts specific to the
documentation and the aarch64-laptops project; the parts specific to the C630's C630's older SDM850 are reasoned from those rather than derived from
older SDM850 are reasoned from those rather than tested. Expect to spend a boot documentation for this machine. Findings belong in this README.
or two adjusting `DEVICE_CMDLINE` in `config/device.env`. Findings belong in
this README.

View File

@@ -73,13 +73,14 @@ find "$WORK" -mindepth 1 -maxdepth 1 \
# becomes a no-op. Only a stock Fedora image pays for it. # becomes a no-op. Only a stock Fedora image pays for it.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
if command -v mke2fs >/dev/null && command -v mcopy >/dev/null \ 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" echo "build tooling already present in the container image"
else else
log "Installing build tooling into the container" log "Installing build tooling into the container"
dnf -y install --setopt=install_weak_deps=False \ dnf -y install --setopt=install_weak_deps=False \
--setopt=cachedir="$DNF_CACHE" --setopt=keepcache=1 \ --setopt=cachedir="$DNF_CACHE" --setopt=keepcache=1 \
e2fsprogs dosfstools mtools gdisk util-linux rsync zstd findutils \ e2fsprogs dosfstools mtools gdisk util-linux rsync zstd findutils \
policycoreutils \
>/dev/null >/dev/null
fi 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" ln -sf ../run/systemd/resolve/stub-resolv.conf "$ROOTFS/etc/resolv.conf"
echo "$DEVICE_NAME" > "$ROOTFS/etc/hostname" echo "$DEVICE_NAME" > "$ROOTFS/etc/hostname"
# mke2fs -d does not reliably carry SELinux labels across, and the builder has # SELinux labelling happens later, once the bind mounts are gone — see
# no policy loaded anyway. Relabel on first boot. # "Labelling the filesystem for SELinux" below.
: > "$ROOTFS/.autorelabel"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
log "Configuring the target system" log "Configuring the target system"
@@ -399,6 +399,47 @@ for d in dev/pts dev sys proc; do
fi fi
done 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" mv "$ROOTFS/boot" "$WORK/boot"
mkdir -p "$ROOTFS/boot" mkdir -p "$ROOTFS/boot"
rm -rf "$WORK/boot/efi" rm -rf "$WORK/boot/efi"

View File

@@ -44,6 +44,15 @@
: "${FEDORA_RELEASE:=44}" : "${FEDORA_RELEASE:=44}"
: "${TARGET_ARCH:=aarch64}" : "${TARGET_ARCH:=aarch64}"
# --- selinux ------------------------------------------------------------
# The build labels the filesystem offline with setfiles, so enforcing is
# viable. It ships permissive anyway: an unlabelled or mislabelled filesystem
# takes PID 1 down with "Failed to allocate manager object" before anything can
# be logged in and fixed, and on a machine this awkward to debug that trade is
# not worth making by default. Switch with `sudo setenforce 1` once it is up,
# or set this to enforcing and rebuild.
: "${SELINUX_MODE:=permissive}"
# --- default account ---------------------------------------------------- # --- default account ----------------------------------------------------
# Password is expired at first login, so it must be changed immediately. # Password is expired at first login, so it must be changed immediately.
: "${DEFAULT_USER:=fedora}" : "${DEFAULT_USER:=fedora}"