Merge ~raharper/curtin:feature/s390x-zkey into curtin:master
- Git
- lp:~raharper/curtin
- feature/s390x-zkey
- Merge into master
Status: | Merged |
---|---|
Approved by: | Ryan Harper |
Approved revision: | 55a46eb11ff0bea6835390d14e867cd7b58cad82 |
Merge reported by: | Server Team CI bot |
Merged at revision: | not available |
Proposed branch: | ~raharper/curtin:feature/s390x-zkey |
Merge into: | curtin:master |
Diff against target: |
476 lines (+349/-17) 6 files modified
curtin/block/__init__.py (+50/-6) curtin/commands/block_meta.py (+39/-9) curtin/commands/curthooks.py (+29/-0) tests/unittests/test_block.py (+33/-0) tests/unittests/test_commands_block_meta.py (+174/-0) tests/unittests/test_curthooks.py (+24/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ryan Harper (community) | Approve | ||
Server Team CI bot | continuous-integration | Approve | |
Dimitri John Ledkov (community) | ship it | Approve | |
Chad Smith | Approve | ||
Review via email: mp+368802@code.launchpad.net |
Commit message
block: Add opportunistic zkey encryption if supported
On s390x, systems with a crypto accelerator may be present
and enabled for sure. When handling a type: dm_crypt block
configuration, curtin will test if zkey is available and if
so, use the zkey command to generate keys and encrypt the
block device using zkey-based secrets.
In the case that zkey is not available, curtin will fallback
to using normal cryptsetup.
Description of the change
Server Team CI bot (server-team-bot) wrote : | # |
Chad Smith (chad.smith) : | # |
Ryan Harper (raharper) wrote : | # |
Thanks for the review, I'll update with changes.
- 534f51b... by Ryan Harper
-
zkey: add strict boolean to toggle log level in error paths.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:ff03de861d8
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Chad Smith (chad.smith) wrote : | # |
LGTM!thanks for the clarification and fixups.
Dimitri John Ledkov (xnox) wrote : | # |
Only minor questions, and a typo to fix.
Ryan Harper (raharper) wrote : | # |
Thanks for the review, I'll fix it up.
- 7b4fd22... by Ryan Harper
-
fix typo in copy_zkey_
repository log message. - 5d5667b... by Ryan Harper
-
Only install s390-tools-zkey if zkey_used
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:4d6c38d5239
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Dimitri John Ledkov (xnox) wrote : | # |
hahahhahahahahhaha
worse typo ever
hahahhahahahahhaha
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:5d5667b3950
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Ryan Harper (raharper) wrote : | # |
vmtest detects the modprobe failure and fails the install even though it went fine.
I need to refactor the logged message when strict=False so that it doesn't trip up
the failure parsing.
- 55a46eb... by Ryan Harper
-
Reword logged error when strict=False to not trip up vmtest error detection.
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:55a46eb11ff
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Ryan Harper (raharper) wrote : | # |
https:/
-------
Ran 112 tests in 2362.318s
OK
Fri, 21 Jun 2019 20:38:24 +0000: vmtest end [0] in 2365s
Looks good.
Preview Diff
1 | diff --git a/curtin/block/__init__.py b/curtin/block/__init__.py | |||
2 | index 5d1b1bd..f30c5df 100644 | |||
3 | --- a/curtin/block/__init__.py | |||
4 | +++ b/curtin/block/__init__.py | |||
5 | @@ -668,6 +668,24 @@ def get_proc_mounts(): | |||
6 | 668 | return mounts | 668 | return mounts |
7 | 669 | 669 | ||
8 | 670 | 670 | ||
9 | 671 | def _get_dev_disk_by_prefix(prefix): | ||
10 | 672 | """ | ||
11 | 673 | Construct a dictionary mapping devname to disk/<prefix> paths | ||
12 | 674 | |||
13 | 675 | :returns: Dictionary populated by examining /dev/disk/<prefix>/* | ||
14 | 676 | |||
15 | 677 | { | ||
16 | 678 | '/dev/sda': '/dev/disk/<prefix>/virtio-aaaa', | ||
17 | 679 | '/dev/sda1': '/dev/disk/<prefix>/virtio-aaaa-part1', | ||
18 | 680 | } | ||
19 | 681 | """ | ||
20 | 682 | return { | ||
21 | 683 | os.path.realpath(bypfx): bypfx | ||
22 | 684 | for bypfx in [os.path.join(prefix, path) | ||
23 | 685 | for path in os.listdir(prefix)] | ||
24 | 686 | } | ||
25 | 687 | |||
26 | 688 | |||
27 | 671 | def get_dev_disk_byid(): | 689 | def get_dev_disk_byid(): |
28 | 672 | """ | 690 | """ |
29 | 673 | Construct a dictionary mapping devname to disk/by-id paths | 691 | Construct a dictionary mapping devname to disk/by-id paths |
30 | @@ -679,12 +697,7 @@ def get_dev_disk_byid(): | |||
31 | 679 | '/dev/sda1': '/dev/disk/by-id/virtio-aaaa-part1', | 697 | '/dev/sda1': '/dev/disk/by-id/virtio-aaaa-part1', |
32 | 680 | } | 698 | } |
33 | 681 | """ | 699 | """ |
40 | 682 | 700 | return _get_dev_disk_by_prefix('/dev/disk/by-id') | |
35 | 683 | prefix = '/dev/disk/by-id' | ||
36 | 684 | return { | ||
37 | 685 | os.path.realpath(byid): byid | ||
38 | 686 | for byid in [os.path.join(prefix, path) for path in os.listdir(prefix)] | ||
39 | 687 | } | ||
41 | 688 | 701 | ||
42 | 689 | 702 | ||
43 | 690 | def disk_to_byid_path(kname): | 703 | def disk_to_byid_path(kname): |
44 | @@ -696,6 +709,15 @@ def disk_to_byid_path(kname): | |||
45 | 696 | return mapping.get(dev_path(kname)) | 709 | return mapping.get(dev_path(kname)) |
46 | 697 | 710 | ||
47 | 698 | 711 | ||
48 | 712 | def disk_to_bypath_path(kname): | ||
49 | 713 | """" | ||
50 | 714 | Return a /dev/disk/by-path path to kname if present. | ||
51 | 715 | """ | ||
52 | 716 | |||
53 | 717 | mapping = _get_dev_disk_by_prefix('/dev/disk/by-path') | ||
54 | 718 | return mapping.get(dev_path(kname)) | ||
55 | 719 | |||
56 | 720 | |||
57 | 699 | def get_device_mapper_links(devpath, first=False): | 721 | def get_device_mapper_links(devpath, first=False): |
58 | 700 | """ Return the best devlink to device at devpath. """ | 722 | """ Return the best devlink to device at devpath. """ |
59 | 701 | info = udevadm_info(devpath) | 723 | info = udevadm_info(devpath) |
60 | @@ -892,6 +914,28 @@ def is_online(device): | |||
61 | 892 | return int(device_size) > 0 | 914 | return int(device_size) > 0 |
62 | 893 | 915 | ||
63 | 894 | 916 | ||
64 | 917 | def zkey_supported(strict=True): | ||
65 | 918 | """ Return True if zkey cmd present and can generate keys, else False.""" | ||
66 | 919 | LOG.debug('Checking if zkey encryption is supported...') | ||
67 | 920 | try: | ||
68 | 921 | util.load_kernel_module('pkey') | ||
69 | 922 | except util.ProcessExecutionError as err: | ||
70 | 923 | msg = "Failed to load 'pkey' kernel module" | ||
71 | 924 | LOG.error(msg + ": %s" % err) if strict else LOG.warning(msg) | ||
72 | 925 | return False | ||
73 | 926 | |||
74 | 927 | try: | ||
75 | 928 | with tempfile.NamedTemporaryFile() as tf: | ||
76 | 929 | util.subp(['zkey', 'generate', tf.name], capture=True) | ||
77 | 930 | LOG.debug('zkey encryption supported.') | ||
78 | 931 | return True | ||
79 | 932 | except util.ProcessExecutionError as err: | ||
80 | 933 | msg = "zkey not supported" | ||
81 | 934 | LOG.error(msg + ": %s" % err) if strict else LOG.warning(msg) | ||
82 | 935 | |||
83 | 936 | return False | ||
84 | 937 | |||
85 | 938 | |||
86 | 895 | @contextmanager | 939 | @contextmanager |
87 | 896 | def exclusive_open(path, exclusive=True): | 940 | def exclusive_open(path, exclusive=True): |
88 | 897 | """ | 941 | """ |
89 | diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py | |||
90 | index 79493fc..a9110a9 100644 | |||
91 | --- a/curtin/commands/block_meta.py | |||
92 | +++ b/curtin/commands/block_meta.py | |||
93 | @@ -1062,6 +1062,7 @@ def dm_crypt_handler(info, storage_config): | |||
94 | 1062 | dm_name = info.get('id') | 1062 | dm_name = info.get('id') |
95 | 1063 | 1063 | ||
96 | 1064 | volume_path = get_path_to_storage_volume(volume, storage_config) | 1064 | volume_path = get_path_to_storage_volume(volume, storage_config) |
97 | 1065 | volume_byid_path = block.disk_to_byid_path(volume_path) | ||
98 | 1065 | 1066 | ||
99 | 1066 | if 'keyfile' in info: | 1067 | if 'keyfile' in info: |
100 | 1067 | if 'key' in info: | 1068 | if 'key' in info: |
101 | @@ -1077,16 +1078,45 @@ def dm_crypt_handler(info, storage_config): | |||
102 | 1077 | else: | 1078 | else: |
103 | 1078 | raise ValueError("encryption key or keyfile must be specified") | 1079 | raise ValueError("encryption key or keyfile must be specified") |
104 | 1079 | 1080 | ||
113 | 1080 | cmd = ["cryptsetup"] | 1081 | # if zkey is available, attempt to generate and use it; if it's not |
114 | 1081 | if cipher: | 1082 | # available or fails to setup properly, fallback to normal cryptsetup |
115 | 1082 | cmd.extend(["--cipher", cipher]) | 1083 | # passing strict=False downgrades log messages to warnings |
116 | 1083 | if keysize: | 1084 | zkey_used = None |
117 | 1084 | cmd.extend(["--key-size", keysize]) | 1085 | if block.zkey_supported(strict=False): |
118 | 1085 | cmd.extend(["luksFormat", volume_path, keyfile]) | 1086 | volume_name = "%s:%s" % (volume_byid_path, dm_name) |
119 | 1086 | 1087 | LOG.debug('Attempting to setup zkey for %s', volume_name) | |
120 | 1087 | util.subp(cmd) | 1088 | luks_type = 'luks2' |
121 | 1089 | gen_cmd = ['zkey', 'generate', '--xts', '--volume-type', luks_type, | ||
122 | 1090 | '--sector-size', '4096', '--name', dm_name, | ||
123 | 1091 | '--description', | ||
124 | 1092 | "curtin generated zkey for %s" % volume_name, | ||
125 | 1093 | '--volumes', volume_name] | ||
126 | 1094 | run_cmd = ['zkey', 'cryptsetup', '--run', '--volumes', | ||
127 | 1095 | volume_byid_path, '--batch-mode', '--key-file', keyfile] | ||
128 | 1096 | try: | ||
129 | 1097 | util.subp(gen_cmd, capture=True) | ||
130 | 1098 | util.subp(run_cmd, capture=True) | ||
131 | 1099 | zkey_used = os.path.join(os.path.split(state['fstab'])[0], | ||
132 | 1100 | "zkey_used") | ||
133 | 1101 | # mark in state that we used zkey | ||
134 | 1102 | util.write_file(zkey_used, "1") | ||
135 | 1103 | except util.ProcessExecutionError as e: | ||
136 | 1104 | LOG.exception(e) | ||
137 | 1105 | msg = 'Setup of zkey on %s failed, fallback to cryptsetup.' | ||
138 | 1106 | LOG.error(msg % volume_path) | ||
139 | 1107 | |||
140 | 1108 | if not zkey_used: | ||
141 | 1109 | LOG.debug('Using cryptsetup on %s', volume_path) | ||
142 | 1110 | luks_type = "luks" | ||
143 | 1111 | cmd = ["cryptsetup"] | ||
144 | 1112 | if cipher: | ||
145 | 1113 | cmd.extend(["--cipher", cipher]) | ||
146 | 1114 | if keysize: | ||
147 | 1115 | cmd.extend(["--key-size", keysize]) | ||
148 | 1116 | cmd.extend(["luksFormat", volume_path, keyfile]) | ||
149 | 1117 | util.subp(cmd) | ||
150 | 1088 | 1118 | ||
152 | 1089 | cmd = ["cryptsetup", "open", "--type", "luks", volume_path, dm_name, | 1119 | cmd = ["cryptsetup", "open", "--type", luks_type, volume_path, dm_name, |
153 | 1090 | "--key-file", keyfile] | 1120 | "--key-file", keyfile] |
154 | 1091 | 1121 | ||
155 | 1092 | util.subp(cmd) | 1122 | util.subp(cmd) |
156 | diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py | |||
157 | index 75f5083..2869c6c 100644 | |||
158 | --- a/curtin/commands/curthooks.py | |||
159 | +++ b/curtin/commands/curthooks.py | |||
160 | @@ -594,6 +594,28 @@ def copy_zpool_cache(zpool_cache, target): | |||
161 | 594 | shutil.copy(zpool_cache, os.path.sep.join([target, 'etc/zfs'])) | 594 | shutil.copy(zpool_cache, os.path.sep.join([target, 'etc/zfs'])) |
162 | 595 | 595 | ||
163 | 596 | 596 | ||
164 | 597 | def copy_zkey_repository(zkey_repository, target, | ||
165 | 598 | target_repo='etc/zkey/repository'): | ||
166 | 599 | if not zkey_repository: | ||
167 | 600 | LOG.warn("zkey repository path must be specified, not copying") | ||
168 | 601 | return | ||
169 | 602 | |||
170 | 603 | tdir = os.path.sep.join([target, target_repo]) | ||
171 | 604 | if not os.path.exists(tdir): | ||
172 | 605 | util.ensure_dir(tdir) | ||
173 | 606 | |||
174 | 607 | files_copied = [] | ||
175 | 608 | for src in os.listdir(zkey_repository): | ||
176 | 609 | source_path = os.path.join(zkey_repository, src) | ||
177 | 610 | target_path = os.path.join(tdir, src) | ||
178 | 611 | if not os.path.exists(target_path): | ||
179 | 612 | shutil.copy2(source_path, target_path) | ||
180 | 613 | files_copied.append(target_path) | ||
181 | 614 | |||
182 | 615 | LOG.debug('Imported zkey repo %s with files: %s', | ||
183 | 616 | zkey_repository, files_copied) | ||
184 | 617 | |||
185 | 618 | |||
186 | 597 | def apply_networking(target, state): | 619 | def apply_networking(target, state): |
187 | 598 | netconf = state.get('network_config') | 620 | netconf = state.get('network_config') |
188 | 599 | interfaces = state.get('interfaces') | 621 | interfaces = state.get('interfaces') |
189 | @@ -1379,6 +1401,13 @@ def builtin_curthooks(cfg, target, state): | |||
190 | 1379 | if os.path.exists(zpool_cache): | 1401 | if os.path.exists(zpool_cache): |
191 | 1380 | copy_zpool_cache(zpool_cache, target) | 1402 | copy_zpool_cache(zpool_cache, target) |
192 | 1381 | 1403 | ||
193 | 1404 | zkey_repository = '/etc/zkey/repository' | ||
194 | 1405 | zkey_used = os.path.join(os.path.split(state['fstab'])[0], "zkey_used") | ||
195 | 1406 | if all(map(os.path.exists, [zkey_repository, zkey_used])): | ||
196 | 1407 | distro.install_packages(['s390-tools-zkey'], target=target, | ||
197 | 1408 | osfamily=osfamily) | ||
198 | 1409 | copy_zkey_repository(zkey_repository, target) | ||
199 | 1410 | |||
200 | 1382 | # If a crypttab file was created by block_meta than it needs to be | 1411 | # If a crypttab file was created by block_meta than it needs to be |
201 | 1383 | # copied onto the target system, and update_initramfs() needs to be | 1412 | # copied onto the target system, and update_initramfs() needs to be |
202 | 1384 | # run, so that the cryptsetup hooks are properly configured on the | 1413 | # run, so that the cryptsetup hooks are properly configured on the |
203 | diff --git a/tests/unittests/test_block.py b/tests/unittests/test_block.py | |||
204 | index c7ebdcc..167a697 100644 | |||
205 | --- a/tests/unittests/test_block.py | |||
206 | +++ b/tests/unittests/test_block.py | |||
207 | @@ -712,4 +712,37 @@ class TestGetSupportedFilesystems(CiTestCase): | |||
208 | 712 | self.assertEqual(0, mock_util.load_file.call_count) | 712 | self.assertEqual(0, mock_util.load_file.call_count) |
209 | 713 | 713 | ||
210 | 714 | 714 | ||
211 | 715 | class TestZkeySupported(CiTestCase): | ||
212 | 716 | |||
213 | 717 | @mock.patch('curtin.block.util') | ||
214 | 718 | def test_zkey_supported_loads_module(self, m_util): | ||
215 | 719 | block.zkey_supported() | ||
216 | 720 | m_util.load_kernel_module.assert_called_with('pkey') | ||
217 | 721 | |||
218 | 722 | @mock.patch('curtin.block.util.load_kernel_module') | ||
219 | 723 | def test_zkey_supported_returns_false_missing_kmod(self, m_kmod): | ||
220 | 724 | m_kmod.side_effect = ( | ||
221 | 725 | util.ProcessExecutionError(stdout=self.random_string(), | ||
222 | 726 | stderr=self.random_string(), | ||
223 | 727 | exit_code=2)) | ||
224 | 728 | self.assertFalse(block.zkey_supported()) | ||
225 | 729 | |||
226 | 730 | @mock.patch('curtin.block.util.subp') | ||
227 | 731 | @mock.patch('curtin.block.util.load_kernel_module') | ||
228 | 732 | def test_zkey_supported_returns_false_zkey_error(self, m_kmod, m_subp): | ||
229 | 733 | m_subp.side_effect = ( | ||
230 | 734 | util.ProcessExecutionError(stdout=self.random_string(), | ||
231 | 735 | stderr=self.random_string(), | ||
232 | 736 | exit_code=2)) | ||
233 | 737 | self.assertFalse(block.zkey_supported()) | ||
234 | 738 | |||
235 | 739 | @mock.patch('curtin.block.tempfile.NamedTemporaryFile') | ||
236 | 740 | @mock.patch('curtin.block.util') | ||
237 | 741 | def test_zkey_supported_calls_zkey_generate(self, m_util, m_temp): | ||
238 | 742 | testname = self.random_string() | ||
239 | 743 | m_temp.return_value.__enter__.return_value.name = testname | ||
240 | 744 | block.zkey_supported() | ||
241 | 745 | m_util.subp.assert_called_with(['zkey', 'generate', testname], | ||
242 | 746 | capture=True) | ||
243 | 747 | |||
244 | 715 | # vi: ts=4 expandtab syntax=python | 748 | # vi: ts=4 expandtab syntax=python |
245 | diff --git a/tests/unittests/test_commands_block_meta.py b/tests/unittests/test_commands_block_meta.py | |||
246 | index b4a9afa..b2e151e 100644 | |||
247 | --- a/tests/unittests/test_commands_block_meta.py | |||
248 | +++ b/tests/unittests/test_commands_block_meta.py | |||
249 | @@ -913,4 +913,178 @@ class TestLvmPartitionHandler(CiTestCase): | |||
250 | 913 | self.assertIn(expected_size_str, call_args[0]) | 913 | self.assertIn(expected_size_str, call_args[0]) |
251 | 914 | 914 | ||
252 | 915 | 915 | ||
253 | 916 | class TestDmCryptHandler(CiTestCase): | ||
254 | 917 | |||
255 | 918 | def setUp(self): | ||
256 | 919 | super(TestDmCryptHandler, self).setUp() | ||
257 | 920 | |||
258 | 921 | basepath = 'curtin.commands.block_meta.' | ||
259 | 922 | self.add_patch(basepath + 'get_path_to_storage_volume', 'm_getpath') | ||
260 | 923 | self.add_patch(basepath + 'util.load_command_environment', | ||
261 | 924 | 'm_load_env') | ||
262 | 925 | self.add_patch(basepath + 'util.which', 'm_which') | ||
263 | 926 | self.add_patch(basepath + 'util.subp', 'm_subp') | ||
264 | 927 | self.add_patch(basepath + 'block', 'm_block') | ||
265 | 928 | |||
266 | 929 | self.target = "my_target" | ||
267 | 930 | self.keyfile = self.random_string() | ||
268 | 931 | self.cipher = self.random_string() | ||
269 | 932 | self.keysize = self.random_string() | ||
270 | 933 | self.config = { | ||
271 | 934 | 'storage': { | ||
272 | 935 | 'version': 1, | ||
273 | 936 | 'config': [ | ||
274 | 937 | {'grub_device': True, | ||
275 | 938 | 'id': 'sda', | ||
276 | 939 | 'name': 'sda', | ||
277 | 940 | 'path': '/wark/xxx', | ||
278 | 941 | 'ptable': 'msdos', | ||
279 | 942 | 'type': 'disk', | ||
280 | 943 | 'wipe': 'superblock'}, | ||
281 | 944 | {'device': 'sda', | ||
282 | 945 | 'id': 'sda-part1', | ||
283 | 946 | 'name': 'sda-part1', | ||
284 | 947 | 'number': 1, | ||
285 | 948 | 'size': '511705088B', | ||
286 | 949 | 'type': 'partition'}, | ||
287 | 950 | {'id': 'dmcrypt0', | ||
288 | 951 | 'type': 'dm_crypt', | ||
289 | 952 | 'dm_name': 'cryptroot', | ||
290 | 953 | 'volume': 'sda-part1', | ||
291 | 954 | 'cipher': self.cipher, | ||
292 | 955 | 'keysize': self.keysize, | ||
293 | 956 | 'keyfile': self.keyfile}, | ||
294 | 957 | ], | ||
295 | 958 | } | ||
296 | 959 | } | ||
297 | 960 | self.storage_config = ( | ||
298 | 961 | block_meta.extract_storage_ordered_dict(self.config)) | ||
299 | 962 | self.m_block.zkey_supported.return_value = False | ||
300 | 963 | self.m_which.return_value = False | ||
301 | 964 | self.fstab = self.tmp_path('fstab') | ||
302 | 965 | self.crypttab = os.path.join(os.path.dirname(self.fstab), 'crypttab') | ||
303 | 966 | self.m_load_env.return_value = {'fstab': self.fstab, | ||
304 | 967 | 'target': self.target} | ||
305 | 968 | |||
306 | 969 | def test_dm_crypt_calls_cryptsetup(self): | ||
307 | 970 | """ verify dm_crypt calls (format, open) w/ correct params""" | ||
308 | 971 | volume_path = self.random_string() | ||
309 | 972 | self.m_getpath.return_value = volume_path | ||
310 | 973 | |||
311 | 974 | info = self.storage_config['dmcrypt0'] | ||
312 | 975 | block_meta.dm_crypt_handler(info, self.storage_config) | ||
313 | 976 | expected_calls = [ | ||
314 | 977 | call(['cryptsetup', '--cipher', self.cipher, | ||
315 | 978 | '--key-size', self.keysize, | ||
316 | 979 | 'luksFormat', volume_path, self.keyfile]), | ||
317 | 980 | call(['cryptsetup', 'open', '--type', 'luks', volume_path, | ||
318 | 981 | info['dm_name'], '--key-file', self.keyfile]) | ||
319 | 982 | ] | ||
320 | 983 | self.m_subp.assert_has_calls(expected_calls) | ||
321 | 984 | self.assertEqual(len(util.load_file(self.crypttab).splitlines()), 1) | ||
322 | 985 | |||
323 | 986 | def test_dm_crypt_zkey_cryptsetup(self): | ||
324 | 987 | """ verify dm_crypt zkey calls generates and run before crypt open.""" | ||
325 | 988 | |||
326 | 989 | # zkey binary is present | ||
327 | 990 | self.m_block.zkey_supported.return_value = True | ||
328 | 991 | self.m_which.return_value = "/my/path/to/zkey" | ||
329 | 992 | volume_path = self.random_string() | ||
330 | 993 | self.m_getpath.return_value = volume_path | ||
331 | 994 | volume_byid = "/dev/disk/by-id/ccw-%s" % volume_path | ||
332 | 995 | self.m_block.disk_to_byid_path.return_value = volume_byid | ||
333 | 996 | |||
334 | 997 | info = self.storage_config['dmcrypt0'] | ||
335 | 998 | volume_name = "%s:%s" % (volume_byid, info['dm_name']) | ||
336 | 999 | block_meta.dm_crypt_handler(info, self.storage_config) | ||
337 | 1000 | expected_calls = [ | ||
338 | 1001 | call(['zkey', 'generate', '--xts', '--volume-type', 'luks2', | ||
339 | 1002 | '--sector-size', '4096', '--name', info['dm_name'], | ||
340 | 1003 | '--description', | ||
341 | 1004 | 'curtin generated zkey for %s' % volume_name, | ||
342 | 1005 | '--volumes', volume_name], capture=True), | ||
343 | 1006 | call(['zkey', 'cryptsetup', '--run', '--volumes', volume_byid, | ||
344 | 1007 | '--batch-mode', '--key-file', self.keyfile], capture=True), | ||
345 | 1008 | call(['cryptsetup', 'open', '--type', 'luks2', volume_path, | ||
346 | 1009 | info['dm_name'], '--key-file', self.keyfile]), | ||
347 | 1010 | ] | ||
348 | 1011 | self.m_subp.assert_has_calls(expected_calls) | ||
349 | 1012 | self.assertEqual(len(util.load_file(self.crypttab).splitlines()), 1) | ||
350 | 1013 | |||
351 | 1014 | def test_dm_crypt_zkey_gen_failure_fallback_to_cryptsetup(self): | ||
352 | 1015 | """ verify dm_cyrpt zkey generate err falls back cryptsetup format. """ | ||
353 | 1016 | |||
354 | 1017 | # zkey binary is present | ||
355 | 1018 | self.m_block.zkey_supported.return_value = True | ||
356 | 1019 | self.m_which.return_value = "/my/path/to/zkey" | ||
357 | 1020 | |||
358 | 1021 | self.m_subp.side_effect = iter([ | ||
359 | 1022 | util.ProcessExecutionError("foobar"), # zkey generate | ||
360 | 1023 | (0, 0), # cryptsetup luksFormat | ||
361 | 1024 | (0, 0), # cryptsetup open | ||
362 | 1025 | ]) | ||
363 | 1026 | |||
364 | 1027 | volume_path = self.random_string() | ||
365 | 1028 | self.m_getpath.return_value = volume_path | ||
366 | 1029 | volume_byid = "/dev/disk/by-id/ccw-%s" % volume_path | ||
367 | 1030 | self.m_block.disk_to_byid_path.return_value = volume_byid | ||
368 | 1031 | |||
369 | 1032 | info = self.storage_config['dmcrypt0'] | ||
370 | 1033 | volume_name = "%s:%s" % (volume_byid, info['dm_name']) | ||
371 | 1034 | block_meta.dm_crypt_handler(info, self.storage_config) | ||
372 | 1035 | expected_calls = [ | ||
373 | 1036 | call(['zkey', 'generate', '--xts', '--volume-type', 'luks2', | ||
374 | 1037 | '--sector-size', '4096', '--name', info['dm_name'], | ||
375 | 1038 | '--description', | ||
376 | 1039 | 'curtin generated zkey for %s' % volume_name, | ||
377 | 1040 | '--volumes', volume_name], capture=True), | ||
378 | 1041 | call(['cryptsetup', '--cipher', self.cipher, | ||
379 | 1042 | '--key-size', self.keysize, | ||
380 | 1043 | 'luksFormat', volume_path, self.keyfile]), | ||
381 | 1044 | call(['cryptsetup', 'open', '--type', 'luks', volume_path, | ||
382 | 1045 | info['dm_name'], '--key-file', self.keyfile]) | ||
383 | 1046 | ] | ||
384 | 1047 | self.m_subp.assert_has_calls(expected_calls) | ||
385 | 1048 | self.assertEqual(len(util.load_file(self.crypttab).splitlines()), 1) | ||
386 | 1049 | |||
387 | 1050 | def test_dm_crypt_zkey_run_failure_fallback_to_cryptsetup(self): | ||
388 | 1051 | """ verify dm_cyrpt zkey run err falls back on cryptsetup format. """ | ||
389 | 1052 | |||
390 | 1053 | # zkey binary is present | ||
391 | 1054 | self.m_block.zkey_supported.return_value = True | ||
392 | 1055 | self.m_which.return_value = "/my/path/to/zkey" | ||
393 | 1056 | |||
394 | 1057 | self.m_subp.side_effect = iter([ | ||
395 | 1058 | (0, 0), # zkey generate | ||
396 | 1059 | util.ProcessExecutionError("foobar"), # zkey cryptsetup --run | ||
397 | 1060 | (0, 0), # cryptsetup luksFormat | ||
398 | 1061 | (0, 0), # cryptsetup open | ||
399 | 1062 | ]) | ||
400 | 1063 | |||
401 | 1064 | volume_path = self.random_string() | ||
402 | 1065 | self.m_getpath.return_value = volume_path | ||
403 | 1066 | volume_byid = "/dev/disk/by-id/ccw-%s" % volume_path | ||
404 | 1067 | self.m_block.disk_to_byid_path.return_value = volume_byid | ||
405 | 1068 | |||
406 | 1069 | info = self.storage_config['dmcrypt0'] | ||
407 | 1070 | volume_name = "%s:%s" % (volume_byid, info['dm_name']) | ||
408 | 1071 | block_meta.dm_crypt_handler(info, self.storage_config) | ||
409 | 1072 | expected_calls = [ | ||
410 | 1073 | call(['zkey', 'generate', '--xts', '--volume-type', 'luks2', | ||
411 | 1074 | '--sector-size', '4096', '--name', info['dm_name'], | ||
412 | 1075 | '--description', | ||
413 | 1076 | 'curtin generated zkey for %s' % volume_name, | ||
414 | 1077 | '--volumes', volume_name], capture=True), | ||
415 | 1078 | call(['zkey', 'cryptsetup', '--run', '--volumes', volume_byid, | ||
416 | 1079 | '--batch-mode', '--key-file', self.keyfile], capture=True), | ||
417 | 1080 | call(['cryptsetup', '--cipher', self.cipher, | ||
418 | 1081 | '--key-size', self.keysize, | ||
419 | 1082 | 'luksFormat', volume_path, self.keyfile]), | ||
420 | 1083 | call(['cryptsetup', 'open', '--type', 'luks', volume_path, | ||
421 | 1084 | info['dm_name'], '--key-file', self.keyfile]) | ||
422 | 1085 | ] | ||
423 | 1086 | self.m_subp.assert_has_calls(expected_calls) | ||
424 | 1087 | self.assertEqual(len(util.load_file(self.crypttab).splitlines()), 1) | ||
425 | 1088 | |||
426 | 1089 | |||
427 | 916 | # vi: ts=4 expandtab syntax=python | 1090 | # vi: ts=4 expandtab syntax=python |
428 | diff --git a/tests/unittests/test_curthooks.py b/tests/unittests/test_curthooks.py | |||
429 | index 26a582c..e7d506e 100644 | |||
430 | --- a/tests/unittests/test_curthooks.py | |||
431 | +++ b/tests/unittests/test_curthooks.py | |||
432 | @@ -9,7 +9,7 @@ from curtin import distro | |||
433 | 9 | from curtin import util | 9 | from curtin import util |
434 | 10 | from curtin import config | 10 | from curtin import config |
435 | 11 | from curtin.reporter import events | 11 | from curtin.reporter import events |
437 | 12 | from .helpers import CiTestCase, dir2dict | 12 | from .helpers import CiTestCase, dir2dict, populate_dir |
438 | 13 | 13 | ||
439 | 14 | 14 | ||
440 | 15 | class TestGetFlashKernelPkgs(CiTestCase): | 15 | class TestGetFlashKernelPkgs(CiTestCase): |
441 | @@ -201,7 +201,8 @@ class TestInstallMissingPkgs(CiTestCase): | |||
442 | 201 | cfg = {} | 201 | cfg = {} |
443 | 202 | curthooks.install_missing_packages(cfg, target=target) | 202 | curthooks.install_missing_packages(cfg, target=target) |
444 | 203 | self.mock_install_packages.assert_called_with( | 203 | self.mock_install_packages.assert_called_with( |
446 | 204 | ['s390-tools'], target=target, osfamily=self.distro_family) | 204 | ['s390-tools'], target=target, |
447 | 205 | osfamily=self.distro_family) | ||
448 | 205 | 206 | ||
449 | 206 | @patch.object(events, 'ReportEventStack') | 207 | @patch.object(events, 'ReportEventStack') |
450 | 207 | def test_install_packages_s390x_has_zipl(self, mock_events): | 208 | def test_install_packages_s390x_has_zipl(self, mock_events): |
451 | @@ -1140,4 +1141,25 @@ class TestCurthooksChzdev(CiTestCase): | |||
452 | 1140 | output = curthooks.chzdev_prepare_for_import(self.chzdev_export) | 1141 | output = curthooks.chzdev_prepare_for_import(self.chzdev_export) |
453 | 1141 | self.assertEqual(self.chzdev_import, output) | 1142 | self.assertEqual(self.chzdev_import, output) |
454 | 1142 | 1143 | ||
455 | 1144 | |||
456 | 1145 | class TestCurthooksCopyZkey(CiTestCase): | ||
457 | 1146 | def setUp(self): | ||
458 | 1147 | super(TestCurthooksCopyZkey, self).setUp() | ||
459 | 1148 | self.add_patch('curtin.distro.install_packages', 'mock_instpkg') | ||
460 | 1149 | |||
461 | 1150 | self.target = self.tmp_dir() | ||
462 | 1151 | self.host_dir = self.tmp_dir() | ||
463 | 1152 | self.zkey_content = { | ||
464 | 1153 | '/etc/zkey/repository/mykey.info': "key info", | ||
465 | 1154 | '/etc/zkey/repository/mykey.skey': "key data", | ||
466 | 1155 | } | ||
467 | 1156 | self.files = populate_dir(self.host_dir, self.zkey_content) | ||
468 | 1157 | self.host_zkey = os.path.join(self.host_dir, 'etc/zkey/repository') | ||
469 | 1158 | |||
470 | 1159 | def test_copy_zkey_when_dir_present(self): | ||
471 | 1160 | curthooks.copy_zkey_repository(self.host_zkey, self.target) | ||
472 | 1161 | found_files = dir2dict(self.target, prefix=self.target) | ||
473 | 1162 | self.assertEqual(self.zkey_content, found_files) | ||
474 | 1163 | |||
475 | 1164 | |||
476 | 1143 | # vi: ts=4 expandtab syntax=python | 1165 | # vi: ts=4 expandtab syntax=python |
PASSED: Continuous integration, rev:71392162468 008a273258c5b2c 4dfa09c8997dc9 /jenkins. ubuntu. com/server/ job/curtin- ci/1296/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-arm64/ 1296 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-ppc64el/ 1296 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= metal-s390x/ 1296 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= torkoal/ 1296
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/curtin- ci/1296/ rebuild
https:/