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:
|
|||
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 | return mounts |
| 7 | |
| 8 | |
| 9 | +def _get_dev_disk_by_prefix(prefix): |
| 10 | + """ |
| 11 | + Construct a dictionary mapping devname to disk/<prefix> paths |
| 12 | + |
| 13 | + :returns: Dictionary populated by examining /dev/disk/<prefix>/* |
| 14 | + |
| 15 | + { |
| 16 | + '/dev/sda': '/dev/disk/<prefix>/virtio-aaaa', |
| 17 | + '/dev/sda1': '/dev/disk/<prefix>/virtio-aaaa-part1', |
| 18 | + } |
| 19 | + """ |
| 20 | + return { |
| 21 | + os.path.realpath(bypfx): bypfx |
| 22 | + for bypfx in [os.path.join(prefix, path) |
| 23 | + for path in os.listdir(prefix)] |
| 24 | + } |
| 25 | + |
| 26 | + |
| 27 | def get_dev_disk_byid(): |
| 28 | """ |
| 29 | Construct a dictionary mapping devname to disk/by-id paths |
| 30 | @@ -679,12 +697,7 @@ def get_dev_disk_byid(): |
| 31 | '/dev/sda1': '/dev/disk/by-id/virtio-aaaa-part1', |
| 32 | } |
| 33 | """ |
| 34 | - |
| 35 | - prefix = '/dev/disk/by-id' |
| 36 | - return { |
| 37 | - os.path.realpath(byid): byid |
| 38 | - for byid in [os.path.join(prefix, path) for path in os.listdir(prefix)] |
| 39 | - } |
| 40 | + return _get_dev_disk_by_prefix('/dev/disk/by-id') |
| 41 | |
| 42 | |
| 43 | def disk_to_byid_path(kname): |
| 44 | @@ -696,6 +709,15 @@ def disk_to_byid_path(kname): |
| 45 | return mapping.get(dev_path(kname)) |
| 46 | |
| 47 | |
| 48 | +def disk_to_bypath_path(kname): |
| 49 | + """" |
| 50 | + Return a /dev/disk/by-path path to kname if present. |
| 51 | + """ |
| 52 | + |
| 53 | + mapping = _get_dev_disk_by_prefix('/dev/disk/by-path') |
| 54 | + return mapping.get(dev_path(kname)) |
| 55 | + |
| 56 | + |
| 57 | def get_device_mapper_links(devpath, first=False): |
| 58 | """ Return the best devlink to device at devpath. """ |
| 59 | info = udevadm_info(devpath) |
| 60 | @@ -892,6 +914,28 @@ def is_online(device): |
| 61 | return int(device_size) > 0 |
| 62 | |
| 63 | |
| 64 | +def zkey_supported(strict=True): |
| 65 | + """ Return True if zkey cmd present and can generate keys, else False.""" |
| 66 | + LOG.debug('Checking if zkey encryption is supported...') |
| 67 | + try: |
| 68 | + util.load_kernel_module('pkey') |
| 69 | + except util.ProcessExecutionError as err: |
| 70 | + msg = "Failed to load 'pkey' kernel module" |
| 71 | + LOG.error(msg + ": %s" % err) if strict else LOG.warning(msg) |
| 72 | + return False |
| 73 | + |
| 74 | + try: |
| 75 | + with tempfile.NamedTemporaryFile() as tf: |
| 76 | + util.subp(['zkey', 'generate', tf.name], capture=True) |
| 77 | + LOG.debug('zkey encryption supported.') |
| 78 | + return True |
| 79 | + except util.ProcessExecutionError as err: |
| 80 | + msg = "zkey not supported" |
| 81 | + LOG.error(msg + ": %s" % err) if strict else LOG.warning(msg) |
| 82 | + |
| 83 | + return False |
| 84 | + |
| 85 | + |
| 86 | @contextmanager |
| 87 | def exclusive_open(path, exclusive=True): |
| 88 | """ |
| 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 | dm_name = info.get('id') |
| 95 | |
| 96 | volume_path = get_path_to_storage_volume(volume, storage_config) |
| 97 | + volume_byid_path = block.disk_to_byid_path(volume_path) |
| 98 | |
| 99 | if 'keyfile' in info: |
| 100 | if 'key' in info: |
| 101 | @@ -1077,16 +1078,45 @@ def dm_crypt_handler(info, storage_config): |
| 102 | else: |
| 103 | raise ValueError("encryption key or keyfile must be specified") |
| 104 | |
| 105 | - cmd = ["cryptsetup"] |
| 106 | - if cipher: |
| 107 | - cmd.extend(["--cipher", cipher]) |
| 108 | - if keysize: |
| 109 | - cmd.extend(["--key-size", keysize]) |
| 110 | - cmd.extend(["luksFormat", volume_path, keyfile]) |
| 111 | - |
| 112 | - util.subp(cmd) |
| 113 | + # if zkey is available, attempt to generate and use it; if it's not |
| 114 | + # available or fails to setup properly, fallback to normal cryptsetup |
| 115 | + # passing strict=False downgrades log messages to warnings |
| 116 | + zkey_used = None |
| 117 | + if block.zkey_supported(strict=False): |
| 118 | + volume_name = "%s:%s" % (volume_byid_path, dm_name) |
| 119 | + LOG.debug('Attempting to setup zkey for %s', volume_name) |
| 120 | + luks_type = 'luks2' |
| 121 | + gen_cmd = ['zkey', 'generate', '--xts', '--volume-type', luks_type, |
| 122 | + '--sector-size', '4096', '--name', dm_name, |
| 123 | + '--description', |
| 124 | + "curtin generated zkey for %s" % volume_name, |
| 125 | + '--volumes', volume_name] |
| 126 | + run_cmd = ['zkey', 'cryptsetup', '--run', '--volumes', |
| 127 | + volume_byid_path, '--batch-mode', '--key-file', keyfile] |
| 128 | + try: |
| 129 | + util.subp(gen_cmd, capture=True) |
| 130 | + util.subp(run_cmd, capture=True) |
| 131 | + zkey_used = os.path.join(os.path.split(state['fstab'])[0], |
| 132 | + "zkey_used") |
| 133 | + # mark in state that we used zkey |
| 134 | + util.write_file(zkey_used, "1") |
| 135 | + except util.ProcessExecutionError as e: |
| 136 | + LOG.exception(e) |
| 137 | + msg = 'Setup of zkey on %s failed, fallback to cryptsetup.' |
| 138 | + LOG.error(msg % volume_path) |
| 139 | + |
| 140 | + if not zkey_used: |
| 141 | + LOG.debug('Using cryptsetup on %s', volume_path) |
| 142 | + luks_type = "luks" |
| 143 | + cmd = ["cryptsetup"] |
| 144 | + if cipher: |
| 145 | + cmd.extend(["--cipher", cipher]) |
| 146 | + if keysize: |
| 147 | + cmd.extend(["--key-size", keysize]) |
| 148 | + cmd.extend(["luksFormat", volume_path, keyfile]) |
| 149 | + util.subp(cmd) |
| 150 | |
| 151 | - cmd = ["cryptsetup", "open", "--type", "luks", volume_path, dm_name, |
| 152 | + cmd = ["cryptsetup", "open", "--type", luks_type, volume_path, dm_name, |
| 153 | "--key-file", keyfile] |
| 154 | |
| 155 | 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 | shutil.copy(zpool_cache, os.path.sep.join([target, 'etc/zfs'])) |
| 162 | |
| 163 | |
| 164 | +def copy_zkey_repository(zkey_repository, target, |
| 165 | + target_repo='etc/zkey/repository'): |
| 166 | + if not zkey_repository: |
| 167 | + LOG.warn("zkey repository path must be specified, not copying") |
| 168 | + return |
| 169 | + |
| 170 | + tdir = os.path.sep.join([target, target_repo]) |
| 171 | + if not os.path.exists(tdir): |
| 172 | + util.ensure_dir(tdir) |
| 173 | + |
| 174 | + files_copied = [] |
| 175 | + for src in os.listdir(zkey_repository): |
| 176 | + source_path = os.path.join(zkey_repository, src) |
| 177 | + target_path = os.path.join(tdir, src) |
| 178 | + if not os.path.exists(target_path): |
| 179 | + shutil.copy2(source_path, target_path) |
| 180 | + files_copied.append(target_path) |
| 181 | + |
| 182 | + LOG.debug('Imported zkey repo %s with files: %s', |
| 183 | + zkey_repository, files_copied) |
| 184 | + |
| 185 | + |
| 186 | def apply_networking(target, state): |
| 187 | netconf = state.get('network_config') |
| 188 | interfaces = state.get('interfaces') |
| 189 | @@ -1379,6 +1401,13 @@ def builtin_curthooks(cfg, target, state): |
| 190 | if os.path.exists(zpool_cache): |
| 191 | copy_zpool_cache(zpool_cache, target) |
| 192 | |
| 193 | + zkey_repository = '/etc/zkey/repository' |
| 194 | + zkey_used = os.path.join(os.path.split(state['fstab'])[0], "zkey_used") |
| 195 | + if all(map(os.path.exists, [zkey_repository, zkey_used])): |
| 196 | + distro.install_packages(['s390-tools-zkey'], target=target, |
| 197 | + osfamily=osfamily) |
| 198 | + copy_zkey_repository(zkey_repository, target) |
| 199 | + |
| 200 | # If a crypttab file was created by block_meta than it needs to be |
| 201 | # copied onto the target system, and update_initramfs() needs to be |
| 202 | # 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 | self.assertEqual(0, mock_util.load_file.call_count) |
| 209 | |
| 210 | |
| 211 | +class TestZkeySupported(CiTestCase): |
| 212 | + |
| 213 | + @mock.patch('curtin.block.util') |
| 214 | + def test_zkey_supported_loads_module(self, m_util): |
| 215 | + block.zkey_supported() |
| 216 | + m_util.load_kernel_module.assert_called_with('pkey') |
| 217 | + |
| 218 | + @mock.patch('curtin.block.util.load_kernel_module') |
| 219 | + def test_zkey_supported_returns_false_missing_kmod(self, m_kmod): |
| 220 | + m_kmod.side_effect = ( |
| 221 | + util.ProcessExecutionError(stdout=self.random_string(), |
| 222 | + stderr=self.random_string(), |
| 223 | + exit_code=2)) |
| 224 | + self.assertFalse(block.zkey_supported()) |
| 225 | + |
| 226 | + @mock.patch('curtin.block.util.subp') |
| 227 | + @mock.patch('curtin.block.util.load_kernel_module') |
| 228 | + def test_zkey_supported_returns_false_zkey_error(self, m_kmod, m_subp): |
| 229 | + m_subp.side_effect = ( |
| 230 | + util.ProcessExecutionError(stdout=self.random_string(), |
| 231 | + stderr=self.random_string(), |
| 232 | + exit_code=2)) |
| 233 | + self.assertFalse(block.zkey_supported()) |
| 234 | + |
| 235 | + @mock.patch('curtin.block.tempfile.NamedTemporaryFile') |
| 236 | + @mock.patch('curtin.block.util') |
| 237 | + def test_zkey_supported_calls_zkey_generate(self, m_util, m_temp): |
| 238 | + testname = self.random_string() |
| 239 | + m_temp.return_value.__enter__.return_value.name = testname |
| 240 | + block.zkey_supported() |
| 241 | + m_util.subp.assert_called_with(['zkey', 'generate', testname], |
| 242 | + capture=True) |
| 243 | + |
| 244 | # 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 | self.assertIn(expected_size_str, call_args[0]) |
| 251 | |
| 252 | |
| 253 | +class TestDmCryptHandler(CiTestCase): |
| 254 | + |
| 255 | + def setUp(self): |
| 256 | + super(TestDmCryptHandler, self).setUp() |
| 257 | + |
| 258 | + basepath = 'curtin.commands.block_meta.' |
| 259 | + self.add_patch(basepath + 'get_path_to_storage_volume', 'm_getpath') |
| 260 | + self.add_patch(basepath + 'util.load_command_environment', |
| 261 | + 'm_load_env') |
| 262 | + self.add_patch(basepath + 'util.which', 'm_which') |
| 263 | + self.add_patch(basepath + 'util.subp', 'm_subp') |
| 264 | + self.add_patch(basepath + 'block', 'm_block') |
| 265 | + |
| 266 | + self.target = "my_target" |
| 267 | + self.keyfile = self.random_string() |
| 268 | + self.cipher = self.random_string() |
| 269 | + self.keysize = self.random_string() |
| 270 | + self.config = { |
| 271 | + 'storage': { |
| 272 | + 'version': 1, |
| 273 | + 'config': [ |
| 274 | + {'grub_device': True, |
| 275 | + 'id': 'sda', |
| 276 | + 'name': 'sda', |
| 277 | + 'path': '/wark/xxx', |
| 278 | + 'ptable': 'msdos', |
| 279 | + 'type': 'disk', |
| 280 | + 'wipe': 'superblock'}, |
| 281 | + {'device': 'sda', |
| 282 | + 'id': 'sda-part1', |
| 283 | + 'name': 'sda-part1', |
| 284 | + 'number': 1, |
| 285 | + 'size': '511705088B', |
| 286 | + 'type': 'partition'}, |
| 287 | + {'id': 'dmcrypt0', |
| 288 | + 'type': 'dm_crypt', |
| 289 | + 'dm_name': 'cryptroot', |
| 290 | + 'volume': 'sda-part1', |
| 291 | + 'cipher': self.cipher, |
| 292 | + 'keysize': self.keysize, |
| 293 | + 'keyfile': self.keyfile}, |
| 294 | + ], |
| 295 | + } |
| 296 | + } |
| 297 | + self.storage_config = ( |
| 298 | + block_meta.extract_storage_ordered_dict(self.config)) |
| 299 | + self.m_block.zkey_supported.return_value = False |
| 300 | + self.m_which.return_value = False |
| 301 | + self.fstab = self.tmp_path('fstab') |
| 302 | + self.crypttab = os.path.join(os.path.dirname(self.fstab), 'crypttab') |
| 303 | + self.m_load_env.return_value = {'fstab': self.fstab, |
| 304 | + 'target': self.target} |
| 305 | + |
| 306 | + def test_dm_crypt_calls_cryptsetup(self): |
| 307 | + """ verify dm_crypt calls (format, open) w/ correct params""" |
| 308 | + volume_path = self.random_string() |
| 309 | + self.m_getpath.return_value = volume_path |
| 310 | + |
| 311 | + info = self.storage_config['dmcrypt0'] |
| 312 | + block_meta.dm_crypt_handler(info, self.storage_config) |
| 313 | + expected_calls = [ |
| 314 | + call(['cryptsetup', '--cipher', self.cipher, |
| 315 | + '--key-size', self.keysize, |
| 316 | + 'luksFormat', volume_path, self.keyfile]), |
| 317 | + call(['cryptsetup', 'open', '--type', 'luks', volume_path, |
| 318 | + info['dm_name'], '--key-file', self.keyfile]) |
| 319 | + ] |
| 320 | + self.m_subp.assert_has_calls(expected_calls) |
| 321 | + self.assertEqual(len(util.load_file(self.crypttab).splitlines()), 1) |
| 322 | + |
| 323 | + def test_dm_crypt_zkey_cryptsetup(self): |
| 324 | + """ verify dm_crypt zkey calls generates and run before crypt open.""" |
| 325 | + |
| 326 | + # zkey binary is present |
| 327 | + self.m_block.zkey_supported.return_value = True |
| 328 | + self.m_which.return_value = "/my/path/to/zkey" |
| 329 | + volume_path = self.random_string() |
| 330 | + self.m_getpath.return_value = volume_path |
| 331 | + volume_byid = "/dev/disk/by-id/ccw-%s" % volume_path |
| 332 | + self.m_block.disk_to_byid_path.return_value = volume_byid |
| 333 | + |
| 334 | + info = self.storage_config['dmcrypt0'] |
| 335 | + volume_name = "%s:%s" % (volume_byid, info['dm_name']) |
| 336 | + block_meta.dm_crypt_handler(info, self.storage_config) |
| 337 | + expected_calls = [ |
| 338 | + call(['zkey', 'generate', '--xts', '--volume-type', 'luks2', |
| 339 | + '--sector-size', '4096', '--name', info['dm_name'], |
| 340 | + '--description', |
| 341 | + 'curtin generated zkey for %s' % volume_name, |
| 342 | + '--volumes', volume_name], capture=True), |
| 343 | + call(['zkey', 'cryptsetup', '--run', '--volumes', volume_byid, |
| 344 | + '--batch-mode', '--key-file', self.keyfile], capture=True), |
| 345 | + call(['cryptsetup', 'open', '--type', 'luks2', volume_path, |
| 346 | + info['dm_name'], '--key-file', self.keyfile]), |
| 347 | + ] |
| 348 | + self.m_subp.assert_has_calls(expected_calls) |
| 349 | + self.assertEqual(len(util.load_file(self.crypttab).splitlines()), 1) |
| 350 | + |
| 351 | + def test_dm_crypt_zkey_gen_failure_fallback_to_cryptsetup(self): |
| 352 | + """ verify dm_cyrpt zkey generate err falls back cryptsetup format. """ |
| 353 | + |
| 354 | + # zkey binary is present |
| 355 | + self.m_block.zkey_supported.return_value = True |
| 356 | + self.m_which.return_value = "/my/path/to/zkey" |
| 357 | + |
| 358 | + self.m_subp.side_effect = iter([ |
| 359 | + util.ProcessExecutionError("foobar"), # zkey generate |
| 360 | + (0, 0), # cryptsetup luksFormat |
| 361 | + (0, 0), # cryptsetup open |
| 362 | + ]) |
| 363 | + |
| 364 | + volume_path = self.random_string() |
| 365 | + self.m_getpath.return_value = volume_path |
| 366 | + volume_byid = "/dev/disk/by-id/ccw-%s" % volume_path |
| 367 | + self.m_block.disk_to_byid_path.return_value = volume_byid |
| 368 | + |
| 369 | + info = self.storage_config['dmcrypt0'] |
| 370 | + volume_name = "%s:%s" % (volume_byid, info['dm_name']) |
| 371 | + block_meta.dm_crypt_handler(info, self.storage_config) |
| 372 | + expected_calls = [ |
| 373 | + call(['zkey', 'generate', '--xts', '--volume-type', 'luks2', |
| 374 | + '--sector-size', '4096', '--name', info['dm_name'], |
| 375 | + '--description', |
| 376 | + 'curtin generated zkey for %s' % volume_name, |
| 377 | + '--volumes', volume_name], capture=True), |
| 378 | + call(['cryptsetup', '--cipher', self.cipher, |
| 379 | + '--key-size', self.keysize, |
| 380 | + 'luksFormat', volume_path, self.keyfile]), |
| 381 | + call(['cryptsetup', 'open', '--type', 'luks', volume_path, |
| 382 | + info['dm_name'], '--key-file', self.keyfile]) |
| 383 | + ] |
| 384 | + self.m_subp.assert_has_calls(expected_calls) |
| 385 | + self.assertEqual(len(util.load_file(self.crypttab).splitlines()), 1) |
| 386 | + |
| 387 | + def test_dm_crypt_zkey_run_failure_fallback_to_cryptsetup(self): |
| 388 | + """ verify dm_cyrpt zkey run err falls back on cryptsetup format. """ |
| 389 | + |
| 390 | + # zkey binary is present |
| 391 | + self.m_block.zkey_supported.return_value = True |
| 392 | + self.m_which.return_value = "/my/path/to/zkey" |
| 393 | + |
| 394 | + self.m_subp.side_effect = iter([ |
| 395 | + (0, 0), # zkey generate |
| 396 | + util.ProcessExecutionError("foobar"), # zkey cryptsetup --run |
| 397 | + (0, 0), # cryptsetup luksFormat |
| 398 | + (0, 0), # cryptsetup open |
| 399 | + ]) |
| 400 | + |
| 401 | + volume_path = self.random_string() |
| 402 | + self.m_getpath.return_value = volume_path |
| 403 | + volume_byid = "/dev/disk/by-id/ccw-%s" % volume_path |
| 404 | + self.m_block.disk_to_byid_path.return_value = volume_byid |
| 405 | + |
| 406 | + info = self.storage_config['dmcrypt0'] |
| 407 | + volume_name = "%s:%s" % (volume_byid, info['dm_name']) |
| 408 | + block_meta.dm_crypt_handler(info, self.storage_config) |
| 409 | + expected_calls = [ |
| 410 | + call(['zkey', 'generate', '--xts', '--volume-type', 'luks2', |
| 411 | + '--sector-size', '4096', '--name', info['dm_name'], |
| 412 | + '--description', |
| 413 | + 'curtin generated zkey for %s' % volume_name, |
| 414 | + '--volumes', volume_name], capture=True), |
| 415 | + call(['zkey', 'cryptsetup', '--run', '--volumes', volume_byid, |
| 416 | + '--batch-mode', '--key-file', self.keyfile], capture=True), |
| 417 | + call(['cryptsetup', '--cipher', self.cipher, |
| 418 | + '--key-size', self.keysize, |
| 419 | + 'luksFormat', volume_path, self.keyfile]), |
| 420 | + call(['cryptsetup', 'open', '--type', 'luks', volume_path, |
| 421 | + info['dm_name'], '--key-file', self.keyfile]) |
| 422 | + ] |
| 423 | + self.m_subp.assert_has_calls(expected_calls) |
| 424 | + self.assertEqual(len(util.load_file(self.crypttab).splitlines()), 1) |
| 425 | + |
| 426 | + |
| 427 | # 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 | from curtin import util |
| 434 | from curtin import config |
| 435 | from curtin.reporter import events |
| 436 | -from .helpers import CiTestCase, dir2dict |
| 437 | +from .helpers import CiTestCase, dir2dict, populate_dir |
| 438 | |
| 439 | |
| 440 | class TestGetFlashKernelPkgs(CiTestCase): |
| 441 | @@ -201,7 +201,8 @@ class TestInstallMissingPkgs(CiTestCase): |
| 442 | cfg = {} |
| 443 | curthooks.install_missing_packages(cfg, target=target) |
| 444 | self.mock_install_packages.assert_called_with( |
| 445 | - ['s390-tools'], target=target, osfamily=self.distro_family) |
| 446 | + ['s390-tools'], target=target, |
| 447 | + osfamily=self.distro_family) |
| 448 | |
| 449 | @patch.object(events, 'ReportEventStack') |
| 450 | def test_install_packages_s390x_has_zipl(self, mock_events): |
| 451 | @@ -1140,4 +1141,25 @@ class TestCurthooksChzdev(CiTestCase): |
| 452 | output = curthooks.chzdev_prepare_for_import(self.chzdev_export) |
| 453 | self.assertEqual(self.chzdev_import, output) |
| 454 | |
| 455 | + |
| 456 | +class TestCurthooksCopyZkey(CiTestCase): |
| 457 | + def setUp(self): |
| 458 | + super(TestCurthooksCopyZkey, self).setUp() |
| 459 | + self.add_patch('curtin.distro.install_packages', 'mock_instpkg') |
| 460 | + |
| 461 | + self.target = self.tmp_dir() |
| 462 | + self.host_dir = self.tmp_dir() |
| 463 | + self.zkey_content = { |
| 464 | + '/etc/zkey/repository/mykey.info': "key info", |
| 465 | + '/etc/zkey/repository/mykey.skey': "key data", |
| 466 | + } |
| 467 | + self.files = populate_dir(self.host_dir, self.zkey_content) |
| 468 | + self.host_zkey = os.path.join(self.host_dir, 'etc/zkey/repository') |
| 469 | + |
| 470 | + def test_copy_zkey_when_dir_present(self): |
| 471 | + curthooks.copy_zkey_repository(self.host_zkey, self.target) |
| 472 | + found_files = dir2dict(self.target, prefix=self.target) |
| 473 | + self.assertEqual(self.zkey_content, found_files) |
| 474 | + |
| 475 | + |
| 476 | # 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:/