Add LUKS disk encryption support for Linux OS morphing#436
Conversation
33a3ef6 to
1e84389
Compare
|
|
||
| def _configure_dracut_keyfiles(self, os_root_dir, uuid_to_keyfile): | ||
| """Write a dracut.conf.d snippet to embed keyfiles in the initramfs.""" | ||
| for dracut_bin in ["usr/bin/dracut", "usr/sbin/dracut", "sbin/dracut"]: |
There was a problem hiding this comment.
I'd just update the caller to use _detect_initramfs_tool and remove this check. The caller would then use either _configure_dracut_keyfiles or _configure_initramfs_tools_keyfiles.
|
|
||
| return "sysvinit" | ||
|
|
||
| def _register_firstboot_script_systemd(self, os_root_dir): |
There was a problem hiding this comment.
We'll also need this for user provided "firstboot" scripts. I guess we'll end up with a separate public method as part of BaseOSMorphingTools.
| disk_id = os.path.basename(self._src_device) | ||
|
|
||
| # Write a minimal Linux OS on the device, encrypted with LUKS. | ||
| test_utils.make_luks_device( |
There was a problem hiding this comment.
Later on, it might be useful to test the scenario in which there are multiple disk attachments. We'd have to ensure that Coriolis doesn't error out if the user didn't specify the keys for secondary encrypted disks.
There was a problem hiding this comment.
Indeed, but that is going to be more relevant in more complex tests, in which we actually spin up a replica and assert that the first boot scripts also worked as intended, and then we can also assert that additional disks are also accessible. Not much we can do here, in these tests specifically.
| Reads osmorphing_info["disk_luks_passphrases"], containing mappings | ||
| {"device_path": "passphrase"}. | ||
|
|
||
| For each device that has a passphrase entry and is confirmed LUKS by |
There was a problem hiding this comment.
So the user will have to specify the luks passphrase for each disk path, right?
Do we expect those to be udev links based on the disk WWN or serial ID? For example:
/dev/disk/by-uuid/4306c488-13c8-447a-825d-3e9216876081
/dev/disk/by-id/wwn-0x6d094660793802002afcbbe61cfbcd38
/dev/disk/by-id/scsi-36d094660793802002afcbbe61cfbcd38
Do we need coriolis-web changes to accommodate this?
There was a problem hiding this comment.
As discussed during the call, the user can't know these paths beforehand since the original WWN / SCSI ID won't be preserved.
The consensus was that we're going to use the same key for all LUKS encrypted disks.
|
|
||
| _SYSTEMD_UNIT = """\ | ||
| [Unit] | ||
| Description=Coriolis LUKS migration firstboot cleanup |
There was a problem hiding this comment.
Won't it run at every boot, not just the first one?
There was a problem hiding this comment.
Nevermind, the invoked scripts are supposed to disable the service.
The bulk of the work lives in the new `LinuxLUKSMixin` class (`osmount/luks_mixin.py`), which is then included in `BaseLinuxOSMountTools`: - `mount_os()`: check `osmorphing_info["disk_luks_passphrases"]` for each block device. confirmed LUKS containers are opened via `cryptsetup luksOpen` and the resulting `/dev/mapper/<name>` path is used in place of the raw device. `dismount_os()` closes them again after all filesystems have been unmounted. - `remove_encryption_artifacts`: after OS morphing, stale TPM2 LUKS tokens and their keyslots are killed and the corresponding `tpm2-*` options are stripped from `/etc/crypttab`. The source TPM does not exist on the destination, so leaving these in place would cause the initramfs to hang or fail on first boot. - `install_encryption_firstboot_setup`: a temporary migration keyfile is injected into the guest, `/etc/crypttab` is updated to reference it, the initramfs is rebuilt so the migrated VM can boot, GRUB is patched to use the crypttab mapper names instead of the osmount-time names, and a systemd one-shot service is installed to re-enroll TPM2 and remove the migration keyfile on the first boot of the destination VM. The firstboot shell script itself lives in `coriolis/osmorphing/osmount/resources/luks_firstboot_initramfs_tools.sh` and targets `update-initramfs`-based systems (Debian / Ubuntu).
Extends `LinuxLuksMixin` with dracut support for RHEL / Fedora / SUSE guests: - `_configure_dracut_keyfiles`: writes a `dracut.conf.d/99-coriolis-luks.conf` snippet that adds the migration keyfiles to `install_items`, ensuring dracut embeds them in the initramfs. It also probes for the `libcryptsetup-token-systemd-tpm2.so` plugin (checked against a list of known paths) and adds it explicitly, because cryptsetup loads TPM2 token plugins via `dlopen` and dracut's `ldd` analysis would otherwise miss it along with its `libtss2` dependencies. - `_build_dracut_include_args`: returns `--include` args that force-embed `/etc/crypttab` and all `coriolis_*.key` keyfiles into the initramfs image. Without an explicit crypttab embed, dracut names the mapper `luks-<UUID>` rather than the crypttab name and cannot find the keyfile at boot. - `luks_firstboot_dracut.sh`: the firstboot shell script for dracut-based systems. Runs once on first boot to re-enroll TPM2, remove the migration keyslots, and rebuild the initramfs so the embedded keyfile no longer ships in future initramfs images.
Adds `cryptsetup` to the `data-minion` Dockerfile. Required by osmount LUKS unlock / lock; `cryptsetup` `luksOpen` / `luksClose` are called over SSH on the morphing container. Adds `make_luks_device` to `test_utils.py`: formats the device with LUKS, opens it, writes a minimal Linux OS tree inside via make_os_device(), then closes the mapper. Adds integration test in which the source disk is LUKS-encrypted. The test runs a full transfer + deployment with skip_os_morphing=False, and asserts that it completed.
1e84389 to
22bab98
Compare
|
|
||
| def _prepare_src_device(self): | ||
| disk_id = os.path.basename(self._src_device) | ||
| os.path.basename(self._src_device) |
There was a problem hiding this comment.
Leftover? It's using os.path.basename without assigning it.
|
|
||
| def _prepare_src_device(self): | ||
| disk_id = os.path.basename(self._src_device) | ||
| os.path.basename(self._src_device) |
The bulk of the work lives in the new
LinuxLuksMixinclass(
osmount/luks_mixin.py), which is then included inBaseLinuxOSMountTools:mount_os(): checkosmorphing_info["disk_luks_passphrases"]for each block device. confirmed LUKS containers are opened viacryptsetup luksOpenand the resulting/dev/mapper/<name>path is used in place of the raw device.dismount_os()closes them again after all filesystems have been unmounted.remove_encryption_artifacts: after OS morphing, stale TPM2 LUKS tokens and their keyslots are killed and the correspondingtpm2-*options are stripped from/etc/crypttab. The source TPM does not exist on the destination, so leaving these in place would cause the initramfs to hang or fail on first boot.install_encryption_firstboot_setup: a temporary migration keyfile is injected into the guest,/etc/crypttabis updated to reference it, the initramfs is rebuilt so the migrated VM can boot, GRUB is patched to use the crypttab mapper names instead of the osmount-time names, and a systemd one-shot service is installed to re-enroll TPM2 and remove the migration keyfile on the first boot of the destination VM.The firstboot shell scripts live in
coriolis/osmorphing/osmount/resources:luks_firstboot_initramfs_tools.shand targetsupdate-initramfs-based systems (Debian / Ubuntu).luks_firstboot_dracut.sh: targets dracut-based systems.