Skip to content

Commit 746e4dd

Browse files
committed
clean luks-autodetect
Signed-off-by: Zen <[email protected]>
1 parent 3f52aee commit 746e4dd

File tree

5 files changed

+63
-51
lines changed

5 files changed

+63
-51
lines changed

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The original goal of this project was to create an initramfs suitable for decryp
1616
- Allows for late insertion of a smartcard
1717
- Can fail back to plain password entry
1818
* Auto-detection and validation of the root mount using `/proc/mounts`
19+
* Auto-detection and validation of LUKS root mounts.
1920
* Auto-detection and validation of the btrfs subvolume used for the root mount, if present.
2021
* Dynamic BTRFS subvolume selection at boot time using `subvol_selector`.
2122
* Auto-detection of kernel modules using `lspci` and `lsmod`
@@ -297,6 +298,7 @@ Similarly `ugrd.kmod.novideo` `nonetwork`, and `nosound` exist to ignore video,
297298
### Filesystem modules
298299

299300
`autodetect_root` (true) Set the root mount parameter based on the current root label or uuid.
301+
`autodetect_root_luks` (true) Attempt to automatically configure LUKS mounts for the root device.
300302

301303
#### ugrd.fs.mounts
302304

src/ugrd/crypto/cryptsetup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__author__ = 'desultory'
2-
__version__ = '1.4.3'
2+
__version__ = '1.4.4'
33

44
from zenlib.util import check_dict
55

@@ -106,7 +106,7 @@ def get_crypt_sources(self) -> list[str]:
106106
if parameters.get('path') and not self['validate']:
107107
self.logger.warning("Using device paths is unreliable and can result in boot failures.")
108108
out += [f"export CRYPTSETUP_SOURCE_{name}={parameters.get('path')}"]
109-
elif not parameters.get('partuuid') and not parameters.get('uuid'):
109+
elif not parameters.get('partuuid') and not parameters.get('uuid') and parameters.get('path'):
110110
raise ValueError("Validation must be disabled to use device paths with the cryptsetup module.")
111111
else:
112112
try:

src/ugrd/fs/mounts.py

Lines changed: 55 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -193,61 +193,67 @@ def _get_dm_devices(self, f_major=None, f_minor=None) -> dict:
193193
return dm_devices
194194

195195

196-
@check_dict('autodetect_root', value=True, log_level=10, message="Skipping root autodetection, autodetect_root is not set.")
197-
@check_dict({'mounts': {'root': 'source'}}, unset=True, log_level=30, message="Skipping root autodetection, root source is already set.")
198-
def autodetect_root(self) -> None:
199-
""" Sets self['mounts']['root']['source'] based on the host mount. """
200-
root_mount_info = _get_blkid_info(self, _get_mounts_source_device(self, '/'))
201-
self.logger.debug("Detected root mount info: %s" % root_mount_info)
202-
196+
@check_dict('autodetect_root_luks', value=True, log_level=10, message="Skipping LUKS autodetection, autodetect_root_luks is not set.")
197+
def _autodetect_root_luks(self, root_mount_info: dict) -> None:
203198
# Check if the mount is under /dev/mapper or starts with /dev/dm-
204-
if root_mount_info['name'].startswith('/dev/mapper') or root_mount_info['name'].startswith('/dev/dm-'):
205-
mount_loc = Path(root_mount_info['name']).resolve()
206-
self.logger.debug("Detected a device mapper mount: %s" % mount_loc)
207-
208-
major, minor = mount_loc.stat().st_rdev >> 8, mount_loc.stat().st_rdev & 0xFF
209-
self.logger.debug("[%s] Major: %s, Minor: %s" % (mount_loc, major, minor))
210-
dm_info = _get_dm_devices(self, major, minor)
211-
212-
if len(dm_info) > 1: # there should only be one device mapper device associated with the mount
213-
self.logger.error("Device mapper devices: %s" % dm_info)
214-
raise RuntimeError("Multiple device mapper devices found for: %s" % mount_loc)
215-
216-
mapped_name, dm_info = dm_info.popitem()
217-
218-
if mount_loc.name != dm_info['name'] and mount_loc.name != mapped_name:
219-
raise ValueError("Device mapper device name mismatch: %s != %s" % (mount_loc.name, dm_info['name']))
199+
if not root_mount_info['name'].startswith('/dev/mapper') and not root_mount_info['name'].startswith('/dev/dm-'):
200+
self.logger.debug("Root mount is not a device mapper mount: %s" % root_mount_info['name'])
201+
return
202+
mount_loc = Path(root_mount_info['name']).resolve()
203+
self.logger.debug("Detected a device mapper mount: %s" % mount_loc)
204+
205+
major, minor = mount_loc.stat().st_rdev >> 8, mount_loc.stat().st_rdev & 0xFF
206+
self.logger.debug("[%s] Major: %s, Minor: %s" % (mount_loc, major, minor))
207+
dm_info = _get_dm_devices(self, major, minor)
208+
209+
if len(dm_info) > 1: # there should only be one device mapper device associated with the mount
210+
self.logger.error("Device mapper devices: %s" % dm_info)
211+
raise RuntimeError("Multiple device mapper devices found for: %s" % mount_loc)
212+
213+
mapped_name, dm_info = dm_info.popitem()
214+
215+
if mount_loc.name != dm_info['name'] and mount_loc.name != mapped_name:
216+
raise ValueError("Device mapper device name mismatch: %s != %s" % (mount_loc.name, dm_info['name']))
217+
218+
if len(dm_info['holders']) > 0:
219+
self.logger.error("Device mapper holders: %s" % dm_info['holders'])
220+
raise RuntimeError("LUKS volumes should not have holders, potential LVM volume: %s" % mount_loc.name)
221+
222+
if len(dm_info['slaves']) == 0:
223+
raise RuntimeError("No slaves found for device mapper device, unknown type: %s" % mount_loc.name)
224+
elif len(dm_info['slaves']) > 1:
225+
self.logger.error("Device mapper slaves: %s" % dm_info['slaves'])
226+
raise RuntimeError("Multiple slaves found for device mapper device, unknown type: %s" % mount_loc.name)
227+
228+
luks_mount = _get_blkid_info(self, Path('/dev/' + dm_info['slaves'][0]))
229+
if luks_mount.get('type') != 'crypto_LUKS':
230+
if not luks_mount.get('uuid'):
231+
self.logger.error("[%s] Unknown device mapper slave type: %s" % (dm_info['slaves'][0], luks_mount.get('type')))
232+
else:
233+
raise RuntimeError("[%s] Unknown device mapper slave type: %s" % (dm_info['slaves'][0], luks_mount.get('type')))
220234

221-
if len(dm_info['holders']) > 0:
222-
self.logger.error("Device mapper holders: %s" % dm_info['holders'])
223-
raise RuntimeError("LUKS volumes should not have holders, potential LVM volume: %s" % mount_loc.name)
235+
if 'ugrd.crypto.cryptsetup' not in self['modules']:
236+
self.logger.info("Autodetected LUKS mount, enabling the cryptsetup module: %s" % luks_mount['name'])
237+
self['modules'] = 'ugrd.crypto.cryptsetup'
224238

225-
if len(dm_info['slaves']) == 0:
226-
raise RuntimeError("No slaves found for device mapper device, unknown type: %s" % mount_loc.name)
227-
elif len(dm_info['slaves']) > 1:
228-
self.logger.error("Device mapper slaves: %s" % dm_info['slaves'])
229-
raise RuntimeError("Multiple slaves found for device mapper device, unknown type: %s" % mount_loc.name)
239+
if uuid := luks_mount.get('uuid'):
240+
self.logger.info("[%s] Detected LUKS volume uuid: %s" % (mount_loc.name, uuid))
241+
self['cryptsetup'] = {dm_info['name']: {'uuid': uuid}}
242+
elif partuuid := luks_mount.get('partuuid'):
243+
self.logger.info("[%s] Detected LUKS volume partuuid: %s" % (mount_loc.name, partuuid))
244+
self['cryptsetup'] = {dm_info['name']: {'partuuid': partuuid}}
230245

231-
luks_mount = _get_blkid_info(self, Path('/dev/' + dm_info['slaves'][0]))
232-
if luks_mount.get('type') != 'crypto_LUKS':
233-
if not luks_mount.get('uuid'):
234-
self.logger.error("[%s] Unknown device mapper slave type: %s" % (dm_info['slaves'][0], luks_mount.get('type')))
235-
else:
236-
raise RuntimeError("[%s] Unknown device mapper slave type: %s" % (dm_info['slaves'][0], luks_mount.get('type')))
246+
self.logger.info("[%s] Configuring cryptsetup for LUKS mount (%s) on: %s\n%s" %
247+
(mount_loc.name, dm_info['name'], luks_mount['name'], pretty_print(self['cryptsetup'])))
237248

238-
if 'ugrd.crypto.cryptsetup' not in self['modules']:
239-
self.logger.info("Autodetected LUKS mount, enabling the cryptsetup module: %s" % luks_mount['name'])
240-
self['modules'] = 'ugrd.crypto.cryptsetup'
241249

242-
if uuid := luks_mount.get('uuid'):
243-
self.logger.info("[%s] Detected LUKS volume uuid: %s" % (mount_loc.name, uuid))
244-
self['cryptsetup'] = {dm_info['name']: {'uuid': uuid}}
245-
elif partuuid := luks_mount.get('partuuid'):
246-
self.logger.info("[%s] Detected LUKS volume partuuid: %s" % (mount_loc.name, partuuid))
247-
self['cryptsetup'] = {dm_info['name']: {'partuuid': partuuid}}
248-
249-
self.logger.info("[%s] Configuring cryptsetup for LUKS mount (%s) on: %s\n%s" %
250-
(mount_loc.name, dm_info['name'], luks_mount['name'], pretty_print(self['cryptsetup'])))
250+
@check_dict('autodetect_root', value=True, log_level=10, message="Skipping root autodetection, autodetect_root is not set.")
251+
@check_dict({'mounts': {'root': 'source'}}, unset=True, log_level=30, message="Skipping root autodetection, root source is already set.")
252+
def autodetect_root(self) -> None:
253+
""" Sets self['mounts']['root']['source'] based on the host mount. """
254+
root_mount_info = _get_blkid_info(self, _get_mounts_source_device(self, '/'))
255+
self.logger.debug("Detected root mount info: %s" % root_mount_info)
256+
_autodetect_root_luks(self, root_mount_info)
251257

252258
mount_info = {'root': {'type': 'auto', 'base_mount': False}}
253259

src/ugrd/fs/mounts.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ binaries = [
88

99
mount_wait = false
1010
autodetect_root = true
11+
autodetect_root_luks = true
1112

1213
[imports.config_processing]
1314
"ugrd.fs.mounts" = [ "_process_mounts_multi", "_process_mount_timeout" ]
@@ -42,6 +43,7 @@ mount_wait = "bool" # Add the mount_wait property, if defined, user input will
4243
mount_timeout = "float" # Add the mount_timeout property, used to define the timeout for mount_wait
4344
mount_cmd = "str" # The mount command called by mount_root, can be overridden
4445
autodetect_root = "bool" # Add the autodetect_root property, if defined, the root mount will be autodetected
46+
autodetect_root_luks = "bool" # Whether or not to try to autodetect LUKS partitions
4547

4648

4749
# Define the base of the root mount

src/ugrd/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ def main():
2323
{'flags': ['--no-firmware'], 'action': 'store_false', 'help': 'Exclude firmware files.', 'dest': 'kmod_pull_firmware'},
2424
{'flags': ['--autodetect-root'], 'action': 'store_true', 'help': 'Autodetect the root partition.'},
2525
{'flags': ['--no-autodetect-root'], 'action': 'store_false', 'help': 'Do not autodetect the root partition.', 'dest': 'autodetect_root'},
26+
{'flags': ['--autodetect-root-luks'], 'action': 'store_true', 'help': 'Autodetect LUKS volumes under the root partition.'},
27+
{'flags': ['--no-autodetect-root-luks'], 'action': 'store_false', 'help': 'Do not autodetect root LUKS volumes.', 'dest': 'autodetect_root'},
2628
{'flags': ['--print-config'], 'action': 'store_true', 'help': 'Print the final config dict.'},
2729
{'flags': ['out_file'], 'action': 'store', 'help': 'Output file location', 'nargs': '?'}]
2830

0 commit comments

Comments
 (0)