encryptors: Introduce support for LUKS2
LUKS2 support was introduced into cryptsetup 2.0.0 [1] and offers various improvements over the original format now referred to as LUKS1. This change introduces an encryptor to os-brick mostly using the existing LuksEncryptor class with the only difference being the `--type` switch supplied to cryptsetup when formatting a volume. As such the bulk of the _format_volume method from the original class has been extracted into a new _format_luks_volume method both the original and new Luks2Encryptor class can now reuse. [1] https://www.saout.de/pipermail/dm-crypt/2017-December/005771.html Change-Id: I09fb2b2be1e376f8ec0f49741c855cfd54ee27f0
This commit is contained in:
parent
80da84a09d
commit
6a01bacda7
os_brick
releasenotes/notes
@ -22,10 +22,12 @@ from oslo_utils import strutils
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
LUKS = "luks"
|
LUKS = "luks"
|
||||||
|
LUKS2 = "luks2"
|
||||||
PLAIN = "plain"
|
PLAIN = "plain"
|
||||||
|
|
||||||
FORMAT_TO_FRONTEND_ENCRYPTOR_MAP = {
|
FORMAT_TO_FRONTEND_ENCRYPTOR_MAP = {
|
||||||
LUKS: 'os_brick.encryptors.luks.LuksEncryptor',
|
LUKS: 'os_brick.encryptors.luks.LuksEncryptor',
|
||||||
|
LUKS2: 'os_brick.encryptors.luks.Luks2Encryptor',
|
||||||
PLAIN: 'os_brick.encryptors.cryptsetup.CryptsetupEncryptor'
|
PLAIN: 'os_brick.encryptors.cryptsetup.CryptsetupEncryptor'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,15 +61,29 @@ class LuksEncryptor(cryptsetup.CryptsetupEncryptor):
|
|||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
def _format_volume(self, passphrase, **kwargs):
|
def _format_volume(self, passphrase, **kwargs):
|
||||||
"""Creates a LUKS header on the volume.
|
"""Creates a LUKS v1 header on the volume.
|
||||||
|
|
||||||
:param passphrase: the passphrase used to access the volume
|
:param passphrase: the passphrase used to access the volume
|
||||||
"""
|
"""
|
||||||
|
self._format_luks_volume(passphrase, 'luks1', **kwargs)
|
||||||
|
|
||||||
|
def _format_luks_volume(self, passphrase, version, **kwargs):
|
||||||
|
"""Creates a LUKS header of a given version or type on the volume.
|
||||||
|
|
||||||
|
:param passphrase: the passphrase used to access the volume
|
||||||
|
:param version: the LUKS version or type to use: one of `luks`,
|
||||||
|
`luks1`, or `luks2`. Be aware that `luks` gives you
|
||||||
|
the default LUKS format preferred by the particular
|
||||||
|
cryptsetup being used (depends on version and compile
|
||||||
|
time parameters), which could be either LUKS1 or
|
||||||
|
LUKS2, so it's better to be specific about what you
|
||||||
|
want here
|
||||||
|
"""
|
||||||
LOG.debug("formatting encrypted volume %s", self.dev_path)
|
LOG.debug("formatting encrypted volume %s", self.dev_path)
|
||||||
|
|
||||||
# NOTE(joel-coffman): cryptsetup will strip trailing newlines from
|
# NOTE(joel-coffman): cryptsetup will strip trailing newlines from
|
||||||
# input specified on stdin unless --key-file=- is specified.
|
# input specified on stdin unless --key-file=- is specified.
|
||||||
cmd = ["cryptsetup", "--batch-mode", "luksFormat", "--type", "luks1",
|
cmd = ["cryptsetup", "--batch-mode", "luksFormat", "--type", version,
|
||||||
"--key-file=-"]
|
"--key-file=-"]
|
||||||
|
|
||||||
cipher = kwargs.get("cipher", None)
|
cipher = kwargs.get("cipher", None)
|
||||||
@ -191,3 +205,28 @@ class LuksEncryptor(cryptsetup.CryptsetupEncryptor):
|
|||||||
run_as_root=True, check_exit_code=[0, 4],
|
run_as_root=True, check_exit_code=[0, 4],
|
||||||
root_helper=self._root_helper,
|
root_helper=self._root_helper,
|
||||||
attempts=3)
|
attempts=3)
|
||||||
|
|
||||||
|
|
||||||
|
class Luks2Encryptor(LuksEncryptor):
|
||||||
|
"""A VolumeEncryptor based on LUKS v2.
|
||||||
|
|
||||||
|
This VolumeEncryptor uses dm-crypt to encrypt the specified volume.
|
||||||
|
"""
|
||||||
|
def __init__(self, root_helper,
|
||||||
|
connection_info,
|
||||||
|
keymgr,
|
||||||
|
execute=None,
|
||||||
|
*args, **kwargs):
|
||||||
|
super(Luks2Encryptor, self).__init__(
|
||||||
|
root_helper=root_helper,
|
||||||
|
connection_info=connection_info,
|
||||||
|
keymgr=keymgr,
|
||||||
|
execute=execute,
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
def _format_volume(self, passphrase, **kwargs):
|
||||||
|
"""Creates a LUKS v2 header on the volume.
|
||||||
|
|
||||||
|
:param passphrase: the passphrase used to access the volume
|
||||||
|
"""
|
||||||
|
self._format_luks_volume(passphrase, 'luks2', **kwargs)
|
||||||
|
@ -253,3 +253,62 @@ class LuksEncryptorTestCase(test_cryptsetup.CryptsetupEncryptorTestCase):
|
|||||||
check_exit_code=True),
|
check_exit_code=True),
|
||||||
], any_order=False)
|
], any_order=False)
|
||||||
self.assertEqual(9, mock_execute.call_count)
|
self.assertEqual(9, mock_execute.call_count)
|
||||||
|
|
||||||
|
|
||||||
|
class Luks2EncryptorTestCase(LuksEncryptorTestCase):
|
||||||
|
def _create(self):
|
||||||
|
return luks.Luks2Encryptor(root_helper=self.root_helper,
|
||||||
|
connection_info=self.connection_info,
|
||||||
|
keymgr=self.keymgr)
|
||||||
|
|
||||||
|
@mock.patch('os_brick.executor.Executor._execute')
|
||||||
|
def test__format_volume(self, mock_execute):
|
||||||
|
self.encryptor._format_volume("passphrase")
|
||||||
|
|
||||||
|
mock_execute.assert_has_calls([
|
||||||
|
mock.call('cryptsetup', '--batch-mode', 'luksFormat',
|
||||||
|
'--type', 'luks2', '--key-file=-', self.dev_path,
|
||||||
|
process_input='passphrase',
|
||||||
|
root_helper=self.root_helper,
|
||||||
|
run_as_root=True, check_exit_code=True, attempts=3),
|
||||||
|
])
|
||||||
|
|
||||||
|
@mock.patch('os_brick.executor.Executor._execute')
|
||||||
|
def test_attach_volume_not_formatted(self, mock_execute):
|
||||||
|
fake_key = 'bc37c5eccebe403f9cc2d0dd20dac2bc'
|
||||||
|
self.encryptor._get_key = mock.MagicMock()
|
||||||
|
self.encryptor._get_key.return_value = (
|
||||||
|
test_cryptsetup.fake__get_key(None, fake_key))
|
||||||
|
|
||||||
|
mock_execute.side_effect = [
|
||||||
|
putils.ProcessExecutionError(exit_code=1), # luksOpen
|
||||||
|
putils.ProcessExecutionError(exit_code=1), # isLuks
|
||||||
|
mock.DEFAULT, # luksFormat
|
||||||
|
mock.DEFAULT, # luksOpen
|
||||||
|
mock.DEFAULT, # ln
|
||||||
|
]
|
||||||
|
|
||||||
|
self.encryptor.attach_volume(None)
|
||||||
|
|
||||||
|
mock_execute.assert_has_calls([
|
||||||
|
mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
|
||||||
|
self.dev_name, process_input=fake_key,
|
||||||
|
root_helper=self.root_helper,
|
||||||
|
run_as_root=True, check_exit_code=True),
|
||||||
|
mock.call('cryptsetup', 'isLuks', '--verbose', self.dev_path,
|
||||||
|
root_helper=self.root_helper,
|
||||||
|
run_as_root=True, check_exit_code=True),
|
||||||
|
mock.call('cryptsetup', '--batch-mode', 'luksFormat',
|
||||||
|
'--type', 'luks2', '--key-file=-', self.dev_path,
|
||||||
|
process_input=fake_key,
|
||||||
|
root_helper=self.root_helper,
|
||||||
|
run_as_root=True, check_exit_code=True, attempts=3),
|
||||||
|
mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
|
||||||
|
self.dev_name, process_input=fake_key,
|
||||||
|
root_helper=self.root_helper,
|
||||||
|
run_as_root=True, check_exit_code=True),
|
||||||
|
mock.call('ln', '--symbolic', '--force',
|
||||||
|
'/dev/mapper/%s' % self.dev_name, self.symlink_path,
|
||||||
|
root_helper=self.root_helper,
|
||||||
|
run_as_root=True, check_exit_code=True),
|
||||||
|
], any_order=False)
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A LUKS2 encryptor has been introduced providing support for this latest
|
||||||
|
version of the Linux Unified Key Setup disk encryption format. This
|
||||||
|
requires ``cryptsetup`` version 2.0.0 or greater.
|
Loading…
Reference in New Issue
Block a user