diff --git a/README.md b/README.md index 09e41db..c1cd6e4 100644 --- a/README.md +++ b/README.md @@ -177,3 +177,14 @@ In case you have an existing Torcx image you can convert it with the `convert_to ``` Please make also sure that your don't have a `containerd.service` drop in file under `/etc` that uses Torcx paths. + + +### Verity + +To generate sysext protected by dm-verity with a signed root hash pass `FORMAT=verity` before invoking any of the scripts. This requires `systemd-repart` with a version >= v255. This also requires passing a path to a private key and certificate through `KEY` and `CERT`. + +Here's an example: +``` +openssl req -batch -new -x509 -sha256 -newkey rsa:2048 -nodes -out root_key.crt -keyout root_key.pem -days 3650 +FORMAT=verity KEY=root_key.pem CERT=root_key.crt ./create_kubernetes_sysext.sh v1.27.3 k8s +``` diff --git a/bake.sh b/bake.sh index 5885324..3778eb5 100755 --- a/bake.sh +++ b/bake.sh @@ -6,6 +6,13 @@ FORMAT="${FORMAT:-squashfs}" ARCH="${ARCH-}" SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH-0}" export SOURCE_DATE_EPOCH +KEY="${KEY-}" +CERT="${CERT-}" + +die() { + echo >&2 "$@" + exit 1 +} # This script is to be called as helper by other scripts but can also be used standalone if [ $# -lt 1 ]; then @@ -22,12 +29,20 @@ elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then exit 1 fi +if [ "${FORMAT}" = "verity" ]; then + [ -z "${KEY}" ] && die "\$KEY required for verity" + [ -z "${CERT}" ] && die "\$CERT required for verity" +fi + SYSEXTNAME="$1" -if [ "${FORMAT}" != "squashfs" ] && [ "${FORMAT}" != "btrfs" ] && [ "${FORMAT}" != "ext4" ] && [ "${FORMAT}" != "ext2" ]; then - echo "Expected FORMAT=squashfs, FORMAT=btrfs, FORMAT=ext4, or FORMAT=ext2, got '${FORMAT}'" >&2 - exit 1 -fi +case ${FORMAT} in + squashfs) ;; + btrfs) ;; + ext4|ext2) ;; + verity) ;; + *) die "Unsupported format: '${FORMAT}'" ;; +esac # Map to valid values for https://www.freedesktop.org/software/systemd/man/os-release.html#ARCHITECTURE= if [ "${ARCH}" = "amd64" ] || [ "${ARCH}" = "x86_64" ]; then @@ -57,7 +72,11 @@ elif [ "${FORMAT}" = "ext4" ] || [ "${FORMAT}" = "ext2" ]; then # Note: We didn't chown to root:root, meaning that the file ownership is left as is mkfs."${FORMAT}" -E root_owner=0:0 -d "${SYSEXTNAME}" "${SYSEXTNAME}".raw resize2fs -M "${SYSEXTNAME}".raw -else +elif [ "${FORMAT}" = "squashfs" ]; then mksquashfs "${SYSEXTNAME}" "${SYSEXTNAME}".raw -all-root +elif [ "${FORMAT}" = "verity" ]; then + systemd-repart --private-key="${KEY}" --certificate="${CERT}" --root="${SYSEXTNAME}" --no-pager --empty=create --size=auto --definitions=repart.d "${SYSEXTNAME}.raw" +else + die "Unsupported format: ${FORMAT}" fi echo "Created ${SYSEXTNAME}.raw" diff --git a/repart.d/01-root.conf b/repart.d/01-root.conf new file mode 100644 index 0000000..890fde6 --- /dev/null +++ b/repart.d/01-root.conf @@ -0,0 +1,7 @@ +[Partition] +Type=root +CopyFiles=/:/ +Format=squashfs +Minimize=best +Verity=data +VerityMatchKey=sysext diff --git a/repart.d/02-verity.conf b/repart.d/02-verity.conf new file mode 100644 index 0000000..fc3ab37 --- /dev/null +++ b/repart.d/02-verity.conf @@ -0,0 +1,7 @@ +[Partition] +Type=root-verity +Verity=hash +VerityMatchKey=sysext +# Only works from v255 +Minimize=best +SizeMinBytes=4K diff --git a/repart.d/03-verity-sig.conf b/repart.d/03-verity-sig.conf new file mode 100644 index 0000000..e18269e --- /dev/null +++ b/repart.d/03-verity-sig.conf @@ -0,0 +1,4 @@ +[Partition] +Type=root-verity-sig +Verity=signature +VerityMatchKey=sysext diff --git a/wrap-verity.sh b/wrap-verity.sh new file mode 100755 index 0000000..1b2e118 --- /dev/null +++ b/wrap-verity.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +set -e + +data=$1 + +verity_hdr=$(veritysetup format $data ${data}.verity) +root_hash=$(echo $(awk -F: '/Root hash:/ { print $2 }' <<<$verity_hdr)) + +data_size=$(stat -c %s $data) +hash_size=$(stat -c %s ${data}.verity) +root_hash_front=${root_hash:0:32} +root_hash_back=${root_hash:32:32} + +# 512-byte sectors +data_sectors=$(( $data_size >> 9 )) +hash_sectors=$(( $hash_size >> 9 )) +data_type=4f68bce3-e8cd-4db1-96e7-fbcaf984b709 +hash_type=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 + +openssl req -batch -new -x509 -sha256 -newkey rsa:2048 -nodes -out root_key.crt -keyout root_key.pem -days 3650 +echo -n "$root_hash" >${data}.roothash +openssl smime -sign -nocerts -noattr -binary -in "${data}.roothash" -inkey "root_key.pem" -signer "root_key.crt" -outform der -out "${data}.roothash.p7s" + +cat <${data}.verity.sig +{"rootHash":"$root_hash","signature":"$(base64 -w 0 <${data}.roothash.p7s)"} +EOF +sig_size=$(stat -c %s ${data}.verity.sig) +# rounded up to 4096 bytes +sig_size=$(( ( $sig_size + 4095 ) / 4096 * 4096 )) +sig_sectors=$(( $sig_size >> 9 )) +sig_type=41092b05-9fc8-4523-994f-2def0408b176 + +# signature + GPT header + PMBR (?) +disk_size=$(( $data_size + $hash_size + 4096 + 2048 * 512 + 33 * 512)) +rm -f disk.img +fallocate -l $disk_size disk.img + +as_hex() { + str=$1 + printf "%.8s-%.4s-%.4s-%.4s-%.12s" \ + "${str:0:8}" "${str:8:4}" "${str:12:4}" \ + "${str:16:4}" "${str:20:12}" +} +data_uuid=$(as_hex $root_hash_front) +hash_uuid=$(as_hex $root_hash_back) + +cat <sda.sfdisk +label: gpt +unit: sectors +sector-size: 512 + +/dev/sda1 : start=2048, size= ${data_sectors}, type=${data_type}, uuid=${data_uuid} +/dev/sda2 : start=$(( $data_sectors + 2048 )), size= ${hash_sectors}, type=${hash_type}, uuid=${hash_uuid} +/dev/sda3 : start=$(( ${hash_sectors} + $data_sectors + 2048 )), size= ${sig_sectors}, type=${sig_type} +EOF + +sfdisk disk.img