]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume: support creating/migrating encrypted lvm volumes
authorIgor Fedotov <igor.fedotov@croit.io>
Tue, 7 Mar 2023 16:53:57 +0000 (19:53 +0300)
committerIgor Fedotov <igor.fedotov@croit.io>
Tue, 8 Aug 2023 09:53:42 +0000 (12:53 +0300)
Fixes: https://tracker.ceph.com/issues/55167
Signed-off-by: Igor Fedotov <igor.fedotov@croit.io>
(cherry picked from commit b45d9b79aa079f3cfe227c74c1df3a3bfad4c7c9)

src/ceph-volume/ceph_volume/devices/lvm/migrate.py
src/ceph-volume/ceph_volume/devices/lvm/prepare.py
src/ceph-volume/ceph_volume/util/encryption.py

index 86159fd505b777dd0a86c20865f8438629b19c3b..64589a2d62840232cbe0d1a8ff9fbcafd1a783a6 100644 (file)
@@ -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)
             ])
index 2f715fdba122c8a87fc2a097a34df4c66c0766a1..06e9a0ffad5191d65ce5bd6f79369885fbb60117 100644 (file)
@@ -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_filestore(device, journal, secrets, tags, osd_id, fsid):
     """
index 3310ab78c37f49a5a9144a3696904dc4a2098910..0831d859700580f1f21149553810ca23e22f5849 100644 (file)
@@ -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
@@ -278,3 +278,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