diff options
author | PMR <pmr@pmr-lander> | 2018-06-28 16:35:41 +0000 |
---|---|---|
committer | PMR <pmr@pmr-lander> | 2018-06-28 16:35:41 +0000 |
commit | 28789c9f43d7da630e56d06abc33f4d57e7de16d (patch) | |
tree | 6bce14f4253a91630646be5516bf03488e88fa05 | |
parent | 6392745efbe2b708fe7a9fdbedb530f321841894 (diff) | |
parent | 05a97f6894458eb6dfa86e6146976eee421f1581 (diff) |
Merge #348581 from ~jocave/plainbox-provider-checkbox:secure-boot-check-snappy
-rwxr-xr-x | bin/boot_mode_test_snappy.py | 125 | ||||
-rw-r--r-- | units/miscellanea/jobs.pxu | 16 |
2 files changed, 141 insertions, 0 deletions
diff --git a/bin/boot_mode_test_snappy.py b/bin/boot_mode_test_snappy.py new file mode 100755 index 00000000..46bed925 --- /dev/null +++ b/bin/boot_mode_test_snappy.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# Copyright 2018 Canonical Ltd. +# Written by: +# Jonathan Cave <jonathan.cave@canonical.com> + +import io +import os +import re +import sys +import subprocess as sp + +import yaml + + +def fitdumpimage(filename): + cmd = 'dumpimage -l {}'.format(filename) + out = sp.check_output(cmd, shell=True).decode(sys.stdout.encoding) + buf = io.StringIO(out) + + # first line should identify FIT file + if not buf.readline().startswith('FIT description'): + raise SystemExit('ERROR: expected FIT image description') + + # second line contains some metadata, skip it + buf.readline() + + # from then on should get blocks of text describing the objects that were + # combined in to the FIT image e.g. kernel, ramdisk, device tree + image_re = re.compile(r'(?:^\ Image)\ \d+\ \((\S+)\)$') + config_re = re.compile(r'^\ Default Configuration|^\ Configuration') + objects = {} + name = '' + while True: + line = buf.readline() + # stop at end + if line == '': + break + # interested in storing image information + match = image_re.search(line) + if match: + name = match.group(1) + objects[name] = {} + continue + # not interested in configurations + if config_re.search(line): + name = '' + continue + # while in an image section store the info + if name != '': + entries = [s.strip() for s in line.split(':', 1)] + objects[name][entries[0]] = entries[1] + return objects + + +def main(): + if len(sys.argv) != 3: + raise SystemExit('ERROR: please supply gadget & kernel name') + gadget = sys.argv[1] + kernel = sys.argv[2] + + gadget_yaml = os.path.join('/snap', gadget, 'current/meta/gadget.yaml') + + if not os.path.exists(gadget_yaml): + raise SystemExit( + 'ERROR: failed to find gadget.yaml at {}'.format(gadget_yaml)) + + with open(gadget_yaml) as f: + data = yaml.load(f) + for k in data['volumes'].keys(): + bootloader = data['volumes'][k]['bootloader'] + if not bootloader: + raise SystemExit('ERROR: could not find name of bootloader') + + if bootloader not in ('u-boot', 'grub'): + raise SystemExit( + 'ERROR: Unexpected bootloader name {}'.format(bootloader)) + print('Bootloader is {}\n'.format(bootloader)) + + if bootloader == 'u-boot': + print('Parsing FIT image information...\n') + + kernel_rev = os.path.basename( + os.path.realpath('/snap/{}/current'.format(kernel))) + boot_kernel = '/boot/uboot/{}_{}.snap/kernel.img'.format( + kernel, kernel_rev) + boot_objects = fitdumpimage(boot_kernel) + + for obj, attrs in boot_objects.items(): + print('Checking object {}'.format(obj)) + if 'Sign value' not in attrs: + raise SystemExit('ERROR: no sign value found for object') + print('Found "Sign value"') + if len(attrs['Sign value']) != 512: + raise SystemExit('ERROR: unexpected sign value size') + if all(s in attrs['Sign algo'] for s in ['sha256', 'rsa2048']): + print('Found expected signing algorithms') + else: + raise SystemExit( + 'ERROR: unexpected signing algorithms {}'.format( + attrs['Sign algo'])) + print() + + # check that all parts of the fit image have + snap_kernel = '/snap/{}/current/kernel.img'.format(kernel) + snap_objects = fitdumpimage(snap_kernel) + if snap_objects != boot_objects: + raise SystemExit( + 'ERROR: boot kernel and current snap kernel do not match') + print('Kernel images in current snap and u-boot match\n') + + print('Secure Boot appears to be enabled on this system') + + if bootloader == 'grub': + cmd = 'mokutil --sb-state' + print('+', cmd, flush=True) + out = sp.check_output(cmd, shell=True).decode(sys.stdout.encoding) + print(out, flush=True) + if out != 'SecureBoot enabled\n': + raise SystemExit('ERROR: mokutil reports Secure Boot not in use') + + print('Secure Boot appears to be enabled on this system') + + +if __name__ == '__main__': + main() diff --git a/units/miscellanea/jobs.pxu b/units/miscellanea/jobs.pxu index 4a9980ba..a1d4f5a1 100644 --- a/units/miscellanea/jobs.pxu +++ b/units/miscellanea/jobs.pxu @@ -148,6 +148,22 @@ command: boot_mode_test secureboot plugin: shell category_id: com.canonical.plainbox::miscellanea estimated_duration: 0.5 +unit: template +template-resource: model_assertion +template-unit: job +requires: + executable.name == 'dumpimage' + executable.name == 'mokutil' +id: miscellanea/secure_boot_mode_{gadget} +_summary: Test that {gadget} Ubuntu Core system booted with Secure Boot active +_description: + Test to verify that the system booted with Secure Boot active. +command: + boot_mode_test_snappy.py {gadget} {kernel} + +plugin: shell +category_id: com.canonical.plainbox::miscellanea +estimated_duration: 0.5 user: root id: miscellanea/efi_pxeboot requires: |