diff --git a/pkgs/image/default.nix b/pkgs/image/default.nix index a248a09..7407df1 100644 --- a/pkgs/image/default.nix +++ b/pkgs/image/default.nix @@ -1,250 +1,286 @@ { + lib, pkgs, patosPkgs, version, runCommand, - updateUrl + updateUrl, }: let pname = "patos-image"; -in -runCommand pname { - inherit version; - inherit updateUrl; - buildInputs = with pkgs; [ - erofs-utils - dosfstools - mtools - jq - ]; + writeConf = + name: attrs: + pkgs.writeTextFile { + name = name; + text = lib.generators.toINI { + mkKeyValue = lib.generators.mkKeyValueDefault { + mkValueString = + v: + if v == true then + ''"yes"'' + else if v == false then + ''"no"'' + else if lib.isString v then + ''"${v}"'' + else + lib.generators.mkValueStringDefault { } v; + } "="; + } attrs; + }; - env = { - # vfat options won't efi won't find the fs otherwise. - SYSTEMD_REPART_MKFS_OPTIONS_VFAT = "-S 512 -c"; - SYSTEMD_REPART_MKFS_OPTIONS_EROFS = "--all-root -zlz4hc,12 -C1048576 -Efragments,dedupe,ztailpacking"; + secureBootImportKeys = writeConf "secure-boot-import-keys.service" { + Unit = { + Description = "Import Secure Boot keys"; + DefaultDependencies = false; + RequiresMountsFor = "/var/lib/sbctl /boot"; + ConditionPathExists = "/boot/sbctl/keys"; + After = "local-fs.target"; + }; + + Service = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "sbctl import-keys -d /boot/sbctl/keys"; + ExecStartPost = "rm -rf /boot/sbctl"; + }; }; - kernelCmdLine = "console=ttyS0 patos.secureboot=false"; -} -'' -mkdir -p $out/init.repart.d $out/final.repart.d -pushd $out + ukiTransfer = writeConf "10-uki.transfer" { + Source = { + Path = updateUrl; + MatchPattern = "patos_@v.efi"; + Type = "url-file"; + }; -mkdir rootfs -cp -prP ${patosPkgs.rootfs}/* rootfs/ -find rootfs/ -type d -exec chmod 755 {} \; + Target = { + InstancesMax = 2; + MatchPattern = "patos_@v+@l-@d.efi patos_@v+@l.efi patos_@v.efi"; + Mode = "0444"; + Path = "/EFI/Linux"; + PathRelativeTo = "esp"; + TriesDone = 0; + TriesLeft = 3; + Type = "regular-file"; + }; -# package kernel modules as sysext (will reduce the image size a little bit (~3MB)) -mkdir rootfs/etc/extensions -rm -rf rootfs/usr/lib/modules -cp ${patosPkgs.kernel}/patos-kernel-modules* rootfs/etc/extensions/ + Transfer = { + Verify = false; + }; + }; -# set default target to multi-user -ln -sf multi-user.target rootfs/usr/lib/systemd/system/default.target + rootVerityTransfer = writeConf "22-root-verity.transfer" { + Source = { + Type = "url-file"; + Path = updateUrl; + MatchPattern = "patos_@v_@u.verity"; + }; -# enable dbus -ln -sf ../dbus.service rootfs/usr/lib/systemd/system/multi-user.target.wants/dbus.service -ln -sf ../dbus.socket rootfs/usr/lib/systemd/system/sockets.target.wants/dbus.socket + Target = { + Type = "partition"; + Path = "auto"; + MatchPattern = "verity-@v"; + MatchPartitionType = "root-verity"; + ReadOnly = "1"; + }; -# enable network services -ln -sf ../systemd-networkd.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-networkd.service -ln -sf ../systemd-resolved.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-resolved.service -ln -sf ../systemd-timesyncd.service rootfs/usr/lib/systemd/system/multi-user.target.wants/systemd-timesyncd.service -# enable default network config -mv rootfs/usr/lib/systemd/network/89-ethernet.network.example rootfs/usr/lib/systemd/network/89-ethernet.network + Transfer = { + Verify = false; + }; + }; -# enable confext/sysext services -ln -sf ../systemd-confext.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-confext.service -ln -sf ../systemd-sysext.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-sysext.service + rootTransfer = writeConf "22-root.transfer" { + Source = { + Type = "url-file"; + Path = updateUrl; + MatchPattern = "patos_@v_@u.root"; + }; -cat <<EOF > rootfs/usr/lib/systemd/system/secure-boot-import-keys.service -[Unit] -Description=Import Secure Boot keys -DefaultDependencies=no -RequiresMountsFor=/var/lib/sbctl /boot -ConditionPathExists=/boot/sbctl/keys -After=local-fs.target + Target = { + Type = "partition"; + Path = "auto"; + MatchPattern = "root-@v"; + MatchPartitionType = "root"; + ReadOnly = 1; + }; + Transfer = { + Verify = false; + }; + }; +in +runCommand pname + { + inherit version; + inherit updateUrl; -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=sbctl import-keys -d /boot/sbctl/keys -ExecStartPost=rm -rf /boot/sbctl -EOF -ln -sf ../secure-boot-import-keys.service rootfs/usr/lib/systemd/system/sysinit.target.wants/secure-boot-import-keys.service + buildInputs = with pkgs; [ + erofs-utils + dosfstools + mtools + jq + ]; -# sysupdate -mkdir -p rootfs/etc/sysupdate.d -cat <<EOF > rootfs/etc/sysupdate.d/10-uki.transfer -[Source] -Path=${updateUrl} -MatchPattern=patos_@v.efi -Type=url-file + env = { + # vfat options won't efi won't find the fs otherwise. + SYSTEMD_REPART_MKFS_OPTIONS_VFAT = "-S 512 -c"; + SYSTEMD_REPART_MKFS_OPTIONS_EROFS = "--all-root -zlz4hc,12 -C1048576 -Efragments,dedupe,ztailpacking"; + }; -[Target] -InstancesMax=2 -MatchPattern=patos_@v+@l-@d.efi patos_@v+@l.efi patos_@v.efi -Mode=0444 -Path=/EFI/Linux -PathRelativeTo=esp -TriesDone=0 -TriesLeft=3 -Type=regular-file + kernelCmdLine = "console=ttyS0 patos.secureboot=false"; + } + '' + mkdir -p $out/init.repart.d $out/final.repart.d + pushd $out -[Transfer] -Verify=no -EOF + mkdir rootfs + cp -prP ${patosPkgs.rootfs}/* rootfs/ + find rootfs/ -type d -exec chmod 755 {} \; -cat <<EOF > rootfs/etc/sysupdate.d/20-root-verity.transfer -[Source] -Type=url-file -Path=${updateUrl} -MatchPattern=patos_@v_@u.verity + # package kernel modules as sysext (will reduce the image size a little bit (~3MB)) + mkdir rootfs/etc/extensions + rm -rf rootfs/usr/lib/modules + cp ${patosPkgs.kernel}/patos-kernel-modules* rootfs/etc/extensions/ -[Target] -Type=partition -Path=auto -MatchPattern=verity-@v -MatchPartitionType=root-verity -ReadOnly=1 + # set default target to multi-user + ln -sf multi-user.target rootfs/usr/lib/systemd/system/default.target -[Transfer] -Verify=no -EOF + # enable dbus + ln -sf ../dbus.service rootfs/usr/lib/systemd/system/multi-user.target.wants/dbus.service + ln -sf ../dbus.socket rootfs/usr/lib/systemd/system/sockets.target.wants/dbus.socket -cat <<EOF > rootfs/etc/sysupdate.d/22-root.transfer -[Source] -Type=url-file -Path=${updateUrl} -MatchPattern=patos_@v_@u.root + # enable network services + ln -sf ../systemd-networkd.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-networkd.service + ln -sf ../systemd-resolved.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-resolved.service + ln -sf ../systemd-timesyncd.service rootfs/usr/lib/systemd/system/multi-user.target.wants/systemd-timesyncd.service + # enable default network config + mv rootfs/usr/lib/systemd/network/89-ethernet.network.example rootfs/usr/lib/systemd/network/89-ethernet.network -[Target] -Type=partition -Path=auto -MatchPattern=root-@v -MatchPartitionType=root -ReadOnly=1 + # enable confext/sysext services + ln -sf ../systemd-confext.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-confext.service + ln -sf ../systemd-sysext.service rootfs/usr/lib/systemd/system/sysinit.target.wants/systemd-sysext.service -[Transfer] -Verify=no -EOF + cp ${secureBootImportKeys} rootfs/usr/lib/systemd/system/secure-boot-import-keys.service + ln -sf ../secure-boot-import-keys.service rootfs/usr/lib/systemd/system/sysinit.target.wants/secure-boot-import-keys.service -# Initial partitioning -cat <<EOF > init.repart.d/10-root.conf -[Partition] -Type=root -Format=erofs -Minimize=best -CopyFiles=/rootfs:/ -Verity=data -VerityMatchKey=root -SplitName=root -EOF + # sysupdate + mkdir -p rootfs/etc/sysupdate.d + cp ${rootTransfer} ${rootVerityTransfer} ${ukiTransfer} rootfs/etc/sysupdate.d/ -cat <<EOF > init.repart.d/20-root-verity.conf -[Partition] -Type=root-verity -Verity=hash -VerityMatchKey=root -Minimize=best -SplitName=verity -EOF + # Initial partitioning + cat <<EOF > init.repart.d/10-root.conf + [Partition] + Type=root + Format=erofs + Minimize=best + CopyFiles=/rootfs:/ + Verity=data + VerityMatchKey=root + SplitName=root + EOF -#TODO: Add verity signature partition + cat <<EOF > init.repart.d/20-root-verity.conf + [Partition] + Type=root-verity + Verity=hash + VerityMatchKey=root + Minimize=best + SplitName=verity + EOF -${patosPkgs.systemd}/usr/bin/systemd-repart \ - --no-pager \ - --empty=create \ - --size=auto \ - --definitions=./init.repart.d \ - --split=true \ - --json=pretty \ - --root=$out \ - patos_$version.raw > init-repart-output.json && rm -f patos_$version.raw + #TODO: Add verity signature partition -roothash=$(jq -r '.[0].roothash' init-repart-output.json) -rootPart=$(jq -r '.[0].split_path' init-repart-output.json) -rootUuid=$(jq -r '.[0].uuid' init-repart-output.json) + ${patosPkgs.systemd}/usr/bin/systemd-repart \ + --no-pager \ + --empty=create \ + --size=auto \ + --definitions=./init.repart.d \ + --split=true \ + --json=pretty \ + --root=$out \ + patos_$version.raw > init-repart-output.json && rm -f patos_$version.raw -verityPart=$(jq -r '.[1].split_path' init-repart-output.json) -verityUuid=$(jq -r '.[1].uuid' init-repart-output.json) + roothash=$(jq -r '.[0].roothash' init-repart-output.json) + rootPart=$(jq -r '.[0].split_path' init-repart-output.json) + rootUuid=$(jq -r '.[0].uuid' init-repart-output.json) -ln -sf patos_$version.verity.raw patos_${version}_$verityUuid.verity -ln -sf patos_$version.root.raw patos_${version}_$rootUuid.root + verityPart=$(jq -r '.[1].split_path' init-repart-output.json) + verityUuid=$(jq -r '.[1].uuid' init-repart-output.json) -${patosPkgs.systemd}/usr/bin/ukify build \ - --linux ${patosPkgs.kernel}/bzImage \ - --initrd ${patosPkgs.initrd}/initrd.xz \ - --os-release @rootfs/etc/os-release \ - --cmdline "$kernelCmdLine roothash=$roothash" \ - -o patos_${version}.efi + ln -sf patos_$version.verity.raw patos_${version}_$verityUuid.verity + ln -sf patos_$version.root.raw patos_${version}_$rootUuid.root -# install ESP -SYSTEMD_RELAX_ESP_CHECKS=1 ${patosPkgs.systemd}/usr/bin/bootctl install --root ./rootfs --esp-path /boot + ${patosPkgs.systemd}/usr/bin/ukify build \ + --linux ${patosPkgs.kernel}/bzImage \ + --initrd ${patosPkgs.initrd}/initrd.xz \ + --os-release @rootfs/etc/os-release \ + --cmdline "$kernelCmdLine roothash=$roothash" \ + -o patos_${version}.efi -# setup factory reset -mkdir -p rootfs/boot/EFI/tools -cp ${pkgs.edk2-uefi-shell}/shell.efi rootfs/boot/EFI/tools/ + # install ESP + SYSTEMD_RELAX_ESP_CHECKS=1 ${patosPkgs.systemd}/usr/bin/bootctl install --root ./rootfs --esp-path /boot -cat <<EOF > rootfs/boot/EFI/tools/factoryreset.nsh -setvar FactoryReset -guid 8cf2644b-4b0b-428f-9387-6d876050dc67 -nv -rt =%1 -reset -EOF + # setup factory reset + mkdir -p rootfs/boot/EFI/tools + cp ${pkgs.edk2-uefi-shell}/shell.efi rootfs/boot/EFI/tools/ -cat <<EOF > rootfs/boot/loader/entries/factoryreset.conf -title Enable Factory Reset -options -nostartup -nomap -options \EFI\tools\factoryreset.nsh L"t" -efi EFI/tools/shell.efi -EOF + cat <<EOF > rootfs/boot/EFI/tools/factoryreset.nsh + setvar FactoryReset -guid 8cf2644b-4b0b-428f-9387-6d876050dc67 -nv -rt =%1 + reset + EOF -echo "timeout 2" > rootfs/boot/loader/loader.conf + cat <<EOF > rootfs/boot/loader/entries/factoryreset.conf + title Enable Factory Reset + options -nostartup -nomap + options \EFI\tools\factoryreset.nsh L"t" + efi EFI/tools/shell.efi + EOF -# install UKI -cp patos_${version}.efi rootfs/boot/EFI/Linux + echo "timeout 2" > rootfs/boot/loader/loader.conf -# Final partitioning -cat <<EOF > final.repart.d/10-esp.conf -[Partition] -Type=esp -Format=vfat -SizeMinBytes=128M -SizeMaxBytes=128M -CopyFiles=/rootfs/boot:/ -EOF + # install UKI + cp patos_${version}.efi rootfs/boot/EFI/Linux -cat <<EOF > final.repart.d/20-root.conf -[Partition] -Type=root -Label=root-${version} -CopyBlocks=/$rootPart -UUID=$rootUuid -SizeMinBytes=64M -SizeMaxBytes=64M -ReadOnly=1 -EOF + # Final partitioning + cat <<EOF > final.repart.d/10-esp.conf + [Partition] + Type=esp + Format=vfat + SizeMinBytes=128M + SizeMaxBytes=128M + CopyFiles=/rootfs/boot:/ + EOF -cat <<EOF > final.repart.d/22-root-verity.conf -[Partition] -Type=root-verity -Label=verity-${version} -CopyBlocks=/$verityPart -UUID=$verityUuid -ReadOnly=1 -EOF + cat <<EOF > final.repart.d/20-root.conf + [Partition] + Type=root + Label=root-${version} + CopyBlocks=/$rootPart + UUID=$rootUuid + SizeMinBytes=64M + SizeMaxBytes=64M + ReadOnly=1 + EOF -# finalize image ready for boot -${patosPkgs.systemd}/usr/bin/systemd-repart \ - --no-pager \ - --empty=create \ - --size=auto \ - --definitions=./final.repart.d \ - --root=$out \ - patos_${version}.img > final-repart-output.json + cat <<EOF > final.repart.d/22-root-verity.conf + [Partition] + Type=root-verity + Label=verity-${version} + CopyBlocks=/$verityPart + UUID=$verityUuid + ReadOnly=1 + EOF -rm -rf rootfs init.repart.d final.repart.d *.json -sha256sum *.root *.verity *.efi *.tar.xz > SHA256SUMS + # finalize image ready for boot + ${patosPkgs.systemd}/usr/bin/systemd-repart \ + --no-pager \ + --empty=create \ + --size=auto \ + --definitions=./final.repart.d \ + --root=$out \ + patos_${version}.img > final-repart-output.json -popd -'' + rm -rf rootfs init.repart.d final.repart.d *.json + sha256sum *.root *.verity *.efi *.tar.xz > SHA256SUMS + + popd + ''