]>
git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/util/encryption.py
2a2c03337b61f0db47d34c6349435f11f6c16f0b
4 from ceph_volume
import process
, conf
5 from ceph_volume
.util
import constants
, system
6 from ceph_volume
.util
.device
import Device
7 from .prepare
import write_keyring
8 from .disk
import lsblk
, device_family
, get_part_entry_type
10 logger
= logging
.getLogger(__name__
)
12 def get_key_size_from_conf():
14 Return the osd dmcrypt key size from config file.
17 default_key_size
= '512'
18 key_size
= conf
.ceph
.get_safe(
20 'osd_dmcrypt_key_size',
23 if key_size
not in ['256', '512']:
24 logger
.warning(("Invalid value set for osd_dmcrypt_key_size ({}). "
25 "Falling back to {}bits".format(key_size
, default_key_size
)))
26 return default_key_size
30 def create_dmcrypt_key():
32 Create the secret dm-crypt key (KEK) used to encrypt/decrypt the Volume Key.
34 random_string
= os
.urandom(128)
35 key
= base64
.b64encode(random_string
).decode('utf-8')
39 def luks_format(key
, device
):
41 Decrypt (open) an encrypted device, previously prepared with cryptsetup
43 :param key: dmcrypt secret key, will be used for decrypting
44 :param device: Absolute path to device
48 '--batch-mode', # do not prompt
50 get_key_size_from_conf(),
51 '--key-file', # misnomer, should be key
52 '-', # because we indicate stdin for the key here
56 process
.call(command
, stdin
=key
, terminal_verbose
=True, show_command
=True)
59 def plain_open(key
, device
, mapping
):
61 Decrypt (open) an encrypted device, previously prepared with cryptsetup in plain mode
63 .. note: ceph-disk will require an additional b64decode call for this to work
65 :param key: dmcrypt secret key
66 :param device: absolute path to device
67 :param mapping: mapping name used to correlate device. Usually a UUID
73 '--allow-discards', # allow discards (aka TRIM) requests for device
81 process
.call(command
, stdin
=key
, terminal_verbose
=True, show_command
=True)
84 def luks_open(key
, device
, mapping
):
86 Decrypt (open) an encrypted device, previously prepared with cryptsetup
88 .. note: ceph-disk will require an additional b64decode call for this to work
90 :param key: dmcrypt secret key
91 :param device: absolute path to device
92 :param mapping: mapping name used to correlate device. Usually a UUID
97 get_key_size_from_conf(),
100 '--allow-discards', # allow discards (aka TRIM) requests for device
105 process
.call(command
, stdin
=key
, terminal_verbose
=True, show_command
=True)
108 def dmcrypt_close(mapping
):
110 Encrypt (close) a device, previously decrypted with cryptsetup
114 if not os
.path
.exists(mapping
):
115 logger
.debug('device mapper path does not exist %s' % mapping
)
116 logger
.debug('will skip cryptsetup removal')
118 # don't be strict about the remove call, but still warn on the terminal if it fails
119 process
.run(['cryptsetup', 'remove', mapping
], stop_on_error
=False)
122 def get_dmcrypt_key(osd_id
, osd_fsid
, lockbox_keyring
=None):
124 Retrieve the dmcrypt (secret) key stored initially on the monitor. The key
125 is sent initially with JSON, and the Monitor then mangles the name to
126 ``dm-crypt/osd/<fsid>/luks``
128 The ``lockbox.keyring`` file is required for this operation, and it is
129 assumed it will exist on the path for the same OSD that is being activated.
130 To support scanning, it is optionally configurable to a custom location
131 (e.g. inside a lockbox partition mounted in a temporary location)
133 if lockbox_keyring
is None:
134 lockbox_keyring
= '/var/lib/ceph/osd/%s-%s/lockbox.keyring' % (conf
.cluster
, osd_id
)
135 name
= 'client.osd-lockbox.%s' % osd_fsid
136 config_key
= 'dm-crypt/osd/%s/luks' % osd_fsid
138 stdout
, stderr
, returncode
= process
.call(
141 '--cluster', conf
.cluster
,
143 '--keyring', lockbox_keyring
,
151 raise RuntimeError('Unable to retrieve dmcrypt secret')
152 return ' '.join(stdout
).strip()
155 def write_lockbox_keyring(osd_id
, osd_fsid
, secret
):
157 Helper to write the lockbox keyring. This is needed because the bluestore OSD will
158 not persist the keyring, and it can't be stored in the data device for filestore because
159 at the time this is needed, the device is encrypted.
161 For bluestore: A tmpfs filesystem is mounted, so the path can get written
162 to, but the files are ephemeral, which requires this file to be created
163 every time it is activated.
164 For filestore: The path for the OSD would exist at this point even if no
165 OSD data device is mounted, so the keyring is written to fetch the key, and
166 then the data device is mounted on that directory, making the keyring
169 if os
.path
.exists('/var/lib/ceph/osd/%s-%s/lockbox.keyring' % (conf
.cluster
, osd_id
)):
172 name
= 'client.osd-lockbox.%s' % osd_fsid
176 keyring_name
='lockbox.keyring',
183 Capture the metadata information of a possibly encrypted device, returning
184 a dictionary with all the values found (if any).
186 An encrypted device will contain information about a device. Example
187 successful output looks like::
189 $ cryptsetup status /dev/mapper/ed6b5a26-eafe-4cd4-87e3-422ff61e26c4
190 /dev/mapper/ed6b5a26-eafe-4cd4-87e3-422ff61e26c4 is active and is in use.
192 cipher: aes-xts-plain64
196 size: 20740063 sectors
199 As long as the mapper device is in 'open' state, the ``status`` call will work.
201 :param device: Absolute path or UUID of the device mapper
208 out
, err
, code
= process
.call(command
, show_command
=True, verbose_on_failure
=False)
212 logger
.warning('failed to detect device mapper information')
215 # get rid of lines that might not be useful to construct the report:
216 if not line
.startswith(' '):
219 column
, value
= line
.split(': ')
222 metadata
[column
.strip()] = value
.strip().strip('"')
226 def legacy_encrypted(device
):
228 Detect if a device was encrypted with ceph-disk or not. In the case of
229 encrypted devices, include the type of encryption (LUKS, or PLAIN), and
230 infer what the lockbox partition is.
232 This function assumes that ``device`` will be a partition.
234 if os
.path
.isdir(device
):
235 mounts
= system
.get_mounts(paths
=True)
236 # yes, rebind the device variable here because a directory isn't going
237 # to help with parsing
238 device
= mounts
.get(device
, [None])[0]
240 raise RuntimeError('unable to determine the device mounted at %s' % device
)
241 metadata
= {'encrypted': False, 'type': None, 'lockbox': '', 'device': device
}
242 # check if the device is online/decrypted first
243 active_mapper
= status(device
)
245 # normalize a bit to ensure same values regardless of source
246 metadata
['type'] = active_mapper
['type'].lower().strip('12') # turn LUKS1 or LUKS2 into luks
247 metadata
['encrypted'] = True if metadata
['type'] in ['plain', 'luks'] else False
248 # The true device is now available to this function, so it gets
249 # re-assigned here for the lockbox checks to succeed (it is not
250 # possible to guess partitions from a device mapper device otherwise
251 device
= active_mapper
.get('device', device
)
252 metadata
['device'] = device
254 uuid
= get_part_entry_type(device
)
255 guid_match
= constants
.ceph_disk_guids
.get(uuid
, {})
256 encrypted_guid
= guid_match
.get('encrypted', False)
258 metadata
['encrypted'] = True
259 metadata
['type'] = guid_match
['encryption_type']
261 # Lets find the lockbox location now, to do this, we need to find out the
262 # parent device name for the device so that we can query all of its
263 # associated devices and *then* look for one that has the 'lockbox' label
264 # on it. Thanks for being awesome ceph-disk
265 disk_meta
= lsblk(device
, abspath
=True)
268 parent_device
= disk_meta
['PKNAME']
269 # With the parent device set, we can now look for the lockbox listing associated devices
270 devices
= [Device(i
['NAME']) for i
in device_family(parent_device
)]
272 if d
.ceph_disk
.type == 'lockbox':
273 metadata
['lockbox'] = d
.abspath