diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..21b6891 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,5 @@ +/*/cfsctl +/*/extra/usr/lib/dracut/modules.d/37composefs/composefs-pivot-sysroot +/*/image.qcow2 +/*/tmp/ +/common/fix-verity/fix-verity.efi diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..def4cd7 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,9 @@ +# composefs examples + +This directory contains a few different approaches to using `cfsctl` to produce +a verified operating system image. + + - `uki`: an OS built around a [Unified Kernel + Image](https://github.com/uapi-group/specifications/blob/main/specs/unified_kernel_image.md). If this image is signed then the signature effectively covers every single file in the filesystem. This works with a special form of multi-stage `Containerfile` which builds a base image, measures it using `cfsctl` and then uses that measurement to inject the composefs image fs-verity hash into the second stage of the build which actually builds the UKI (and embeds the hash into the `.cmdline`). We avoid a circular hash dependency by removing the UKI from the final image via a white-out (but `cfsctl` still knows how to find it). + - `bls`: an OS built around a separate kernel and initramfs installed with a [Type #1 Boot Loader Specification Entries](https://uapi-group.org/specifications/specs/boot_loader_specification/#type-1-boot-loader-specification-entries). In this case we simply hack the bootloader entry to refer to the correct composefs hash at install type. + - `unified`: similar to the `uki` example, but avoiding the intermediate `cfsctl` step by running `cfsctl` inside a build stage from the `Containerfile` itself. This involves bind-mounting the earlier build stage of the base image so that we can measure it from inside the stage that builds the UKI. diff --git a/examples/bls/.gitignore b/examples/bls/.gitignore deleted file mode 100644 index acef31e..0000000 --- a/examples/bls/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/cfsctl -/extra/usr/lib/dracut/modules.d/37composefs/composefs-pivot-sysroot -/fix-verity.efi -/image.qcow2 -/tmp/ diff --git a/examples/bls/build b/examples/bls/build index 3089f70..5a6947b 100755 --- a/examples/bls/build +++ b/examples/bls/build @@ -42,18 +42,4 @@ else fi sed -i 's@ /boot/@ /@' "${BLE}" -> tmp/image.raw -SYSTEMD_REPART_MKFS_OPTIONS_EXT4='-O verity' \ - fakeroot \ - systemd-repart \ - --empty=require \ - --size=auto \ - --dry-run=no \ - --no-pager \ - --offline=yes \ - --root=tmp \ - --definitions=repart.d \ - tmp/image.raw - -qemu-img convert -f raw tmp/image.raw -O qcow2 image.qcow2 -./fix-verity image.qcow2 # https://github.com/tytso/e2fsprogs/issues/201 +../common/make-image image.qcow2 diff --git a/examples/bls/fix-verity b/examples/bls/fix-verity deleted file mode 100755 index 783a49a..0000000 --- a/examples/bls/fix-verity +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/sh - -# workaround for https://github.com/tytso/e2fsprogs/issues/201 - -set -eux - -# We use a custom UKI with an initramfs containing a script that remounts -# /sysroot read-write and enables fs-verity on all of the objects in -# /composefs/objects. -# -# The first time we're run (or if we are modified) we (re-)generate the UKI. -# This is done inside of a container (for independence from the host OS). - -image_file="$1" - -if [ "$0" -nt fix-verity.efi ]; then - podman run --rm -i fedora > tmp/fix-verity.efi <<'EOF' - set -eux - - cat > /tmp/fix-verity.sh <<'EOS' - mount -o remount,rw /sysroot - ( - cd /sysroot/composefs/objects - echo >&2 'Enabling fsverity on composefs objects' - for i in */*; do - fsverity enable $i; - done - echo >&2 'done!' - ) - umount /sysroot - sync - poweroff -ff -EOS - - ( - dnf --setopt keepcache=1 install -y \ - kernel binutils systemd-boot-unsigned btrfs-progs fsverity-utils - dracut \ - --uefi \ - --no-hostonly \ - --install 'sync fsverity' \ - --include /tmp/fix-verity.sh /lib/dracut/hooks/pre-pivot/fix-verity.sh \ - --kver "$(rpm -q kernel-core --qf '%{VERSION}-%{RELEASE}.%{ARCH}')" \ - --kernel-cmdline="root=PARTLABEL=root-x86-64 console=ttyS0" \ - /tmp/fix-verity.efi - ) >&2 - - cat /tmp/fix-verity.efi -EOF - mv tmp/fix-verity.efi fix-verity.efi -fi - -qemu-system-x86_64 \ - -nographic \ - -m 4096 \ - -enable-kvm \ - -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \ - -drive file="$1",if=virtio,media=disk \ - -kernel fix-verity.efi diff --git a/examples/bls/run b/examples/bls/run deleted file mode 100755 index 5742835..0000000 --- a/examples/bls/run +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -set -eux - -cd "${0%/*}" - -qemu-system-x86_64 \ - -m 4096 \ - -enable-kvm \ - -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \ - -drive file=image.qcow2,if=virtio,cache=unsafe \ - -nic user,model=virtio-net-pci diff --git a/examples/bls/run b/examples/bls/run new file mode 120000 index 0000000..29f550a --- /dev/null +++ b/examples/bls/run @@ -0,0 +1 @@ +../common/run \ No newline at end of file diff --git a/examples/common/README.md b/examples/common/README.md new file mode 100644 index 0000000..fb80792 --- /dev/null +++ b/examples/common/README.md @@ -0,0 +1,33 @@ +# Common files used in examples + +This isn't a composefs example, but it's used by the other examples. + +## `fix-fsverity/` + +This is a workaround for missing fs-verity support in e2fsprogs and +systemd-repart. + +That's being worked on here: + - https://github.com/systemd/systemd/issues/35352 + - https://github.com/tytso/e2fsprogs/pull/203 + +But we'll probably need this workaround until those changes are widely +available. + +## `run` + +The script to run the VM. + +## `make-image` + +Creates the qcow2 filesystem image from the contents of the `tmp/` directory. + +This also invokes the `fix-fsverity` hack required to build a working image. + +## `run-repart` + +The part of `make-image` that needs to run under `fakeroot`. + +## `repart.d` + +The partition definitions for `systemd-repart`. diff --git a/examples/common/fix-verity/Containerfile b/examples/common/fix-verity/Containerfile new file mode 100755 index 0000000..91228e8 --- /dev/null +++ b/examples/common/fix-verity/Containerfile @@ -0,0 +1,14 @@ +FROM fedora:41 +COPY dracut-hook.sh / +RUN --mount=type=cache,target=/var/cache/libdnf5 <&2 'Enabling fsverity on composefs objects' + for i in */*; do + fsverity enable $i; + done + echo >&2 'done!' +) +umount /sysroot +sync +poweroff -ff diff --git a/examples/common/fix-verity/fix-verity b/examples/common/fix-verity/fix-verity new file mode 100755 index 0000000..788c979 --- /dev/null +++ b/examples/common/fix-verity/fix-verity @@ -0,0 +1,27 @@ +#!/bin/sh + +set -eux + +mydir="${0%/*}" +fix_verity_efi="${mydir}/fix-verity.efi" + +# hihi fakeroot +unset LD_PRELOAD + +if [ ! -f ${fix_verity_efi} ]; then + if ! podman image exists quay.io/lis/fix-verity; then + podman image build -t quay.io/lis/fix-verity "${mydir}" + fi + + podman run --rm -i quay.io/lis/fix-verity \ + cat /fix-verity.efi > "${fix_verity_efi}".tmp + mv "${fix_verity_efi}.tmp" "${fix_verity_efi}" +fi + +qemu-system-x86_64 \ + -nographic \ + -m 4096 \ + -enable-kvm \ + -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \ + -drive file="$1",format=raw,if=virtio,media=disk \ + -kernel "${fix_verity_efi}" diff --git a/examples/common/make-image b/examples/common/make-image new file mode 100755 index 0000000..7d1b652 --- /dev/null +++ b/examples/common/make-image @@ -0,0 +1,8 @@ +#!/bin/sh + +set -eux + +fakeroot "${0%/*}/run-repart" tmp/image.raw +"${0%/*}/fix-verity/fix-verity" tmp/image.raw +qemu-img convert -f raw tmp/image.raw -O qcow2 image.qcow2 +rm tmp/image.raw diff --git a/examples/bls/repart.d/01-esp.conf b/examples/common/repart.d/01-esp.conf similarity index 100% rename from examples/bls/repart.d/01-esp.conf rename to examples/common/repart.d/01-esp.conf diff --git a/examples/bls/repart.d/02-sysroot.conf b/examples/common/repart.d/02-sysroot.conf similarity index 100% rename from examples/bls/repart.d/02-sysroot.conf rename to examples/common/repart.d/02-sysroot.conf diff --git a/examples/common/run b/examples/common/run new file mode 100755 index 0000000..5742835 --- /dev/null +++ b/examples/common/run @@ -0,0 +1,12 @@ +#!/bin/sh + +set -eux + +cd "${0%/*}" + +qemu-system-x86_64 \ + -m 4096 \ + -enable-kvm \ + -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \ + -drive file=image.qcow2,if=virtio,cache=unsafe \ + -nic user,model=virtio-net-pci diff --git a/examples/uki/make-image b/examples/common/run-repart similarity index 84% rename from examples/uki/make-image rename to examples/common/run-repart index ff05a0f..d847aa7 100755 --- a/examples/uki/make-image +++ b/examples/common/run-repart @@ -6,6 +6,8 @@ chown -R 0:0 tmp/sysroot chcon -R system_u:object_r:usr_t:s0 tmp/sysroot/composefs chcon system_u:object_r:var_t:s0 tmp/sysroot/var +definitions="${0%/*}/repart.d" + > tmp/image.raw SYSTEMD_REPART_MKFS_OPTIONS_EXT4='-O verity' \ systemd-repart \ @@ -15,5 +17,5 @@ SYSTEMD_REPART_MKFS_OPTIONS_EXT4='-O verity' \ --no-pager \ --offline=yes \ --root=tmp \ - --definitions=repart.d \ + --definitions="${definitions}" \ tmp/image.raw diff --git a/examples/uki/.gitignore b/examples/uki/.gitignore deleted file mode 100644 index acef31e..0000000 --- a/examples/uki/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/cfsctl -/extra/usr/lib/dracut/modules.d/37composefs/composefs-pivot-sysroot -/fix-verity.efi -/image.qcow2 -/tmp/ diff --git a/examples/uki/build b/examples/uki/build index 6d40e16..4ef698e 100755 --- a/examples/uki/build +++ b/examples/uki/build @@ -50,6 +50,4 @@ cp /usr/lib/systemd/boot/efi/systemd-bootx64.efi tmp/efi/EFI/systemd cp /usr/lib/systemd/boot/efi/systemd-bootx64.efi tmp/efi/EFI/BOOT/BOOTX64.EFI ${CFSCTL} oci prepare-boot "${FINAL_ID}" tmp/efi -fakeroot ./make-image -qemu-img convert -f raw tmp/image.raw -O qcow2 image.qcow2 -./fix-verity image.qcow2 # https://github.com/tytso/e2fsprogs/issues/201 +../common/make-image image.qcow2 diff --git a/examples/uki/fix-verity b/examples/uki/fix-verity deleted file mode 100755 index 783a49a..0000000 --- a/examples/uki/fix-verity +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/sh - -# workaround for https://github.com/tytso/e2fsprogs/issues/201 - -set -eux - -# We use a custom UKI with an initramfs containing a script that remounts -# /sysroot read-write and enables fs-verity on all of the objects in -# /composefs/objects. -# -# The first time we're run (or if we are modified) we (re-)generate the UKI. -# This is done inside of a container (for independence from the host OS). - -image_file="$1" - -if [ "$0" -nt fix-verity.efi ]; then - podman run --rm -i fedora > tmp/fix-verity.efi <<'EOF' - set -eux - - cat > /tmp/fix-verity.sh <<'EOS' - mount -o remount,rw /sysroot - ( - cd /sysroot/composefs/objects - echo >&2 'Enabling fsverity on composefs objects' - for i in */*; do - fsverity enable $i; - done - echo >&2 'done!' - ) - umount /sysroot - sync - poweroff -ff -EOS - - ( - dnf --setopt keepcache=1 install -y \ - kernel binutils systemd-boot-unsigned btrfs-progs fsverity-utils - dracut \ - --uefi \ - --no-hostonly \ - --install 'sync fsverity' \ - --include /tmp/fix-verity.sh /lib/dracut/hooks/pre-pivot/fix-verity.sh \ - --kver "$(rpm -q kernel-core --qf '%{VERSION}-%{RELEASE}.%{ARCH}')" \ - --kernel-cmdline="root=PARTLABEL=root-x86-64 console=ttyS0" \ - /tmp/fix-verity.efi - ) >&2 - - cat /tmp/fix-verity.efi -EOF - mv tmp/fix-verity.efi fix-verity.efi -fi - -qemu-system-x86_64 \ - -nographic \ - -m 4096 \ - -enable-kvm \ - -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \ - -drive file="$1",if=virtio,media=disk \ - -kernel fix-verity.efi diff --git a/examples/uki/repart.d/01-esp.conf b/examples/uki/repart.d/01-esp.conf deleted file mode 100644 index 67f93e1..0000000 --- a/examples/uki/repart.d/01-esp.conf +++ /dev/null @@ -1,6 +0,0 @@ -[Partition] -Type=esp -Format=vfat -CopyFiles=/efi:/ -SizeMinBytes=512M -SizeMaxBytes=512M diff --git a/examples/uki/repart.d/02-sysroot.conf b/examples/uki/repart.d/02-sysroot.conf deleted file mode 100644 index 65f289e..0000000 --- a/examples/uki/repart.d/02-sysroot.conf +++ /dev/null @@ -1,6 +0,0 @@ -[Partition] -Type=root -Format=ext4 -SizeMinBytes=10G -SizeMaxBytes=10G -CopyFiles=/sysroot:/ diff --git a/examples/uki/run b/examples/uki/run deleted file mode 100755 index 5742835..0000000 --- a/examples/uki/run +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -set -eux - -cd "${0%/*}" - -qemu-system-x86_64 \ - -m 4096 \ - -enable-kvm \ - -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \ - -drive file=image.qcow2,if=virtio,cache=unsafe \ - -nic user,model=virtio-net-pci diff --git a/examples/uki/run b/examples/uki/run new file mode 120000 index 0000000..29f550a --- /dev/null +++ b/examples/uki/run @@ -0,0 +1 @@ +../common/run \ No newline at end of file diff --git a/examples/unified/.gitignore b/examples/unified/.gitignore deleted file mode 100644 index acef31e..0000000 --- a/examples/unified/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/cfsctl -/extra/usr/lib/dracut/modules.d/37composefs/composefs-pivot-sysroot -/fix-verity.efi -/image.qcow2 -/tmp/ diff --git a/examples/unified/build b/examples/unified/build index 3a51933..324d311 100755 --- a/examples/unified/build +++ b/examples/unified/build @@ -30,6 +30,4 @@ cp /usr/lib/systemd/boot/efi/systemd-bootx64.efi tmp/efi/EFI/systemd cp /usr/lib/systemd/boot/efi/systemd-bootx64.efi tmp/efi/EFI/BOOT/BOOTX64.EFI ${CFSCTL} oci prepare-boot "${IMAGE_ID}" tmp/efi -fakeroot ./make-image -qemu-img convert -f raw tmp/image.raw -O qcow2 image.qcow2 -./fix-verity image.qcow2 # https://github.com/tytso/e2fsprogs/issues/201 +../common/make-image image.qcow2 diff --git a/examples/unified/fix-verity b/examples/unified/fix-verity deleted file mode 100755 index 783a49a..0000000 --- a/examples/unified/fix-verity +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/sh - -# workaround for https://github.com/tytso/e2fsprogs/issues/201 - -set -eux - -# We use a custom UKI with an initramfs containing a script that remounts -# /sysroot read-write and enables fs-verity on all of the objects in -# /composefs/objects. -# -# The first time we're run (or if we are modified) we (re-)generate the UKI. -# This is done inside of a container (for independence from the host OS). - -image_file="$1" - -if [ "$0" -nt fix-verity.efi ]; then - podman run --rm -i fedora > tmp/fix-verity.efi <<'EOF' - set -eux - - cat > /tmp/fix-verity.sh <<'EOS' - mount -o remount,rw /sysroot - ( - cd /sysroot/composefs/objects - echo >&2 'Enabling fsverity on composefs objects' - for i in */*; do - fsverity enable $i; - done - echo >&2 'done!' - ) - umount /sysroot - sync - poweroff -ff -EOS - - ( - dnf --setopt keepcache=1 install -y \ - kernel binutils systemd-boot-unsigned btrfs-progs fsverity-utils - dracut \ - --uefi \ - --no-hostonly \ - --install 'sync fsverity' \ - --include /tmp/fix-verity.sh /lib/dracut/hooks/pre-pivot/fix-verity.sh \ - --kver "$(rpm -q kernel-core --qf '%{VERSION}-%{RELEASE}.%{ARCH}')" \ - --kernel-cmdline="root=PARTLABEL=root-x86-64 console=ttyS0" \ - /tmp/fix-verity.efi - ) >&2 - - cat /tmp/fix-verity.efi -EOF - mv tmp/fix-verity.efi fix-verity.efi -fi - -qemu-system-x86_64 \ - -nographic \ - -m 4096 \ - -enable-kvm \ - -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \ - -drive file="$1",if=virtio,media=disk \ - -kernel fix-verity.efi diff --git a/examples/unified/make-image b/examples/unified/make-image deleted file mode 100755 index ff05a0f..0000000 --- a/examples/unified/make-image +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -set -eux - -chown -R 0:0 tmp/sysroot -chcon -R system_u:object_r:usr_t:s0 tmp/sysroot/composefs -chcon system_u:object_r:var_t:s0 tmp/sysroot/var - -> tmp/image.raw -SYSTEMD_REPART_MKFS_OPTIONS_EXT4='-O verity' \ - systemd-repart \ - --empty=require \ - --size=auto \ - --dry-run=no \ - --no-pager \ - --offline=yes \ - --root=tmp \ - --definitions=repart.d \ - tmp/image.raw diff --git a/examples/unified/repart.d/01-esp.conf b/examples/unified/repart.d/01-esp.conf deleted file mode 100644 index 67f93e1..0000000 --- a/examples/unified/repart.d/01-esp.conf +++ /dev/null @@ -1,6 +0,0 @@ -[Partition] -Type=esp -Format=vfat -CopyFiles=/efi:/ -SizeMinBytes=512M -SizeMaxBytes=512M diff --git a/examples/unified/repart.d/02-sysroot.conf b/examples/unified/repart.d/02-sysroot.conf deleted file mode 100644 index 65f289e..0000000 --- a/examples/unified/repart.d/02-sysroot.conf +++ /dev/null @@ -1,6 +0,0 @@ -[Partition] -Type=root -Format=ext4 -SizeMinBytes=10G -SizeMaxBytes=10G -CopyFiles=/sysroot:/ diff --git a/examples/unified/run b/examples/unified/run deleted file mode 100755 index 5742835..0000000 --- a/examples/unified/run +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -set -eux - -cd "${0%/*}" - -qemu-system-x86_64 \ - -m 4096 \ - -enable-kvm \ - -bios /usr/share/edk2/ovmf/OVMF_CODE.fd \ - -drive file=image.qcow2,if=virtio,cache=unsafe \ - -nic user,model=virtio-net-pci diff --git a/examples/unified/run b/examples/unified/run new file mode 120000 index 0000000..29f550a --- /dev/null +++ b/examples/unified/run @@ -0,0 +1 @@ +../common/run \ No newline at end of file