From 64bbd3bc6431b98e3122bc3c3a6c3f6a62cb058f Mon Sep 17 00:00:00 2001 From: Igor Fedotov Date: Tue, 7 Mar 2023 19:53:57 +0300 Subject: [PATCH] ceph-volume: support creating/migrating encrypted lvm volumes Fixes: https://tracker.ceph.com/issues/55167 Signed-off-by: Igor Fedotov (cherry picked from commit b45d9b79aa079f3cfe227c74c1df3a3bfad4c7c9) --- .../ceph_volume/devices/lvm/migrate.py | 44 +++++++++++++++---- .../ceph_volume/devices/lvm/prepare.py | 14 +----- .../ceph_volume/util/encryption.py | 27 ++++++++++-- 3 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/ceph-volume/ceph_volume/devices/lvm/migrate.py b/src/ceph-volume/ceph_volume/devices/lvm/migrate.py index 86159fd505b..64589a2d628 100644 --- a/src/ceph-volume/ceph_volume/devices/lvm/migrate.py +++ b/src/ceph-volume/ceph_volume/devices/lvm/migrate.py @@ -6,6 +6,7 @@ from textwrap import dedent from ceph_volume.util import system, disk, merge_dict from ceph_volume.util.device import Device from ceph_volume.util.arg_validators import valid_osd_id +from ceph_volume.util import encryption as encryption_utils from ceph_volume import decorators, terminal, process from ceph_volume.api import lvm as api from ceph_volume.systemd import systemctl @@ -300,6 +301,15 @@ class Migrate(object): osd_path, self.get_filename_by_type(type))] return ret + def close_encrypted(self, source_devices): + # close source device(-s) if they're encrypted and have been removed + for device,type in source_devices: + if (type == 'db' or type == 'wal'): + logger.info("closing dmcrypt volume {}" + .format(device.lv_api.lv_uuid)) + encryption_utils.dmcrypt_close( + mapping = device.lv_api.lv_uuid, skip_path_check=True) + @decorators.needs_root def migrate_to_new(self, osd_id, osd_fsid, devices, target_lv): source_devices = self.get_source_devices(devices) @@ -312,9 +322,14 @@ class Migrate(object): "Unable to migrate to : {}".format(self.args.target)) target_path = target_lv.lv_path - + tag_tracker = VolumeTagTracker(devices, target_lv) + # prepare and encrypt target if data volume is encrypted + if tag_tracker.data_device.lv_api.encrypted: + secret = encryption_utils.get_dmcrypt_key(osd_id, osd_fsid) + mlogger.info(' preparing dmcrypt for {}, uuid {}'.format(target_lv.lv_path, target_lv.lv_uuid)) + target_path = encryption_utils.prepare_dmcrypt( + key=secret, device=target_path, mapping=target_lv.lv_uuid) try: - tag_tracker = VolumeTagTracker(devices, target_lv) # we need to update lvm tags for all the remaining volumes # and clear for ones which to be removed @@ -340,10 +355,13 @@ class Migrate(object): 'Failed to migrate device, error code:{}'.format(exit_code)) raise SystemExit( 'Failed to migrate to : {}'.format(self.args.target)) - else: - system.chown(os.path.join(osd_path, "block.{}".format( - target_type))) - terminal.success('Migration successful.') + + system.chown(os.path.join(osd_path, "block.{}".format( + target_type))) + if tag_tracker.data_device.lv_api.encrypted: + self.close_encrypted(source_devices) + terminal.success('Migration successful.') + except: tag_tracker.undo() raise @@ -391,8 +409,9 @@ class Migrate(object): 'Failed to migrate device, error code:{}'.format(exit_code)) raise SystemExit( 'Failed to migrate to : {}'.format(self.args.target)) - else: - terminal.success('Migration successful.') + if tag_tracker.data_device.lv_api.encrypted: + self.close_encrypted(source_devices) + terminal.success('Migration successful.') except: tag_tracker.undo() raise @@ -574,7 +593,14 @@ class NewVolume(object): mlogger.info( 'Making new volume at {} for OSD: {} ({})'.format( target_lv.lv_path, osd_id, osd_path)) + target_path = target_lv.lv_path tag_tracker = VolumeTagTracker(devices, target_lv) + # prepare and encrypt target if data volume is encrypted + if tag_tracker.data_device.lv_api.encrypted: + secret = encryption_utils.get_dmcrypt_key(osd_id, osd_fsid) + mlogger.info(' preparing dmcrypt for {}, uuid {}'.format(target_lv.lv_path, target_lv.lv_uuid)) + target_path = encryption_utils.prepare_dmcrypt( + key=secret, device=target_path, mapping=target_lv.lv_uuid) try: tag_tracker.update_tags_when_lv_create(self.create_type) @@ -584,7 +610,7 @@ class NewVolume(object): '--path', osd_path, '--dev-target', - target_lv.lv_path, + target_path, '--command', 'bluefs-bdev-new-{}'.format(self.create_type) ]) diff --git a/src/ceph-volume/ceph_volume/devices/lvm/prepare.py b/src/ceph-volume/ceph_volume/devices/lvm/prepare.py index 1cf19d98d92..85c8a146771 100644 --- a/src/ceph-volume/ceph_volume/devices/lvm/prepare.py +++ b/src/ceph-volume/ceph_volume/devices/lvm/prepare.py @@ -23,19 +23,7 @@ def prepare_dmcrypt(key, device, device_type, tags): return '' tag_name = 'ceph.%s_uuid' % device_type uuid = tags[tag_name] - # format data device - encryption_utils.luks_format( - key, - device - ) - encryption_utils.luks_open( - key, - device, - uuid - ) - - return '/dev/mapper/%s' % uuid - + return encryption_utils.prepare_dmcrypt(key, device, uuid) def prepare_bluestore(block, wal, db, secrets, tags, osd_id, fsid): """ diff --git a/src/ceph-volume/ceph_volume/util/encryption.py b/src/ceph-volume/ceph_volume/util/encryption.py index fdb73e1b1c8..f8aea80b493 100644 --- a/src/ceph-volume/ceph_volume/util/encryption.py +++ b/src/ceph-volume/ceph_volume/util/encryption.py @@ -106,20 +106,20 @@ def luks_open(key, device, mapping): process.call(command, stdin=key, terminal_verbose=True, show_command=True) -def dmcrypt_close(mapping): +def dmcrypt_close(mapping, skip_path_check=False): """ Encrypt (close) a device, previously decrypted with cryptsetup - :param mapping: + :param mapping: mapping name or path used to correlate device. + :param skip_path_check: whether we need path presence validation. """ - if not os.path.exists(mapping): + if not skip_path_check and not os.path.exists(mapping): logger.debug('device mapper path does not exist %s' % mapping) logger.debug('will skip cryptsetup removal') return # don't be strict about the remove call, but still warn on the terminal if it fails process.run(['cryptsetup', 'remove', mapping], stop_on_error=False) - def get_dmcrypt_key(osd_id, osd_fsid, lockbox_keyring=None): """ Retrieve the dmcrypt (secret) key stored initially on the monitor. The key @@ -273,3 +273,22 @@ def legacy_encrypted(device): metadata['lockbox'] = d.path break return metadata + +def prepare_dmcrypt(key, device, mapping): + """ + Helper for devices that are encrypted. The operations needed for + block, db, wal, or data/journal devices are all the same + """ + if not device: + return '' + # format data device + luks_format( + key, + device + ) + luks_open( + key, + device, + mapping + ) + return '/dev/mapper/%s' % mapping -- 2.39.5