From 56aa7e5a170c194fae1745344557499019d141b1 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 29 Mar 2016 15:34:20 -0700 Subject: [PATCH] Add support for generating PCR configuration at image build time We need to ship some PCR measurements alongside images in order to make it easier for admins to provide an appropriate policy. Add some tooling to generate the appropriate hashes during build, pack those into a zip file and upload it. --- build_library/build_image_util.sh | 8 ++++ build_library/dev_image_util.sh | 4 +- build_library/generate_grub_hashes.py | 57 +++++++++++++++++++++++++++ build_library/generate_kernel_hash.sh | 13 ++++++ build_library/prod_image_util.sh | 4 +- 5 files changed, 84 insertions(+), 2 deletions(-) create mode 100755 build_library/generate_grub_hashes.py create mode 100755 build_library/generate_kernel_hash.sh diff --git a/build_library/build_image_util.sh b/build_library/build_image_util.sh index 6f827cc4e6..5c2cfb44d6 100755 --- a/build_library/build_image_util.sh +++ b/build_library/build_image_util.sh @@ -285,6 +285,9 @@ finish_image() { local install_grub=0 local disk_img="${BUILD_DIR}/${image_name}" + local pcr_policy="${image_name%.bin}_pcr_policy.zip" + local pcr_dir="${BUILD_DIR}/pcrs" + mkdir -p "${pcr_dir}" # Copy kernel to support dm-verity boots sudo mkdir -p "${root_fs_dir}/boot/coreos" @@ -361,6 +364,7 @@ finish_image() { "${root_fs_dir}/boot/coreos/vmlinuz-a" fi + ${BUILD_LIBRARY_DIR}/generate_kernel_hash.sh "${root_fs_dir}/boot/coreos/vmlinuz-a" ${COREOS_VERSION} >${pcr_dir}/kernel.config rm -rf "${BUILD_DIR}"/configroot cleanup_mounts "${root_fs_dir}" trap - EXIT @@ -381,5 +385,9 @@ finish_image() { --target="${target}" --disk_image="${disk_img}" --noverity fi done + ${BUILD_LIBRARY_DIR}/generate_grub_hashes.py ${disk_img} /usr/lib/grub/ ${pcr_dir} ${COREOS_VERSION} fi + pushd ${BUILD_DIR} + zip -r -9 $pcr_policy pcrs + popd } diff --git a/build_library/dev_image_util.sh b/build_library/dev_image_util.sh index 38a28edfb4..e15f56dbe1 100755 --- a/build_library/dev_image_util.sh +++ b/build_library/dev_image_util.sh @@ -91,6 +91,7 @@ create_dev_image() { local image_contents="${image_name%.bin}_contents.txt" local image_packages="${image_name%.bin}_packages.txt" local image_licenses="${image_name%.bin}_licenses.txt" + local image_pcr_policy="${image_name%.bin}_pcr_policy.zip" start_image "${image_name}" "${disk_layout}" "${root_fs_dir}" "${update_group}" @@ -125,5 +126,6 @@ EOF upload_image -d "${BUILD_DIR}/${image_name}.bz2.DIGESTS" \ "${BUILD_DIR}/${image_contents}" \ "${BUILD_DIR}/${image_packages}" \ - "${BUILD_DIR}/${image_name}" + "${BUILD_DIR}/${image_name}" \ + "${BUILD_DIR}/${image_pcr_policy}" } diff --git a/build_library/generate_grub_hashes.py b/build_library/generate_grub_hashes.py new file mode 100755 index 0000000000..8bc82a3dfe --- /dev/null +++ b/build_library/generate_grub_hashes.py @@ -0,0 +1,57 @@ +#!/usr/bin/python + +import hashlib +import json +import os +import string +import subprocess +import sys + +filename = sys.argv[1] +grubdir = sys.argv[2] +outputdir = sys.argv[3] +version = sys.argv[4] +bootoffset = string.atoi(subprocess.check_output(['cgpt', 'show', '-i', '2', '-b', filename])) * 512 +with open(filename, "rb") as f: + boot = f.read(440) + f.seek(bootoffset) + diskboot = f.read(512) + corelen = bytearray(diskboot)[508] | bytearray(diskboot)[509] << 8 + f.seek(bootoffset+512) + core = f.read(corelen * 512) + hashes = {"4": {"binaryvalues": [{"values": [{"value": hashlib.sha1(boot).hexdigest(), "description": "CoreOS Grub boot.img %s" % version}]}]}, + "8": {"binaryvalues" : [{"values": [{"value": hashlib.sha1(diskboot).hexdigest(), "description": "CoreOS Grub diskboot.img %s" % version}]}]}, + "9": {"binaryvalues": [{"values": [{"value": hashlib.sha1(core).hexdigest(), "description": "CoreOS Grub core.img %s" % version}]}]}} + with open(os.path.join(outputdir, "grub_loader.config"), "w") as f: + f.write(json.dumps(hashes, sort_keys=True)) + + +hashvalues = [] +for folder, subs, files in os.walk(grubdir): + for filename in files: + if filename.endswith(".mod"): + with open(os.path.join(folder, filename), "rb") as f: + mod = f.read() + value = hashlib.sha1(mod).hexdigest() + description = "CoreOS Grub %s %s" % (filename, version) + hashvalues.append({"value": value, "description": description}) + +with open(os.path.join(outputdir, "grub_modules.config"), "w") as f: + f.write(json.dumps({"9": {"binaryvalues": [{"prefix": "grub_module", "values": hashvalues}]}})) + +with open(os.path.join(outputdir, "kernel_cmdline.config"), "w") as f: + f.write(json.dumps({"8": {"asciivalues": [{"prefix": "grub_kernel_cmdline", "values": [{"value": "rootflags=rw mount.usrflags=ro BOOT_IMAGE=/coreos/vmlinuz-[ab] mount.usr=PARTUUID=\S{36} rootflags=rw mount.usrflags=ro consoleblank=0 root=LABEL=ROOT (console=\S+)? (coreos.autologin=\S+)? verity.usrhash=\\S{64}", "description": "CoreOS kernel command line %s" % version}]}]}})) + +commands = [{"value": '\[.*\]', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'gptprio.next -d usr -u usr_uuid', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'insmod all_video', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'linux /coreos/vmlinuz-[ab] rootflags=rw mount.usrflags=ro consoleblank=0 root=LABEL=ROOT (console=\S+)? (coreos.autologin=\S+)?', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'menuentry CoreOS \S+ --id=coreos\S* {', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'search --no-floppy --set first_boot --disk-uuid 00000000-0000-0000-0000-000000000001', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'search --no-floppy --set oem --part-label OEM --hint hd0,gpt1', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'set .+', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'setparams CoreOS default', "description": "CoreOS Grub configuration %s" % version}, + {"value": 'source (hd0,gpt6)/grub.cfg', "description": "CoreOS Grub configuration %s" % version}] + +with open(os.path.join(outputdir, "grub_commands.config"), "w") as f: + f.write(json.dumps({"8": {"asciivalues": [{"prefix": "grub_cmd", "values": commands}]}})) diff --git a/build_library/generate_kernel_hash.sh b/build_library/generate_kernel_hash.sh new file mode 100755 index 0000000000..e9a258180b --- /dev/null +++ b/build_library/generate_kernel_hash.sh @@ -0,0 +1,13 @@ +#!/usr/bin/python + +import hashlib +import json +import os +import sys + +path=sys.argv[1] +version=sys.argv[2] + +with open(path, "rb") as f: + kernel = f.read() + print json.dumps({"9": {"binaryvalues": [{"prefix": "grub_linux", "values": [{"value": hashlib.sha1(kernel).hexdigest(), "description": "coreos-%s" % version}]}]}}) diff --git a/build_library/prod_image_util.sh b/build_library/prod_image_util.sh index 6bf1755c8d..17bccd2b84 100755 --- a/build_library/prod_image_util.sh +++ b/build_library/prod_image_util.sh @@ -66,6 +66,7 @@ create_prod_image() { local image_contents="${image_name%.bin}_contents.txt" local image_packages="${image_name%.bin}_packages.txt" local image_licenses="${image_name%.bin}_licenses.txt" + local image_pcr_policy="${image_name%.bin}_pcr_policy.zip" start_image "${image_name}" "${disk_layout}" "${root_fs_dir}" "${update_group}" @@ -111,5 +112,6 @@ EOF upload_image -d "${BUILD_DIR}/${image_name}.bz2.DIGESTS" \ "${BUILD_DIR}/${image_contents}" \ "${BUILD_DIR}/${image_packages}" \ - "${BUILD_DIR}/${image_name}" + "${BUILD_DIR}/${image_name}" \ + "${BUILD_DIR}/${image_pcr_policy}" }