Skip to content

Commit

Permalink
UKIIIIIII
Browse files Browse the repository at this point in the history
Signed-off-by: Itxaka <[email protected]>
  • Loading branch information
Itxaka authored and Itxaka committed Mar 14, 2023
1 parent 9e695c6 commit a697995
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 26 deletions.
154 changes: 134 additions & 20 deletions Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ framework:
ENV USER=root

COPY . /build

RUN go mod download
RUN go run -ldflags "${LDFLAGS}" ./cmd/profile-build/main.go ${FLAVOR} $REPOSITORIES_FILE /framework

# Copy kairos binaries
Expand Down Expand Up @@ -299,6 +299,7 @@ framework-image:
base-image:
ARG FLAVOR
ARG VARIANT
ARG BUILD_INITRD="true"
IF [ "$BASE_IMAGE" = "" ]
# Source the flavor-provided docker file
FROM DOCKERFILE -f images/Dockerfile.$FLAVOR .
Expand Down Expand Up @@ -359,24 +360,26 @@ base-image:
RUN rm -rf /boot/initrd.img-*
END
# Regenerate initrd if necessary
IF [ "$FLAVOR" = "opensuse-leap" ] || [ "$FLAVOR" = "opensuse-leap-arm-rpi" ] || [ "$FLAVOR" = "opensuse-tumbleweed-arm-rpi" ] || [ "$FLAVOR" = "opensuse-tumbleweed" ]
RUN mkinitrd
ELSE IF [ "$FLAVOR" = "fedora" ] || [ "$FLAVOR" = "rockylinux" ]
RUN kernel=$(ls /boot/vmlinuz-* | head -n1) && \
ln -sf "${kernel#/boot/}" /boot/vmlinuz
RUN kernel=$(ls /lib/modules | head -n1) && \
dracut -v -N -f "/boot/initrd-${kernel}" "${kernel}" && \
ln -sf "initrd-${kernel}" /boot/initrd
RUN kernel=$(ls /lib/modules | head -n1) && depmod -a "${kernel}"
# https://github.com/kairos-io/elemental-cli/blob/23ca64435fedb9f521c95e798d2c98d2714c53bd/pkg/elemental/elemental.go#L553
RUN rm -rf /boot/initramfs-*
ELSE IF [ "$FLAVOR" = "debian" ] || [ "$FLAVOR" = "ubuntu" ] || [ "$FLAVOR" = "ubuntu-20-lts" ] || [ "$FLAVOR" = "ubuntu-22-lts" ]
RUN kernel=$(ls /boot/vmlinuz-* | head -n1) && \
ln -sf "${kernel#/boot/}" /boot/vmlinuz
RUN kernel=$(ls /lib/modules | head -n1) && \
dracut -f "/boot/initrd-${kernel}" "${kernel}" && \
ln -sf "initrd-${kernel}" /boot/initrd
RUN kernel=$(ls /lib/modules | head -n1) && depmod -a "${kernel}"
IF [ "$BUILD_INITRD" = "true" ]
IF [ "$FLAVOR" = "opensuse-leap" ] || [ "$FLAVOR" = "opensuse-leap-arm-rpi" ] || [ "$FLAVOR" = "opensuse-tumbleweed-arm-rpi" ] || [ "$FLAVOR" = "opensuse-tumbleweed" ]
RUN mkinitrd
ELSE IF [ "$FLAVOR" = "fedora" ] || [ "$FLAVOR" = "rockylinux" ]
RUN kernel=$(ls /boot/vmlinuz-* | head -n1) && \
ln -sf "${kernel#/boot/}" /boot/vmlinuz
RUN kernel=$(ls /lib/modules | head -n1) && \
dracut -v -N -f "/boot/initrd-${kernel}" "${kernel}" && \
ln -sf "initrd-${kernel}" /boot/initrd
RUN kernel=$(ls /lib/modules | head -n1) && depmod -a "${kernel}"
# https://github.com/kairos-io/elemental-cli/blob/23ca64435fedb9f521c95e798d2c98d2714c53bd/pkg/elemental/elemental.go#L553
RUN rm -rf /boot/initramfs-*
ELSE IF [ "$FLAVOR" = "debian" ] || [ "$FLAVOR" = "ubuntu" ] || [ "$FLAVOR" = "ubuntu-20-lts" ] || [ "$FLAVOR" = "ubuntu-22-lts" ]
RUN kernel=$(ls /boot/vmlinuz-* | head -n1) && \
ln -sf "${kernel#/boot/}" /boot/vmlinuz
RUN kernel=$(ls /lib/modules | head -n1) && \
dracut -f "/boot/initrd-${kernel}" "${kernel}" && \
ln -sf "initrd-${kernel}" /boot/initrd
RUN kernel=$(ls /lib/modules | head -n1) && depmod -a "${kernel}"
END
END

IF [ ! -e "/boot/vmlinuz" ]
Expand All @@ -394,7 +397,8 @@ base-image:
RUN rm -rf /tmp/*

image:
FROM +base-image
ARG BUILD_INITRD="true"
FROM +base-image --BUILD_INITRD=$BUILD_INITRD
ARG FLAVOR
ARG VARIANT
ARG KAIROS_VERSION
Expand All @@ -418,6 +422,116 @@ image-rootfs:
FROM +image
SAVE ARTIFACT --keep-own /. rootfs

uki-artifacts:
FROM +image --BUILD_INITRD=false
RUN /usr/bin/immucore version
RUN ln -s /usr/bin/immucore /init
RUN find . \( -path ./sys -prune -o -path ./run -prune -o -path ./dev -prune -o -path ./tmp -prune -o -path ./proc -prune \) -o -print | cpio -R root:root -H newc -o | gzip -2 > /tmp/initramfs.cpio.gz
RUN echo "console=tty1 console=ttyS0 root=LABEL=COS_ACTIVE net.ifnames=1 cos-img/filename=/cOS/active.img panic=5 security=selinux rd.cos.oemlabel=COS_OEM selinux=1 rd.immucore.debug rd.immucore.uki" > /tmp/Cmdline
RUN basename $(ls /boot/vmlinuz-* |grep -v rescue | head -n1)| sed --expression "s/vmlinuz-//g" > /tmp/Uname
SAVE ARTIFACT /boot/vmlinuz Kernel
SAVE ARTIFACT /etc/os-release Osrelease
SAVE ARTIFACT /tmp/Cmdline Cmdline
SAVE ARTIFACT /tmp/Uname Uname
SAVE ARTIFACT /tmp/initramfs.cpio.gz Initrd

uki:
FROM registry.opensuse.org/opensuse/leap:15.4
RUN zypper in -y binutils systemd # objcopy from binutils and systemd-stub from systemd
WORKDIR build
COPY +uki-artifacts/Kernel Kernel
COPY +uki-artifacts/Initrd Initrd
COPY +uki-artifacts/Osrelease Osrelease
COPY +uki-artifacts/Uname Uname
COPY +uki-artifacts/Cmdline Cmdline
RUN objcopy /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
--add-section .osrel=Osrelease --set-section-flags .osrel=data,readonly \
--add-section .cmdline=Cmdline --set-section-flags .cmdline=data,readonly \
--add-section .initrd=Initrd --set-section-flags .initrd=data,readonly \
--add-section .uname=Uname --set-section-flags .uname=data,readonly \
--add-section .linux=Kernel --set-section-flags .linux=code,readonly \
$ISO_NAME.unsigned.efi \
--change-section-vma .osrel=0x17000 \
--change-section-vma .cmdline=0x18000 \
--change-section-vma .initrd=0x19000 \
--change-section-vma .uname=0x5a0ed000 \
--change-section-vma .linux=0x5a0ee000
ARG KVERSION=$(cat Uname)
SAVE ARTIFACT $ISO_NAME.unsigned.efi uki.efi AS LOCAL build/$ISO_NAME.unsigned-$KVERSION.efi


uki-signed:
FROM registry.opensuse.org/opensuse/leap:15.4
RUN zypper in -y efitools sbsigntools shim
# Platform key
RUN openssl req -new -x509 -subj "/CN=Kairos PK/" -days 3650 -nodes -newkey rsa:2048 -sha256 -keyout PK.key -out PK.crt
# CER keys are for FW install
RUN openssl x509 -in PK.crt -out PK.cer -outform DER
# Key exchange
RUN openssl req -new -x509 -subj "/CN=Kairos KEK/" -days 3650 -nodes -newkey rsa:2048 -sha256 -keyout KEK.key -out KEK.crt
# CER keys are for FW install
RUN openssl x509 -in KEK.crt -out KEK.cer -outform DER
# Signature DB
RUN openssl req -new -x509 -subj "/CN=Kairos DB/" -days 3650 -nodes -newkey rsa:2048 -sha256 -keyout DB.key -out DB.crt
# CER keys are for FW install
RUN openssl x509 -in DB.crt -out DB.cer -outform DER
COPY +uki/uki.efi .
RUN sbsign --key DB.key --cert DB.crt --output uki.signed.efi uki.efi
SAVE ARTIFACT PK.key PK.key AS LOCAL build/PK.key
SAVE ARTIFACT PK.crt PK.crt AS LOCAL build/PK.crt
SAVE ARTIFACT PK.cer PK.cer AS LOCAL build/PK.cer
SAVE ARTIFACT KEK.key KEK.key AS LOCAL build/KEK.key
SAVE ARTIFACT KEK.crt KEK.crt AS LOCAL build/KEK.crt
SAVE ARTIFACT KEK.cer KEK.cer AS LOCAL build/KEK.cer
SAVE ARTIFACT DB.key DB.key AS LOCAL build/DB.key
SAVE ARTIFACT DB.crt DB.crt AS LOCAL build/DB.crt
SAVE ARTIFACT DB.cer DB.cer AS LOCAL build/DB.cer
SAVE ARTIFACT uki.signed.efi uki.efi AS LOCAL build/uki.signed.efi
SAVE ARTIFACT /usr/share/efi/x86_64/MokManager.efi MokManager.efi

# This target will prepare a disk.img ready with the uki artifact on it for qemu. Just attach it to qemu and mark you vm to boot from that disk
# here we take advantage of the uefi fallback method, which will load an efi binary in /EFI/BOOT/BOOTX64.efi if there is nothing
# else that it can boot from :D Just make sure to have your disk.img set as boot device in qemu.
prepare-uki-disk-image:
FROM fedora:37
ARG SIGNED_EFI=false
RUN dnf install -y mtools
IF [ "$SIGNED_EFI" = "true" ]
COPY +uki-signed/uki.efi .
COPY +uki-signed/PK.key .
COPY +uki-signed/PK.crt .
COPY +uki-signed/PK.cer .
COPY +uki-signed/KEK.key .
COPY +uki-signed/KEK.crt .
COPY +uki-signed/KEK.cer .
COPY +uki-signed/DB.key .
COPY +uki-signed/DB.crt .
COPY +uki-signed/DB.cer .
COPY +uki-signed/MokManager.efi .
ELSE
COPY +uki/uki.efi .
END
RUN dd if=/dev/zero of=disk.img bs=1G count=2
RUN mformat -i disk.img -F ::
RUN mmd -i disk.img ::/EFI
RUN mmd -i disk.img ::/EFI/BOOT
RUN mcopy -i disk.img uki.efi ::/EFI/BOOT/BOOTX64.efi
IF [ "$SIGNED_EFI" = "true" ]
RUN mcopy -i disk.img PK.key ::/EFI/BOOT/PK.key
RUN mcopy -i disk.img PK.crt ::/EFI/BOOT/PK.crt
RUN mcopy -i disk.img PK.cer ::/EFI/BOOT/PK.cer
RUN mcopy -i disk.img KEK.key ::/EFI/BOOT/KEK.key
RUN mcopy -i disk.img KEK.crt ::/EFI/BOOT/KEK.crt
RUN mcopy -i disk.img KEK.cer ::/EFI/BOOT/KEK.cer
RUN mcopy -i disk.img DB.key ::/EFI/BOOT/DB.key
RUN mcopy -i disk.img DB.crt ::/EFI/BOOT/DB.crt
RUN mcopy -i disk.img DB.cer ::/EFI/BOOT/DB.cer
RUN mcopy -i disk.img MokManager.efi ::/EFI/BOOT/mmx64.efi
END
RUN mdir -i disk.img ::/EFI/BOOT
SAVE ARTIFACT disk.img AS LOCAL build/disk.img


###
### Artifacts targets (ISO, netboot, ARM)
###
Expand Down
66 changes: 66 additions & 0 deletions UKI-hack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# UKI: Unified Kernel Image


It's basically a kernel, initrd and cmdline for the kernel all lumped up together in an efi binary. Mixing it with something like systemd-stub
means that you can boot from the EFI shell directly into the system.

You can add more stuff to it like the os-release info, the kernel version (uname), splash image, Devicetree , etc...

This way you got everything in one nice package and can sign the whole thing for secureboot or calculate the hashes for measured boot.


Usually under secureboot the initrd is not signed (as its generated locally), so once the kernel is run initrd signature is not verified. Nor you can measure it with TPM PCRs

UKI bundles the kernel with initrd and everything else, so you can sign the whole thing AND pre-calculate the hashes for TPM PCRs in advance.


Good writeup: https://0pointer.net/blog/brave-new-trusted-boot-world.html


### So why not a bit more?

So why not store the whole system in the initramfs?

In this branch on the earthfile there is a new target called uki. This will generate an efi with the whole kairos system under the initramfs.
This uses immucore to mount and set up the whole system.

There is an extra target called `prepare-uki-disk-image` which will generate a disk.img with the efi file inside in the proper place, so you
can just attach that image to a qemu vm and boot from there. An extra arg `SIGNED_EFI` will provide the same image but with a signed efi and all the keys needed
to insert hem into the uefo firmware and test secureboot.

The only special thing the target does is use objcopy to add sections to the systemd-stub pointing to the correct data:

```bash
RUN objcopy /usr/lib/systemd/boot/efi/linuxx64.efi.stub \
--add-section .osrel=Osrelease --set-section-flags .osrel=data,readonly \
--add-section .cmdline=Cmdline --set-section-flags .cmdline=data,readonly \
--add-section .initrd=Initrd --set-section-flags .initrd=data,readonly \
--add-section .uname=Uname --set-section-flags .uname=data,readonly \
--add-section .linux=Kernel --set-section-flags .linux=code,readonly \
$ISO_NAME.unsigned.efi \
--change-section-vma .osrel=0x17000 \
--change-section-vma .cmdline=0x18000 \
--change-section-vma .initrd=0x19000 \
--change-section-vma .uname=0x5a0ed000 \
--change-section-vma .linux=0x5a0ee000
```

Where:
* Kernel is the kernel that will be booted.
* Initrd is the initramfs that will be booted by the kernel. Currently, a dump of the docker-rootfs...rootfs
* Uname the output of `uname -r` (Optional content)
* Osrelease is the /etc/os-release file from the kairos rootfs (Optional content)
* Cmdline is the line to be passed to the kernel (Optional content, but needed in our case)


Good links:

- https://man.archlinux.org/man/systemd-stub.7
- https://wiki.osdev.org/UEFI#UEFI_applications_in_detail
- https://github.com/uapi-group/specifications/blob/main/specs/unified_kernel_image.md
- https://man.archlinux.org/man/systemd-measure.1.en
- https://manuais.iessanclemente.net/images/a/a6/EFI-ShellCommandManual.pdf
- https://0pointer.net/blog/brave-new-trusted-boot-world.html



2 changes: 1 addition & 1 deletion overlay/files/system/oem/00_rootfs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ stages:
providers: ["aws", "gcp", "openstack", "cdrom"]
path: "/oem"
rootfs:
- if: '[ ! -f "/run/cos/recovery_mode" ]'
- if: '[ ! -f "/run/cos/recovery_mode" ] && [ ! -e "/run/cos/uki_mode" ]'
name: "Layout configuration"
environment_file: /run/cos/cos-layout.env
environment:
Expand Down
4 changes: 2 additions & 2 deletions overlay/files/system/oem/10_accounting.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ stages:
homedir: "/home/kairos"
groups:
- "admin"
- name: "Set user password if running in live"
if: "[ -e /run/cos/live_mode ]"
- name: "Set user password if running in live or uki"
if: "[ -e /run/cos/live_mode ] || [ -e /run/cos/uki_mode ]"
users:
kairos:
passwd: "kairos"
Expand Down
44 changes: 41 additions & 3 deletions overlay/files/system/oem/11_persistency.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
name: "Configure persistent dirs bind-mounts"
stages:
rootfs.after:
- if: '[ ! -f "/run/cos/recovery_mode" ]'
name: "Layout configuration"
- if: '[ -e "/run/cos/uki_mode" ]'
# omit the persistent partition on uki mode
# And mount all persistent mounts under the overlay
name: "Layout configuration for UKI"
environment_file: /run/cos/cos-layout.env
environment:
RW_PATHS: "/var /etc /srv"
OVERLAY: "tmpfs:25%"
PERSISTENT_STATE_PATHS: >-
/var
/etc
/src
/etc/systemd
/etc/modprobe.d
/etc/rancher
/etc/sysconfig
/etc/runlevels
/etc/ssh
/etc/ssl/certs
/etc/iscsi
/etc/cni
/etc/kubernetes
/home
/opt
/root
/snap
/var/snap
/usr/libexec
/var/log
/var/lib/rancher
/var/lib/kubelet
/var/lib/snapd
/var/lib/wicked
/var/lib/longhorn
/var/lib/cni
/usr/share/pki/trust
/usr/share/pki/trust/anchors
/var/lib/ca-certificates
- if: '[ ! -f "/run/cos/recovery_mode" ] && [ ! -e "/run/cos/uki_mode" ]'
name: "Layout configuration for active/passive"
environment_file: /run/cos/cos-layout.env
environment:
VOLUMES: "LABEL=COS_OEM:/oem LABEL=COS_PERSISTENT:/usr/local"
Expand Down Expand Up @@ -55,7 +93,7 @@ stages:
echo PERSISTENT_STATE_PATHS=\"${PERSISTENT_STATE_PATHS}\" >> /run/cos/cos-layout.env
- if: |
cat /proc/cmdline | grep -q "kairos.boot_live_mode"
name: "Layout configuration"
name: "Layout configuration for boot_live_mode"
environment_file: /run/cos/cos-layout.env
environment:
VOLUMES: "LABEL=COS_OEM:/oem LABEL=COS_PERSISTENT:/usr/local"
Expand Down

0 comments on commit a697995

Please sign in to comment.