Skip to content

Commit

Permalink
Add random key support for LUKS encryption
Browse files Browse the repository at this point in the history
Allow to pass luks="random". In random mode use the
generated keyfile as the only key to decrypt. This is
only secure if the generated initrd also gets protected
e.g. through encryption like it is done with the secure
linux execution on zSystems
  • Loading branch information
schaefi committed Nov 21, 2024
1 parent 1d9f2d7 commit 43cd86d
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 13 deletions.
31 changes: 25 additions & 6 deletions doc/source/image_description/elements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -566,15 +566,34 @@ root_clone="number"
boot_clone="number"
Same as `root_clone` but applied to the boot partition if present

luks="passphrase|file:///path/to/keyfile":
luks="passphrase|file:///path/to/keyfile|random":
Supplying a value will trigger the encryption of the partition
serving the root filesystem using the LUKS extension. The supplied
value represents either the passphrase string or the location of
a key file if specified as `file://...` resource. When using
a key file it is in the responsibility of the user how
this key file is actually being used. By default any
distribution will just open an interactive dialog asking
for the credentials at boot time !
a key file if specified as `file://...` resource or the reserved
name `random`. When using a passphrase the system will interactively
ask for that passphrase on first boot unless it is set empty.
In case of an empty passphrase the system cannot be considered secure.
When using a key file the information from the file is read and
used as a passphrase. The given key file is **not automatically**
placed into the system or added to the `etc/crypttab` which means
the passphrase in the key file is by default requested from an
interactive dialog at boot time. When using the reserved word
`random`, kiwi will create a key file with a random passphrase
and place this information into `etc/crypttab`. This allows
the system to boot without user interaction but also requires
the initrd to be protected in some way because it will contain
the keyfile. The use of `random` is therefore only secure if
the image adds additional security that encrypts the initrd
like it is e.g. done in the IBM secure execution process.
If the encryption of the system is combined with the attribute
`bootpartition="false"` it's important to understand that this
will place `/boot` into the encrypted area of the system and
leaves reading boot data from it as a responsibility to the
bootloader. Not every bootloader can cope with that and those
that can e.g. grub will then open an interactive dialog at
the bootloader level asking for the credentials to decrypt the
root filesystem.

luks_version="luks|luks1|luks2":
Specify which `LUKS` version should be used. If not set and by
Expand Down
2 changes: 1 addition & 1 deletion kiwi/builder/disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ def _map_luks(
# LUKS pool without asking
#
luks_need_keyfile = \
True if self.boot_is_crypto or self.luks == '' else False
True if self.boot_is_crypto or self.luks == '' or self.luks == 'random' else False
luks_root.create_crypto_luks(
passphrase=self.luks or '',
osname=self.luks_os,
Expand Down
29 changes: 23 additions & 6 deletions kiwi/storage/luks_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ def create_crypto_luks(
if not passphrase:
log.warning('Using an empty passphrase for the key setup')

if keyfile:
self.luks_keyfile = keyfile
keyfile_path = os.path.normpath(
os.sep.join([root_dir, self.luks_keyfile])
)
LuksDevice.create_random_keyfile(keyfile_path)

if randomize:
log.info('--> Randomizing...')
storage_size_mbytes = self.storage_provider.get_byte_size(
Expand All @@ -123,12 +130,26 @@ def create_crypto_luks(

log.info('--> Creating LUKS map')

if passphrase:
if passphrase and passphrase == 'random':
# In random mode use the generated keyfile as the only
# key to decrypt. This is only secure if the generated
# initrd also gets protected, e.g through encryption
# like it is done with the secure linux execution on
# zSystems
passphrase_file = keyfile_path
# Do not add an additional keyfile
keyfile = ''
elif passphrase:
# Setup a passphrase file for which the system will
# ask for in an interactive dialog
passphrase_file_tmp = Temporary().new_file()
with open(passphrase_file_tmp.name, 'w') as credentials:
credentials.write(passphrase)
passphrase_file = passphrase_file_tmp.name
else:
# Setup an empty passphrase, insecure and only useful
# for initial deployment which then applies a process
# to secure the image e.g reencrypt
passphrase_file_zero = '/dev/zero'
extra_options = [
'--keyfile-size', '32'
Expand All @@ -142,12 +163,8 @@ def create_crypto_luks(
'luksFormat', storage_device
]
)

if keyfile:
self.luks_keyfile = keyfile
keyfile_path = os.path.normpath(
os.sep.join([root_dir, self.luks_keyfile])
)
LuksDevice.create_random_keyfile(keyfile_path)
Command.run(
[
'cryptsetup', '--key-file', passphrase_file
Expand Down
43 changes: 43 additions & 0 deletions test/unit/storage/luks_device_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,49 @@ def test_create_crypto_luks_empty_passphrase(
]
self.luks.luks_device = None

@patch('kiwi.storage.luks_device.LuksDevice')
@patch('kiwi.storage.luks_device.Command.run')
@patch('kiwi.storage.luks_device.Temporary.new_file')
@patch('os.chmod')
def test_create_crypto_luks_random_passphrase(
self, mock_os_chmod, mock_tmpfile, mock_command, mock_LuksDevice
):
tmpfile = Mock()
tmpfile.name = 'tmpfile'
mock_tmpfile.return_value = tmpfile
with patch('builtins.open', create=True):
self.luks.create_crypto_luks(
passphrase='random', osname='sle12',
keyfile='some-keyfile', root_dir='root'
)
assert mock_command.call_args_list == [
call(
[
'dd', 'if=/dev/urandom', 'bs=1M', 'count=1',
'of=/dev/some-device'
]
),
call(
[
'cryptsetup', '-q', '--key-file', 'root/some-keyfile',
'--cipher', 'aes-xts-plain64',
'--key-size', '256', '--hash', 'sha1',
'luksFormat', '/dev/some-device'
]
),
call(
[
'cryptsetup', '--key-file', 'root/some-keyfile', 'luksOpen',
'/dev/some-device', 'luksRoot'
]
)
]
mock_LuksDevice.create_random_keyfile.assert_called_once_with(
'root/some-keyfile'
)
assert self.luks.luks_keyfile == 'some-keyfile'
self.luks.luks_device = ''

@patch('kiwi.storage.luks_device.LuksDevice')
@patch('kiwi.storage.luks_device.Command.run')
@patch('kiwi.storage.luks_device.Temporary.new_file')
Expand Down

0 comments on commit 43cd86d

Please sign in to comment.