Merge ~jocave/plainbox-provider-checkbox:disk-encryption-test into plainbox-provider-checkbox:master

Proposed by Jonathan Cave
Status: Merged
Approved by: Jonathan Cave
Approved revision: 0bd6567a77d97e24874d658e0067790a9ff690e1
Merged at revision: b10d0339f864aa2ce2d1301569ae8302327fcc0a
Proposed branch: ~jocave/plainbox-provider-checkbox:disk-encryption-test
Merge into: plainbox-provider-checkbox:master
Diff against target: 135 lines (+122/-0)
2 files modified
bin/fde_tests.py (+101/-0)
units/disk/encryption.pxu (+21/-0)
Reviewer Review Type Date Requested Status
Sylvain Pineau (community) Approve
Maciej Kisielewski Approve
Review via email: mp+348206@code.launchpad.net

Description of the change

A test to check whether full disk encryption is used on the device. Tries to make minimal assumptions and work back from the critical mount point depending on whether running on an Ubuntu Core device or a traditional desktop install.

Tested by building in to checkbox-vienna for Ubuntu Core and checkbox-oem-qa for desktop.

Note the extra required utilities.

To post a comment you must log in.
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

Good stuff. +1
Couple of hints inline, landable after solving the unused 'argv'

review: Approve
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

Great work, I'm wondering if we really need the two extra verification steps but if we can do more...

+1

review: Approve
Revision history for this message
Jonathan Cave (jocave) wrote :

Applied hints from kissiel

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/bin/fde_tests.py b/bin/fde_tests.py
2new file mode 100755
3index 0000000..3008735
4--- /dev/null
5+++ b/bin/fde_tests.py
6@@ -0,0 +1,101 @@
7+#!/usr/bin/env python3
8+# Copyright 2018 Canonical Ltd.
9+# Written by:
10+# Jonathan Cave <jonathan.cave@canonical.com>
11+
12+"""Test that Full Disk Encryption is in use.
13+
14+$ fde_tests.py
15+Canonical has a reference implementation of full disk encryption for IoT
16+devices. With no arguments passed this test checks this implementation is in
17+operation on the device under test.
18+
19+$ fde_tests.py desktop
20+Checks if the system appears to be using full disk encryption as configured by
21+the desktop installer.
22+"""
23+
24+import os
25+import re
26+import subprocess as sp
27+import sys
28+
29+
30+def main():
31+ on_desktop = len(sys.argv) > 1 and sys.argv[1] == 'desktop'
32+
33+ # the mountpoint corresponding to the on disk encrypted partition
34+ base_mount = '/' if on_desktop else '/writable'
35+
36+ # discover the underlying mount point for the encrypted part
37+ cmd = 'findmnt {} -n -o SOURCE'.format(base_mount)
38+ print('+ {}'.format(cmd))
39+ try:
40+ source = sp.check_output(cmd, shell=True).decode(
41+ sys.stdout.encoding).strip()
42+ except sp.CalledProcessError:
43+ raise SystemExit(
44+ 'ERROR: could not find mountpoint for {}'.format(base_mount))
45+ print(source, '\n')
46+
47+ # resolve the source to an actual device node
48+ print('+ realpath {}'.format(source))
49+ device = os.path.realpath(source)
50+ print(device, '\n')
51+
52+ # work upwards through the tree of devices until we find the one that has
53+ # the type 'crypt'
54+ kname = os.path.basename(device)
55+ while True:
56+ cmd = 'lsblk -r -n -i -o KNAME,TYPE,PKNAME | grep "^{}"'.format(kname)
57+ print('+ {}'.format(cmd))
58+ try:
59+ lsblk = sp.check_output(cmd, shell=True).decode(
60+ sys.stdout.encoding).strip()
61+ except sp.CalledProcessError:
62+ raise SystemExit('ERROR: lsblk call failed')
63+ _, devtype, parent = lsblk.split(maxsplit=2)
64+ print(devtype, '\n')
65+ if devtype == 'crypt':
66+ # found the device
67+ break
68+ if devtype == 'disk':
69+ # reached the physical device, end the search
70+ raise SystemExit(
71+ 'ERROR: could not find a block device of type "crypt"')
72+ kname = parent
73+
74+ # the presence of device with type 'crypt' is probably confirmation enough
75+ # but to be really sure lets check to see it is found by cryptsetup
76+
77+ # first we need to know its mapper name
78+ cmd = 'dmsetup info /dev/{} | grep "^Name:"'.format(kname)
79+ print('+ {}'.format(cmd))
80+ try:
81+ mapper_name = sp.check_output(cmd, shell=True).decode(
82+ sys.stdout.encoding).strip().split()[-1]
83+ except sp.CalledProcessError:
84+ raise SystemExit(
85+ 'ERROR: dmsetup info on device {} failed'.format(kname))
86+ print(mapper_name, '\n')
87+
88+ # then query the info in cryptsetup
89+ cmd = 'cryptsetup status {}'.format(mapper_name)
90+ print('+ {}'.format(cmd))
91+ try:
92+ cryptinfo = sp.check_output(cmd, shell=True).decode(
93+ sys.stdout.encoding).strip()
94+ except sp.CalledProcessError:
95+ raise SystemExit('ERROR: dmsetup failed')
96+ print(cryptinfo, '\n')
97+
98+ # use the type as the final arbiter of success
99+ regexp = re.compile(r'type:\ *LUKS1')
100+ if regexp.search(cryptinfo):
101+ print('Full Disk Encryption is operational on this device')
102+ else:
103+ raise SystemExit('ERROR: cryptsetup did not report LUKS1 in use')
104+
105+
106+if __name__ == "__main__":
107+ main()
108diff --git a/units/disk/encryption.pxu b/units/disk/encryption.pxu
109new file mode 100644
110index 0000000..b7d41c8
111--- /dev/null
112+++ b/units/disk/encryption.pxu
113@@ -0,0 +1,21 @@
114+
115+id: disk/encryption/detect
116+category_id: com.canonical.plainbox::disk
117+plugin: shell
118+template-engine: jinja2
119+user: root
120+requires:
121+ executable.name == 'lsblk'
122+ executable.name == 'dmsetup'
123+ executable.name == 'cryptsetup'
124+_summary: Test that Full Disk Encryption is in use
125+_description:
126+ Examine the system to detect if one of the standard full disk encryption
127+ implementations is in use
128+command:
129+ {%- if __on_ubuntucore__ %}
130+ fde_tests.py
131+ {%- else %}
132+ fde_tests.py desktop
133+ {% endif -%}
134+estimated_duration: 2.0
135\ No newline at end of file

Subscribers

People subscribed via source and target branches