Skip to content

Commit

Permalink
Merge pull request #30 from smoser/feature/restore-the-api
Browse files Browse the repository at this point in the history
Feature / bootkit does not need keyset access
  • Loading branch information
hallyn committed Aug 18, 2023
2 parents c7684b4 + a421197 commit 14a2a0d
Show file tree
Hide file tree
Showing 33 changed files with 5,594 additions and 241 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ jobs:
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
with:
files: pkg/oci-boot
files: pkg/bkcust
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
NAME := bootkit
COMMANDS = pkg/bkcust pkg/oci-boot

include subs.mk
include common.mk
Expand All @@ -7,6 +8,22 @@ include common.mk
build:
$(STACKER_RBUILD)

custom: pkg/bkcust
$(STACKER_BUILD) --stacker-file=layers/custom/custom.yaml

bin: $(COMMANDS)

$(COMMANDS): $(ALL_GO_FILES)
@$(call pkg_build,./cmd/$(notdir $@))

.PHONY: pkg-build
pkg-build:
cd pkg && $(STACKER_BUILD)

go-build: $(ALL_GO_FILES) $(COMMANDS)
@$(call pkg_build,./... ./cmd/*)


LAYERS := $(shell cd $(TOP_D)/layers && \
for d in *; do [ -f "$$d/stacker.yaml" ] && echo "$$d"; done )

Expand All @@ -15,7 +32,7 @@ LAYERS := $(shell cd $(TOP_D)/layers && \
# --stacker-file=layers/<layer>/stacker.yaml
# The result is you can 'make layer-shim' and also tab-complete that.
$(foreach d,$(LAYERS),layer-$(d)):
@echo $(STACKER_BUILD) "--stacker-file=$(@:layer-=layers/)*/stacker.yaml"
$(STACKER_BUILD) "--stacker-file=$(subst layer-,layers/,$@)/stacker.yaml"

.PHONY: publish
publish:
Expand Down
42 changes: 33 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
# bootkit

## bootkit layer
The build of bootkit uses a (named) keyset as created by
(trust)[https://github.com/project-machine/trust].
The build of bootkit creates a layer 'bootkit' that has ovmf (kvm firmware), shim, kernel, and initramfs artifacts in an easily available and organized format.

This can be defined by substituting KEYSET_D during the stacker build.
The list of files in a bootkit layer are:

* bootkit/mos/initrd-mos.cpio.gz - The files needed for 'mos' functionality in an initramfs. This is effectively a "mos initrd module" that can be appended to initrd/core.cpio.gz to give mos functionality.

* bootkit/stubby/stubby.efi - A build of [stubby](https://github.com/puzzleos/stubby). The consumer combines stubby, kernel, initramfs and cmdline to create a UKI.

* bootkit/ovmf/ovmf-code.fd, bootkit/ovmf/ovmf-vars.fd - A build of OVMF code and vars. The vars are empty, they contain no built-in PK, KEK or DB values, and are expected to be customized before use. Vars can be customized via [virt-firmware](https://pypi.org/project/virt-firmware/).

* bootkit/initrd/firmware.cpio.gz - This contains early microcode for linux kernel. If used, it should be the first content in an initramfs and should not be compressed. See linux kernel [doc](https://github.com/torvalds/linux/blob/master/Documentation/arch/x86/microcode.rst) for more information.

* bootkit/kernel/version - text file containing 'uname -r' for the kernel.

* bootkit/kernel/boot/vmlinuz - linux kernel for booting.

* bootkit/kernel/modules.squashfs - A squashfs archive of kernel modules. The top level directory in it is the output of `uname -r`. It can be directly consumed by mounting over /lib/modules.

* bootkit/kernel/initrd-modules.cpio.gz - a cpio archive of kernel modules that are typically used in an initramfs. (modules.squashfs is a strict superset)

* bootkit/shim/shim.efi - a build of [shim](https://github.com/rhboot/shim) with no included VENDOR_DB. This needs to be customized by adding a signature list. See ([cert-to-efi-sig-list(1)](https://manpages.ubuntu.com/manpages/jammy/man1/cert-to-efi-sig-list.1.html) for information on signature list format.

## customized layer
Customized layer can be built with `make custom KEYSET_D=/path/to/keyset`.

A keyset is needed to build the customized layer. the path to a keyset directory in the same format as [keys](https://github.com/project-machine/keys/) repository must be provided in the KEYSET_D variable.

Certificates from keyset are embedded into the produced artifacts. Private keys from the provided keyset are used to sign the artifacts.

The customized layer build output contains:

* customized/ovmf-code.fd, customized/ovmf-vars.fd - OVMF code and vars. Vars are populated with uefi-db, uefi-pk, uefi-kek.
* customized/shim.efi - A shim that contains in it's DB the certificates for `uki-production`, `uki-tpm`, and `uki-limited`. It is signed with the `uefi-pk` private key.
* customized/kernel.efi - a Universal Kernel Image. It contains the `manifest-ca/cert.pem` key at /manifestCA.pem and is signed with `uki-production` private key.

Bootkit publishes a oci image that contains these files:
* boot.tar - tarball of normal linux distribution boot/ files.
* shim.efi - a shim loader signed with the uefi-db keys.
* kernel.efi - a universal kernel initrd signed with the production (uki-production) keys.
* ovmf-vars.fd, ovmf-code.fd - OVMF files for qemu that are populated
with the uki-limited, uki-production, and uki-tpm keys.

## oci-boot
oci-boot is a tool that can be used to create a bootable iso or disk image from the
Expand Down
11 changes: 9 additions & 2 deletions common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ STACKER_D ?= $(BUILD_D)/stacker
ROOTS_D ?= $(BUILD_D)/roots
OCI_D ?= $(BUILD_D)/oci

# STACKER_COMMON_OPTS = --debug
STACKER_COMMON_OPTS = --debug
# STACKER_BUILD_ARGS = --shell-fail
STACKER_OPTS = --stacker-dir=$(STACKER_D) --roots-dir=$(ROOTS_D) --oci-dir=$(OCI_D) $(STACKER_COMMON_OPTS)
STACKER_BUILD = stacker $(STACKER_OPTS) build $(STACKER_BUILD_ARGS) --layer-type=squashfs --layer-type=tar --layer-type=squashfs $(STACKER_SUBS)
STACKER_BUILD = stacker $(STACKER_OPTS) build $(STACKER_BUILD_ARGS) --layer-type=squashfs --layer-type=tar $(STACKER_SUBS)
STACKER_RBUILD = stacker $(STACKER_OPTS) recursive-build $(STACKER_BUILD_ARGS) --search-dir=layers/ --layer-type=squashfs --layer-type=tar $(STACKER_SUBS)
STACKER_PUBLISH = stacker $(STACKER_OPTS) publish \
--search-dir=layers/ --layer-type=squashfs $(STACKER_SUBS) \
Expand All @@ -30,3 +30,10 @@ STACKER_PUBLISH = stacker $(STACKER_OPTS) publish \
# It expands to:
# [ -n "value-of-$VARNAME" ] || { echo "rule-name ...VARNAME"; exit 1; }
required_var = [ -n "$(value $1)" ] || { echo "$@ requires environment variable $1"; exit 1; }

ALL_GO_FILES := $(wildcard pkg/*.go pkg/*/*.go pkg/*/*/*.go)

pkg_build = vr() { echo "$$@" 1>&2; "$$@"; } ; \
build() { for f in "$$@"; do \
vr go build -buildmode=exe -tags containers_image_openpgp "$$f" || break; done; } ; \
vr cd pkg && export GO_BIN=. && build $(1)
30 changes: 12 additions & 18 deletions layers/bootkit/stacker.yaml
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
config:
prerequisites:
- ../kernel/stacker.yaml
- ../shim/stacker.yaml
- ../uki/stacker.yaml
- ../mos/stacker.yaml
- ../ovmf/stacker.yaml
- ../shim/stacker.yaml
- ../stubby/stacker.yaml

bootkit-assemble:
build_only: true
from:
type: built
tag: build-krd
import:
- stacker://shim-build/export/shim.tar
- stacker://uki-build/export/uki.tar
- stacker://kernel-build/export/initrd.tar
- stacker://kernel-build/export/kernel.tar
- stacker://mos-build/export/mos.tar
- stacker://ovmf-build/export/ovmf.tar
- stacker://kernel-build/export/boot.tar
- stacker://kernel-build/export/modules.squashfs
- stacker://shim-build/export/shim.tar
- stacker://stubby-build/export/stubby.tar
run: |
#!/bin/bash -ex
d=$(mktemp -d)
trap "rm -Rf $d" EXIT
tar -C "$d" -xf /stacker/shim.tar
tar -C "$d" -xf /stacker/uki.tar
tar -C "$d" -xf /stacker/ovmf.tar
prepd="$d/bootkit"
mkdir "$prepd"
cp "$d/shim/shim.efi" "$prepd/shim.efi"
cp "$d/uki/kernel.efi" "$prepd/kernel.efi"
cp "$d/ovmf/ovmf-vars.fd" "$prepd/ovmf-vars.fd"
cp "$d/ovmf/ovmf-vars-empty.fd" "$prepd/ovmf-vars-empty.fd"
cp "$d/ovmf/ovmf-code.fd" "$prepd/ovmf-code.fd"
cp /stacker/boot.tar /stacker/modules.squashfs "$prepd"
mkdir "$d/bootkit"
for f in /stacker/*.tar; do
tar -C "$d/bootkit" -xf "$f"
done
mkdir /export
tar -C "$d" -cf /export/bootkit.tar bootkit/
Expand Down
137 changes: 137 additions & 0 deletions layers/custom/custom.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
config:
prerequisites:
- ../minbase/stacker.yaml

build-customize:
build_only: true
from:
type: built
tag: minbase
run: |
pkgtool install binutils cpio efitools pigz python3 python3-pip
pip install virt-firmware
custom-bootkit-input:
build_only: true
from:
type: docker
url: ${{CUSTOM_BOOTKIT_INPUT:oci:${{STACKER_OCI_DIR}}:bootkit}}

## bootkit artifacts have no key data in them and are not signed.
## customized uses bootkit artifacts and key access to create
## signed artifacts that have databases populated.
## It produces:
## ovmf-code.fd (unmodified, ovmf-code and ovmf-vars are always kept as a pair)
## ovmf-vars.fd with uefi-pk, uefi-kek and uefi-db populated.
## shim.efi with uki-limited, uki-production, uki-tpm populated.
## kernel.efi with manifest-ca
customized:
build_only: true
from:
type: built
tag: build-customize
import:
- path: ../../tools/custbk
- path: ../../pkg/bkcust
- path: ${{KEYSET_D}}/
dest: /import/keys/
- path: stacker://custom-bootkit-input/bootkit
run: |
d=$(mktemp -d)
trap "rm -Rf $d" EXIT
cp /stacker/custbk /usr/local/bin/custbk
chmod 755 /usr/local/bin/custbk
workdir=$d/workd
bkdir="/stacker/bootkit"
outdir="$d/customized"
[ -d "$bkdir" ] ||
{ echo "$bkdir is not a directory"; exit 1; }
find $bkdir ! -type d | xargs ls -altr
keydir=$(echo /import/keys/*)
[ -d "$keydir" ] ||
{ echo "did not find keydir in /import/keys/*"; exit 1; }
mkdir "$outdir" "$workdir"
### ovmf
cp "$bkdir/ovmf/ovmf-code.fd" "$outdir/ovmf-code.fd"
/stacker/bkcust virtfw secure-boot \
--output=$outdir/ovmf-vars.fd \
"--platform=$keydir/uefi-pk" \
"--kek=$keydir/uefi-kek" \
"--db=$keydir/uefi-db" \
"$bkdir/ovmf/ovmf-vars.fd"
## ovmf custbk (shell)
# custbk virt-fw-vars \
# "--set-pk=keydir:$keydir/uefi-pk/" \
# "--add-kek=keydir:$keydir/uefi-kek/" \
# "--add-db=keydir:$keydir/uefi-db/" \
# "--input=$bkdir/ovmf/ovmf-vars.fd" \
# "--output=$outdir/ovmf-vars.fd" \
# "--secure-boot" \
# "--no-microsoft"
### shim
/stacker/bkcust shim set-db \
--output="$outdir/shim.efi" \
"$bkdir/shim/shim.efi" \
"$keydir/uki-limited" \
"$keydir/uki-production" \
"$keydir/uki-tpm"
/stacker/bkcust sign-efi "$outdir/shim.efi" \
"$keydir/uefi-db/cert.pem" "$keydir/uefi-db/privkey.pem"
## shim custbk (shell) commands.
# custbk build-esl "$workdir/db.esl" \
# "keydir:$keydir/uki-limited" \
# "keydir:$keydir/uki-production" \
# "keydir:$keydir/uki-tpm"
# custbk shim-set-db "$outdir/shim.efi" "$bkdir/shim/shim.efi" "$workdir/db.esl"
# sbsign "--cert=$keydir/uefi-db/cert.pem" \
# "--key=$keydir/uefi-db/privkey.pem" \
# "--output=$outdir/shim.efi" "$outdir/shim.efi"
### kernel
## TODO: implement bkcust (golang path)
## kernel custbk (shell) commands
ixdir="$workdir/initrd-extra"
mkdir "$ixdir"
cp "$keydir/manifest-ca/cert.pem" "$ixdir/manifestCA.pem"
cp -r "$keydir/pcr7data" "$ixdir/pcr7data"
custbk initrd-join \
"--microcode=$bkdir/initrd/firmware.cpio.gz" \
"$workdir/initrd.img" \
"$bkdir/initrd/core.cpio.gz" \
"$bkdir/kernel/initrd-modules.cpio.gz" \
"$bkdir/mos/initrd-mos.cpio.gz" \
"dir:$ixdir"
rm -Rf "$ixdir"
### uki / smoosh
/stacker/bkcust stubby smoosh --cmdline="" \
"$outdir/kernel.efi" "$bkdir/stubby/stubby.efi" \
"$bkdir/kernel/boot/vmlinuz" \
"$workdir/initrd.img"
/stacker/bkcust sign-efi "$outdir/kernel.efi" \
"$keydir/uki-production/cert.pem" "$keydir/uki-production/privkey.pem"
## uki custbk (shell) commands
# custbk smoosh \
# --cmdline="" \
# "$outdir/kernel.efi" \
# "$bkdir/stubby/stubby.efi" \
# "$bkdir/kernel/boot/vmlinuz" \
# "$workdir/initrd.img"
# sbsign \
# "--cert=$keydir/uki-production/cert.pem" \
# "--key=$keydir/uki-production/privkey.pem" \
# "--output=$outdir/kernel.efi" "$outdir/kernel.efi"
mkdir /export
tar -C "$outdir/.." -cf /export/customized.tar "${outdir##*/}"
31 changes: 19 additions & 12 deletions layers/kernel/stacker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,46 @@ config:
prerequisites:
- ../build-krd/stacker.yaml

kernel-build:
kernel-pkg:
build_only: true
from:
type: built
tag: build-krd
run: |
pkgtool install linux-image-virtual
kernel-build:
build_only: true
from:
type: built
tag: kernel-pkg
run: |
ver=$(cd /lib/modules && for d in */modules.dep; do echo ${d%/*}; done)
set -- $ver
[ $# -eq 0 ] && { echo "nothing found in /lib/modules"; exit 1; }
[ $# -gt 1 ] && { echo "found $# things in /lib/modules"; exit 1; }
d=$(mktemp -d)
mkdir -p "$d/boot/$ver" "$d/initrd" /export
mkdir -p "$d/kernel" "$d/kernel/boot" "$d/initrd" /export
# create firwmare.cpio.gz, core.cpio.gz and modules.cpio.gz in $d/initrd
build-initrd build -v -v --modules --core "$d/initrd"
chmod ugo+r "$d/initrd"/*
mv "$d/initrd/modules.cpio.gz" "$d/kernel/initrd-modules.cpio.gz"
echo "$ver" > "$d/initrd/version"
echo "$ver" > "/lib/modules/$ver/version"
echo "$ver" > "$d/boot/$ver/version"
echo "$ver" > "$d/version"
echo "$ver" > "$d/kernel/boot/version"
echo "$ver" > "$d/kernel/version"
for f in System.map config vmlinuz; do
cp /boot/$f-$ver $d/boot/$ver/$f
mv "/boot/$f-$ver" "$d/kernel/boot/$f"
done
tar -C "$d" -cf /export/boot.tar boot/$ver/
tar -C "$d" -cf /export/initrd.tar initrd/
( cd /lib/modules &&
mksquashfs $ver/ /export/modules.squashfs -xattrs -comp xz -keep-as-directory )
mksquashfs "$ver/" "$d/kernel/modules.squashfs" -xattrs -comp xz -keep-as-directory )
chmod -R u=rwX,go=rX "$d/initrd"/* "$d/kernel"/*
tar -C "$d" -cf /export/kernel.tar kernel/
tar -C "$d" -cf /export/initrd.tar initrd/
echo "$ver" > /export/version
rm -Rf "$d"
4 changes: 2 additions & 2 deletions layers/mos/stacker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ mos-build:
# twice in final initramfs cpio (once from core and once from here)
/usr/lib/dracut/dracut-install -D "$workd" --ldd --resolvelazy "$workd/usr/bin"/*
create-cpio "$workd" "$d/initrd/mos.cpio.gz"
create-cpio "$workd" "$d/mos/initrd-mos.cpio.gz"
mkdir /export
tar -C "$d" -cf /export/mos.tar initrd/
tar -C "$d" -cf /export/mos.tar mos/
Loading

0 comments on commit 14a2a0d

Please sign in to comment.