]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume: add dmcrypt support in raw mode
authorGuillaume Abrioux <gabrioux@redhat.com>
Fri, 24 Apr 2020 14:45:02 +0000 (16:45 +0200)
committerGuillaume Abrioux <gabrioux@redhat.com>
Mon, 29 Jun 2020 11:44:49 +0000 (11:44 +0000)
This commit adds the dmcrypt support in `ceph-volume raw` mode.

Note about `ceph-volume raw list` change:
Given `lsblk -J` (json output) option isn't available on all OS, I came up with
adding '--inverse' option to the existing command which allows us to get the
mapper devices list in that command output. Not listing root devices containing
partitions shouldn't have side effect since we are in `ceph-volume raw`
context.

example:
running `lsblk --paths --nodeps --output=NAME --noheadings` doesn't allow to
get the mapper list because the output is like following :

$ lsblk --paths --nodeps --output=NAME --noheadings
/dev/sda
/dev/sdb
/dev/sdc
/dev/sdd

the dmcrypt mappers are hidden because of the `--nodeps` given they are
displayed as a dependency.

$ lsblk --paths --output=NAME --noheadings
/dev/sda
|-/dev/mapper/ceph-3b52c90d-6548-407d-bde1-efd31809702f-sda-block-dmcrypt
`-/dev/mapper/ceph-3b52c90d-6548-407d-bde1-efd31809702f-sda-db-dmcrypt
/dev/sdb
/dev/sdc
/dev/sdd

adding `--inverse` is a trick to get around this issue, the counterpart is that
we can't list root devices if they contain at least one partition but this
shouldn't be an issue in `ceph-volume raw` context given we only deal with
raw devices.

Fixes: https://tracker.ceph.com/issues/45959
Signed-off-by: Guillaume Abrioux <gabrioux@redhat.com>
(cherry picked from commit 159c30d29f009c07e51a4bd7cd5ecea8cf6c8739)

src/ceph-volume/ceph_volume/devices/raw/common.py
src/ceph-volume/ceph_volume/devices/raw/list.py
src/ceph-volume/ceph_volume/devices/raw/prepare.py

index d34a2941d1684cc70ec1e5ab9ebf5dc4588a30ad..197af3cc848f8a79ccbca9248a746ec06d57013a 100644 (file)
@@ -45,4 +45,9 @@ def create_parser(prog, description):
         dest='block_wal',
         help='Path to bluestore block.wal block device'
     )
+    parser.add_argument(
+        '--dmcrypt',
+        action='store_true',
+        help='Enable device encryption via dm-crypt',
+    )
     return parser
index b04f55cd8da0abbe7c006e556feab1055a32b23b..bb15bf199012bb52dff50262247c78c31906ca02 100644 (file)
@@ -30,8 +30,34 @@ class List(object):
         if not devs:
             logger.debug('Listing block devices via lsblk...')
             devs = []
+            # adding '--inverse' allows us to get the mapper devices list in that command output.
+            # not listing root devices containing partitions shouldn't have side effect since we are
+            # in `ceph-volume raw` context.
+            #
+            #   example:
+            #   running `lsblk --paths --nodeps --output=NAME --noheadings` doesn't allow to get the mapper list
+            #   because the output is like following :
+            #
+            #   $ lsblk --paths --nodeps --output=NAME --noheadings
+            #   /dev/sda
+            #   /dev/sdb
+            #   /dev/sdc
+            #   /dev/sdd
+            #
+            #   the dmcrypt mappers are hidden because of the `--nodeps` given they are displayed as a dependency.
+            #
+            #   $ lsblk --paths --output=NAME --noheadings
+            #   /dev/sda
+            #   |-/dev/mapper/ceph-3b52c90d-6548-407d-bde1-efd31809702f-sda-block-dmcrypt
+            #   `-/dev/mapper/ceph-3b52c90d-6548-407d-bde1-efd31809702f-sda-db-dmcrypt
+            #   /dev/sdb
+            #   /dev/sdc
+            #   /dev/sdd
+            #
+            #   adding `--inverse` is a trick to get around this issue, the counterpart is that we can't list root devices if they contain
+            #   at least one partition but this shouldn't be an issue in `ceph-volume raw` context given we only deal with raw devices.
             out, err, ret = process.call([
-                'lsblk', '--paths', '--nodeps', '--output=NAME', '--noheadings'
+                'lsblk', '--paths', '--nodeps', '--output=NAME', '--noheadings', '--inverse'
             ])
             assert not ret
             devs = out
index cb5c59ce40f5315946a9b1d0d2e3686793a7ec07..d0d740c1c84b0acb315f5e40def429431d6366ec 100644 (file)
@@ -1,8 +1,11 @@
 from __future__ import print_function
 import json
 import logging
+import os
 from textwrap import dedent
 from ceph_volume.util import prepare as prepare_utils
+from ceph_volume.util import encryption as encryption_utils
+from ceph_volume.util import disk
 from ceph_volume.util import system
 from ceph_volume import conf, decorators, terminal
 from ceph_volume.devices.lvm.common import rollback_osd
@@ -10,6 +13,27 @@ from .common import create_parser
 
 logger = logging.getLogger(__name__)
 
+def prepare_dmcrypt(key, device, device_type, fsid):
+    """
+    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 ''
+    kname = disk.lsblk(device)['KNAME']
+    mapping = 'ceph-{}-{}-{}-dmcrypt'.format(fsid, kname, device_type)
+    # format data device
+    encryption_utils.luks_format(
+        key,
+        device
+    )
+    encryption_utils.luks_open(
+        key,
+        device,
+        mapping
+    )
+
+    return '/dev/mapper/{}'.format(mapping)
 
 def prepare_bluestore(block, wal, db, secrets, osd_id, fsid, tmpfs):
     """
@@ -22,6 +46,12 @@ def prepare_bluestore(block, wal, db, secrets, osd_id, fsid, tmpfs):
     """
     cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key())
 
+    if secrets.get('dmcrypt_key'):
+        key = secrets['dmcrypt_key']
+        block = prepare_dmcrypt(key, block, 'block', fsid)
+        wal = prepare_dmcrypt(key, wal, 'wal', fsid)
+        db = prepare_dmcrypt(key, db, 'db', fsid)
+
     # create the directory
     prepare_utils.create_osd_path(osd_id, tmpfs=tmpfs)
     # symlink the block
@@ -64,7 +94,8 @@ class Prepare(object):
             logger.info('will rollback OSD ID creation')
             rollback_osd(self.args, self.osd_id)
             raise
-        terminal.success("ceph-volume raw prepare successful for: %s" % self.args.data)
+        dmcrypt_log = 'dmcrypt' if args.dmcrypt else 'clear'
+        terminal.success("ceph-volume raw {} prepare successful for: {}".format(dmcrypt_log, self.args.data))
 
     def get_cluster_fsid(self):
         """
@@ -79,6 +110,13 @@ class Prepare(object):
     @decorators.needs_root
     def prepare(self):
         secrets = {'cephx_secret': prepare_utils.create_key()}
+        encrypted = 1 if self.args.dmcrypt else 0
+        cephx_lockbox_secret = '' if not encrypted else prepare_utils.create_key()
+
+        if encrypted:
+            secrets['dmcrypt_key'] = os.getenv('CEPH_VOLUME_DMCRYPT_SECRET')
+            secrets['cephx_lockbox_secret'] = cephx_lockbox_secret # dummy value to make `ceph osd new` not complaining
+
         osd_fsid = system.generate_uuid()
         crush_device_class = self.args.crush_device_class
         if crush_device_class:
@@ -94,6 +132,7 @@ class Prepare(object):
         # reuse a given ID if it exists, otherwise create a new ID
         self.osd_id = prepare_utils.create_id(
             osd_fsid, json.dumps(secrets))
+
         prepare_bluestore(
             self.args.data,
             wal,
@@ -112,8 +151,6 @@ class Prepare(object):
         Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
         it can later get activated and the OSD daemon can get started.
 
-        Encryption is not supported.
-
             ceph-volume raw prepare --bluestore --data {device}
 
         DB and WAL devices are supported.
@@ -132,5 +169,10 @@ class Prepare(object):
         if not self.args.bluestore:
             terminal.error('must specify --bluestore (currently the only supported backend)')
             raise SystemExit(1)
+        if self.args.dmcrypt and not os.getenv('CEPH_VOLUME_DMCRYPT_SECRET'):
+            terminal.error('encryption was requested (--dmcrypt) but environment variable ' \
+                           'CEPH_VOLUME_DMCRYPT_SECRET is not set, you must set ' \
+                           'this variable to provide a dmcrypt secret.')
+            raise SystemExit(1)
 
         self.safe_prepare(self.args)