Skip to content

Commit 8c82847

Browse files
authored
Merge pull request #2703 from OSInside/re_encrypt_support
Added LUKS reencryption support
2 parents 5d0419c + 2252087 commit 8c82847

File tree

13 files changed

+266
-17
lines changed

13 files changed

+266
-17
lines changed

build-tests/x86/tumbleweed/test-image-luks/appliance.kiwi

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
2+
<!-- OBS-Profiles: @BUILD_FLAVOR@ -->
33
<image schemaversion="7.5" name="kiwi-test-image-luks">
44
<description type="system">
55
<author>Marcus Schäfer</author>
66
<contact>[email protected]</contact>
77
<specification>Disk full encryption disk test build</specification>
88
</description>
9+
<profiles>
10+
<profile name="Insecure" description="Encrypted no reencryption"/>
11+
<profile name="ReEncryptExtraBootEmptyPass" description="Run reencryption with extra boot partition and empty passphrase"/>
12+
<profile name="ReEncryptExtraBootWithPass" description="Run reencryption with extra boot partition and passphrase"/>
13+
<profile name="ReEncryptFullDisk" description="Run full disk reencryption with passphrase"/>
14+
</profiles>
915
<preferences>
1016
<version>1.15.1</version>
1117
<packagemanager>zypper</packagemanager>
@@ -16,13 +22,51 @@
1622
<rpm-check-signatures>false</rpm-check-signatures>
1723
<bootsplash-theme>breeze</bootsplash-theme>
1824
<bootloader-theme>openSUSE</bootloader-theme>
25+
</preferences>
26+
<preferences profiles="Insecure">
1927
<type image="oem" filesystem="ext4" kernelcmdline="console=ttyS0" firmware="uefi" luks="linux" luks_version="luks2" luks_pbkdf="pbkdf2" bootpartition="false">
2028
<luksformat>
2129
<option name="--cipher" value="aes-xts-plain64"/>
2230
<option name="--key-size" value="256"/>
2331
</luksformat>
2432
<oemconfig>
25-
<oem-resize>false</oem-resize>
33+
<oem-resize>true</oem-resize>
34+
</oemconfig>
35+
<bootloader name="grub2" console="serial" timeout="10"/>
36+
</type>
37+
</preferences>
38+
<preferences profiles="ReEncryptExtraBootEmptyPass">
39+
<type image="oem" filesystem="ext4" kernelcmdline="console=ttyS0 rd.kiwi.oem.luks.reencrypt" firmware="uefi" luks="" luks_version="luks2" luks_pbkdf="pbkdf2" bootpartition="true">
40+
<luksformat>
41+
<option name="--cipher" value="aes-xts-plain64"/>
42+
<option name="--key-size" value="256"/>
43+
</luksformat>
44+
<oemconfig>
45+
<oem-resize>true</oem-resize>
46+
</oemconfig>
47+
<bootloader name="grub2" console="serial" timeout="10"/>
48+
</type>
49+
</preferences>
50+
<preferences profiles="ReEncryptExtraBootWithPass">
51+
<type image="oem" filesystem="ext4" kernelcmdline="console=ttyS0 rd.kiwi.oem.luks.reencrypt" firmware="uefi" luks="linux" luks_version="luks2" luks_pbkdf="pbkdf2" bootpartition="true">
52+
<luksformat>
53+
<option name="--cipher" value="aes-xts-plain64"/>
54+
<option name="--key-size" value="256"/>
55+
</luksformat>
56+
<oemconfig>
57+
<oem-resize>true</oem-resize>
58+
</oemconfig>
59+
<bootloader name="grub2" console="serial" timeout="10"/>
60+
</type>
61+
</preferences>
62+
<preferences profiles="ReEncryptFullDisk">
63+
<type image="oem" filesystem="ext4" kernelcmdline="console=ttyS0 rd.kiwi.oem.luks.reencrypt" firmware="uefi" luks="linux" luks_version="luks2" luks_pbkdf="pbkdf2" bootpartition="false">
64+
<luksformat>
65+
<option name="--cipher" value="aes-xts-plain64"/>
66+
<option name="--key-size" value="256"/>
67+
</luksformat>
68+
<oemconfig>
69+
<oem-resize>true</oem-resize>
2670
</oemconfig>
2771
<bootloader name="grub2" console="serial" timeout="10"/>
2872
</type>
@@ -61,6 +105,7 @@
61105
<package name="shim"/>
62106
<package name="timezone"/>
63107
<package name="cryptsetup"/>
108+
<package name="dracut-kiwi-oem-repart"/>
64109
</packages>
65110
<packages type="bootstrap">
66111
<package name="gawk"/>

build-tests/x86/tumbleweed/test-image-luks/config.sh

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
# :
1616
# STATUS : BETA
1717
#----------------
18+
declare kiwi_iname=${kiwi_iname}
19+
declare kiwi_profiles=${kiwi_profiles}
20+
1821
#======================================
1922
# Functions...
2023
#--------------------------------------
24+
# shellcheck disable=SC1091
2125
test -f /.kconfig && . /.kconfig
22-
test -f /.profile && . /.profile
2326

2427
#======================================
2528
# Greeting...
@@ -46,3 +49,32 @@ baseSetRunlevel 3
4649
#------------------------------------------
4750
rm -rf /usr/share/doc/packages/*
4851
rm -rf /usr/share/doc/manual/*
52+
53+
54+
# For image tests with an extra boot partition the
55+
# kernel must not be a symlink to another area of
56+
# the filesystem. Latest changes on SUSE changed the
57+
# layout of the kernel which breaks every image with
58+
# an extra boot partition
59+
#
60+
# All of the following is more than a hack and I
61+
# don't like it all
62+
#
63+
# Complains and discussions about this please with
64+
# the SUSE kernel team as we in kiwi can just live
65+
# with the consequences of this change
66+
#
67+
for profile in ${kiwi_profiles//,/ }; do
68+
if [ "${profile}" = "ReEncryptExtraBootEmptyPass" ] || [ "${profile}" = "ReEncryptExtraBootWithPass" ]; then
69+
pushd /
70+
71+
for file in /boot/* /boot/.*; do
72+
if [ -L "${file}" ];then
73+
link_target=$(readlink "${file}")
74+
if [[ "${link_target}" =~ usr/lib/modules ]];then
75+
mv "${link_target}" "${file}"
76+
fi
77+
fi
78+
done
79+
fi
80+
done

doc/source/concept_and_workflow/customize_the_boot_process.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ the available kernel boot parameters for these modules:
209209
Note that options starting with `rd.kiwi` are not passed to avoid
210210
side effects.
211211

212+
``rd.kiwi.oem.luks.reencrypt``
213+
For OEM LUKS2 encrypted disk images. If set, reencrypts the disk
214+
prior an eventual resize and therefore creates a new key pool and
215+
master key. The reencryption is advisable if the image binary is
216+
not protected. With access to the image binary it's possible to
217+
extract the luks header which then allows to decrypt the data
218+
unless it was reencrypted. The reencryption process only runs if
219+
the checksum of the luks header still matches the one from the
220+
original disk image. Be aware that the reencryption will ask
221+
for the passphrase if the image has been built with an initial
222+
luks passphrase.
223+
212224
``rd.kiwi.oem.maxdisk=size[KMGT]``
213225
Specifies the maximum disk size an unattended OEM installation uses for image
214226
deployment. Unattended OEM deployments default to deploying on `/dev/sda` (or

dracut/modules.d/90kiwi-repart/kiwi-repart-disk.sh

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ function initialize {
2424
test -f ${profile} && import_file ${profile}
2525
import_file ${partition_ids}
2626

27+
# Optional env TERM setup
28+
term=$(getarg "rd.kiwi.term=")
29+
[ -n "${term}" ] && export TERM="${term}"
30+
31+
# Create dialog profile from current env
32+
# Used in the systemd dialog unit
33+
env >/dialog_profile
34+
2735
disk=$(lookup_disk_device_from_root)
2836
export disk
2937

@@ -248,6 +256,16 @@ mask_fsck_root_service
248256
# initialize for disk repartition
249257
initialize
250258

259+
# reencrypt luks device
260+
if luks_system "${disk}";then
261+
if getargbool 0 rd.kiwi.oem.luks.reencrypt; then
262+
reencrypt_luks "${disk}"
263+
fi
264+
fi
265+
266+
# wait for the root device to appear
267+
wait_for_storage_device "${root_device}"
268+
251269
# check if repart/resize is wanted
252270
if ! resize_wanted "${root_device}" "${disk}"; then
253271
return
@@ -258,9 +276,6 @@ if [ "$(get_partition_table_type "${disk}")" = 'gpt' ];then
258276
relocate_gpt_at_end_of_disk "${disk}"
259277
fi
260278

261-
# wait for the root device to appear
262-
wait_for_storage_device "${root_device}"
263-
264279
# resize disk partition table
265280
if lvm_system;then
266281
repart_lvm_disk || return

dracut/modules.d/99kiwi-lib/kiwi-dialog-lib.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,8 @@ function ask_and_shutdown {
165165
systemctl halt
166166
fi
167167
}
168+
169+
function ask_for_credentials {
170+
local text_message="$1"
171+
run_dialog --insecure --passwordbox "\"${text_message}\"" 7 60
172+
}

dracut/modules.d/99kiwi-lib/kiwi-luks-lib.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#!/bin/bash
22

3+
# shellcheck disable=SC1091
34
type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh
45
type set_root_map >/dev/null 2>&1 || . /lib/kiwi-lib.sh
56
type wait_for_storage_device >/dev/null 2>&1 || . /lib/kiwi-partitions-lib.sh
67
type ask_for_password >/dev/null 2>&1 || . /lib/dracut-crypt-lib.sh
8+
type run_progress_dialog >/dev/null 2>&1 || . /lib/kiwi-dialog-lib.sh
79

810
function luks_system {
911
declare kiwi_RootPart=${kiwi_RootPart}
@@ -20,6 +22,60 @@ function deactivate_luks {
2022
/usr/lib/systemd/systemd-cryptsetup detach luks
2123
}
2224

25+
function reencrypt_luks {
26+
declare kiwi_RootPart=${kiwi_RootPart}
27+
local disk=$1
28+
local header_checksum_origin=/root/.luks.header
29+
local header_checksum_cur=/root/.luks.header.cur
30+
local keyfile=/root/.root.keyfile
31+
local passphrase_file=/root/.slot0
32+
local progress=/dev/install_progress
33+
local load_text="Reencrypting..."
34+
local title_text="LUKS"
35+
local device
36+
device=$(get_partition_node_name "${disk}" "${kiwi_RootPart}")
37+
read -r header_checksum_origin < "${header_checksum_origin}"
38+
if [ "${kiwi_luks_empty_passphrase}" = "true" ];then
39+
cryptsetup \
40+
--key-file /dev/zero \
41+
--keyfile-size 32 \
42+
luksHeaderBackup "${device}" \
43+
--header-backup-file "${header_checksum_cur}"
44+
else
45+
cryptsetup \
46+
--key-file "${keyfile}" \
47+
luksHeaderBackup "${device}" \
48+
--header-backup-file "${header_checksum_cur}"
49+
fi
50+
header_checksum_cur=$(
51+
sha256sum "${header_checksum_cur}" |\
52+
cut -f1 -d" "; rm -f "${header_checksum_cur}"
53+
)
54+
if [ "${header_checksum_origin}" == "${header_checksum_cur}" ];then
55+
if [ "${kiwi_luks_empty_passphrase}" = "true" ];then
56+
echo -n > "${passphrase_file}"
57+
else
58+
ask_for_credentials "Enter Credentials for Key Slot(0)"
59+
get_dialog_result > "${passphrase_file}"
60+
fi
61+
setup_progress_fifo ${progress}
62+
(
63+
# reencrypt slot0, this will wipe all key slots
64+
cryptsetup reencrypt \
65+
--progress-frequency 1 \
66+
--key-file "${passphrase_file}" \
67+
--key-slot 0 \
68+
"${device}" 2>&1 | sed -u 's/.* \([0-9]*\)[0-9.]*%.*/\1/'
69+
) >"${progress}" &
70+
run_progress_dialog "${load_text}" "${title_text}"
71+
if [ -e "${keyfile}" ];then
72+
# re-add keyfile if present
73+
cryptsetup --key-file "${passphrase_file}" luksAddKey \
74+
"${device}" "${keyfile}"
75+
fi
76+
fi
77+
}
78+
2379
function resize_luks {
2480
cryptsetup resize luks
2581
}

dracut/modules.d/99kiwi-lib/module-setup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ install() {
2222
e2fsck btrfsck xfs_repair \
2323
vgs vgchange lvextend lvcreate lvresize pvresize \
2424
mdadm cryptsetup dialog \
25-
pv curl xz \
25+
pv curl xz sha256sum sed \
2626
dmsetup
2727
inst_multiple -o dolly
2828
if type partx &> /dev/null;then

kiwi/boot/image/dracut.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,16 @@ def post_init(self) -> None:
6161
# Initialize empty list of dracut caller options
6262
self.dracut_options: List[str] = []
6363
self.included_files: List[str] = []
64+
self.delete_after_include_files: List[str] = []
6465
self.modules: List[str] = []
6566
self.add_modules: List[str] = []
6667
self.omit_modules: List[str] = []
6768
self.available_modules = self._get_modules()
6869

69-
def include_file(self, filename: str, install_media: bool = False) -> None:
70+
def include_file(
71+
self, filename: str, install_media: bool = False,
72+
delete_after_include: bool = False
73+
) -> None:
7074
"""
7175
Include file to dracut boot image
7276
@@ -75,6 +79,8 @@ def include_file(self, filename: str, install_media: bool = False) -> None:
7579
"""
7680
self.included_files.append('--install')
7781
self.included_files.append(filename)
82+
if delete_after_include:
83+
self.delete_after_include_files.append(filename)
7884

7985
def include_module(self, module: str, install_media: bool = False) -> None:
8086
"""
@@ -238,6 +244,8 @@ def create_initrd(
238244
Command.run(
239245
['chmod', '644', self.initrd_filename]
240246
)
247+
for filename in self.delete_after_include_files:
248+
os.unlink(f'{self.boot_root_directory}/{filename}')
241249

242250
def _get_modules(self) -> List[str]:
243251
cmd = Command.run(

kiwi/builder/disk.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,8 @@ def _build_main_system(
782782

783783
self._write_crypttab_to_system_image(luks_root)
784784

785+
self._write_luks_header_checksum_to_boot_image(luks_root)
786+
785787
self._write_integritytab_to_system_image(integrity_root)
786788

787789
self._write_generic_fstab_to_system_image(device_map, system)
@@ -1271,6 +1273,20 @@ def _write_crypttab_to_system_image(
12711273
os.sep + os.sep.join(['etc', os.path.basename(filename)])
12721274
)
12731275

1276+
def _write_luks_header_checksum_to_boot_image(
1277+
self, luks_root: Optional[LuksDevice]
1278+
) -> None:
1279+
if luks_root is not None:
1280+
log.info('Including origin LUKS header checksum')
1281+
filename = ''.join(
1282+
[self.root_dir, '/root/.luks.header']
1283+
)
1284+
self.boot_image.include_file(
1285+
filename=os.sep + os.sep.join(
1286+
['root', os.path.basename(filename)]
1287+
), delete_after_include=True
1288+
)
1289+
12741290
def _write_generic_fstab_to_system_image(
12751291
self, device_map: Dict,
12761292
system: Optional[Union[FileSystemBase, VolumeManagerBase]]

kiwi/storage/luks_device.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
# project
2323
from kiwi.utils.temporary import Temporary
24+
from kiwi.utils.checksum import Checksum
25+
from kiwi.path import Path
2426
from kiwi.command import Command
2527
from kiwi.defaults import Defaults
2628
from kiwi.storage.device_provider import DeviceProvider
@@ -172,6 +174,23 @@ def create_crypto_luks(
172174
'luksAddKey', storage_device, keyfile_path
173175
]
174176
)
177+
178+
# Create backup header checksum as reencryption reference
179+
master_checksum = f'{root_dir}/root/.luks.header'
180+
Path.wipe(master_checksum)
181+
Command.run(
182+
[
183+
'cryptsetup', '--key-file', passphrase_file
184+
] + extra_options + [
185+
'luksHeaderBackup', storage_device,
186+
'--header-backup-file', master_checksum
187+
]
188+
)
189+
checksum = Checksum(master_checksum).sha256()
190+
with open(master_checksum, 'w') as shasum:
191+
shasum.write(checksum)
192+
193+
# open the pool
175194
Command.run(
176195
[
177196
'cryptsetup', '--key-file', passphrase_file

0 commit comments

Comments
 (0)