Label the filesystem for SELinux at build time
All checks were successful
build image / build (push) Successful in 25m5s
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:
41
README.md
41
README.md
@@ -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.
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
Reference in New Issue
Block a user