diff --git a/diskless.pkr.hcl b/diskless.pkr.hcl index 821968c..d7d6dc4 100644 --- a/diskless.pkr.hcl +++ b/diskless.pkr.hcl @@ -293,7 +293,16 @@ build { # --- 4. Assemble + sign the UKI; build the OCI release-artifact image ---- post-processor "shell-local" { inline = [ - "NVMET_TRADDR='${var.nvmet_traddr}' bash scripts/extract-artifacts.sh output-qemu/diskless-build.qcow2 ${var.artifact_dir}/${var.image_version} ${var.disable_dot1x}", + # extract-artifacts MUST run as root so tar/mkfs.erofs preserve the build + # tree's real ownership (root:ssh_keys, tty/crontab setgid groups, user + # home dirs) — we dropped mkfs.erofs --all-root, which used to mask this by + # flattening everything to root:root. sudo is a no-op if already root, and + # the runner has passwordless sudo (cf. the `sudo apt-get` install step). + "sudo bash -c \"NVMET_TRADDR='${var.nvmet_traddr}' bash scripts/extract-artifacts.sh output-qemu/diskless-build.qcow2 ${var.artifact_dir}/${var.image_version} ${var.disable_dot1x}\"", + # Hand the OUTPUT FILES back to the build user (their in-erofs ownership is + # already baked correctly) so build-oci.sh can write the UKI and push with + # the build user's `buildah login`. + "sudo chown -R \"$(id -u):$(id -g)\" ${var.artifact_dir}", "bash scripts/build-oci.sh ${var.artifact_dir}/${var.image_version} ${var.oci_image} ${var.image_version} '${var.sb_key}' '${var.sb_cert}' ${var.oci_push}", ] } diff --git a/docs/TODO.md b/docs/TODO.md index 39066b8..b64980b 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -223,6 +223,17 @@ boot LAN. build-time assertion that `/usr/bin/bash` is labeled before `mkfs.erofs` (mkfs.erofs preserves xattrs). Needs `attr` on the build host. The image now boots **enforcing**. + - **`mkfs.erofs --all-root` was flattening all ownership to root:root.** Mode + bits + (now) xattrs survive the export, but `--all-root` forced every inode + to uid/gid 0 — discarding group ownership (`ssh_keys` on host keys → + sshd "permissions 640 too open" → refused; `tty`/`crontab` setgid groups) + and user ownership (home dirs). **Fixed:** dropped `--all-root` so real + ownership is preserved from the tree. That now requires `extract-artifacts.sh` + to run as **root** (added an `id -u` guard; `tar -x` needs CAP_CHOWN to + restore ownership) — the post-processor runs it via `sudo` and `chown`s the + output files back to the build user so `build-oci.sh` (rootless buildah + + its own login) still works. Combined with the host-key strip above, sshd + starts cleanly. **⚠ The secure path has the same dependency on the (absent) nvmf autoconnect** — when 802.1X is re-enabled, the macsec0 path must likewise drive `connect-all` (or the network-manager port #1 must wire diff --git a/scripts/extract-artifacts.sh b/scripts/extract-artifacts.sh index dfc411d..3e46851 100755 --- a/scripts/extract-artifacts.sh +++ b/scripts/extract-artifacts.sh @@ -41,6 +41,16 @@ for bin in virt-copy-out virt-ls guestfish mkfs.erofs veritysetup jq getfattr; d command -v "$bin" >/dev/null || { echo "ERROR: $bin not found on build host" >&2; exit 1; } done +# Must run as root: `tar -x` can only restore the build tree's real ownership +# (root:ssh_keys host keys, tty/crontab setgid groups, service-user dirs, user +# home dirs) with CAP_CHOWN. Non-root silently flattens ownership to the caller +# -> a broken image (sshd refuses, setgid tools misbehave). We removed +# mkfs.erofs --all-root, so correct ownership now depends on this. +if [ "$(id -u)" -ne 0 ]; then + echo "ERROR: run extract-artifacts.sh as root so file ownership is preserved in the rootfs." >&2 + exit 1 +fi + mkdir -p "$OUT" export LIBGUESTFS_BACKEND=direct WORK=$(mktemp -d) @@ -98,8 +108,12 @@ echo "==> Building reproducible erofs image" # a kernel you've confirmed has EROFS_FS_ZIP (grep CONFIG_EROFS /boot/config-*). # -T0 + fixed UUID keep output deterministic => stable dm-verity root hash for # identical input trees. +# Do NOT use --all-root: it forces every inode to root:root, discarding group +# ownership (ssh_keys on host keys -> sshd "permissions too open"; tty/crontab +# setgid binaries) and user ownership (home dirs). Real ownership is preserved +# from the tree, which is why this script must run as root (see the euid check). FIXED_UUID="6c10a7f0-1d2e-4f5a-9b3c-000000000001" -mkfs.erofs -T 0 -U "$FIXED_UUID" --all-root \ +mkfs.erofs -T 0 -U "$FIXED_UUID" \ "$WORK/rootfs.erofs" "$WORK/root" echo "==> Generating dm-verity hash tree as a SEPARATE file (two-namespace layout)"