filesystem: add ZFS_LUKS capability flag

Start the ZFS_LUKS work with adding the flag and connecting the parts
needed.  Enable cryptoswap as part of that.
This commit is contained in:
Dan Bungert 2024-02-05 12:34:30 -07:00
parent 39640140ca
commit 72ca35d06b
5 changed files with 97 additions and 10 deletions

View File

@ -145,7 +145,7 @@ class FilesystemManipulator:
if key: if key:
device = self.model.add_dm_crypt( device = self.model.add_dm_crypt(
device, device,
key, key=key,
recovery_key=spec.get("recovery-key"), recovery_key=spec.get("recovery-key"),
) )
devices.add(device) devices.add(device)
@ -177,6 +177,15 @@ class FilesystemManipulator:
delete_lvm_partition = delete_logical_volume delete_lvm_partition = delete_logical_volume
def create_cryptoswap(self, device):
dmc = self.model.add_dm_crypt(
device,
keyfile="/dev/urandom",
options=["swap", "initramfs"],
)
self.create_filesystem(dmc, dict(fstype="swap"))
return dmc
def create_zpool(self, device, pool, mountpoint, boot=False, canmount="on"): def create_zpool(self, device, pool, mountpoint, boot=False, canmount="on"):
fs_properties = dict( fs_properties = dict(
atime=None, atime=None,
@ -341,7 +350,7 @@ class FilesystemManipulator:
if key: if key:
d = self.model.add_dm_crypt( d = self.model.add_dm_crypt(
d, d,
key, key=key,
recovery_key=spec.get("recovery-key"), recovery_key=spec.get("recovery-key"),
) )
d._constructed_device = existing d._constructed_device = existing

View File

@ -347,6 +347,7 @@ class GuidedCapability(enum.Enum):
LVM = enum.auto() LVM = enum.auto()
LVM_LUKS = enum.auto() LVM_LUKS = enum.auto()
ZFS = enum.auto() ZFS = enum.auto()
ZFS_LUKS = enum.auto()
CORE_BOOT_ENCRYPTED = enum.auto() CORE_BOOT_ENCRYPTED = enum.auto()
CORE_BOOT_UNENCRYPTED = enum.auto() CORE_BOOT_UNENCRYPTED = enum.auto()
@ -381,7 +382,10 @@ class GuidedCapability(enum.Enum):
] ]
def is_zfs(self) -> bool: def is_zfs(self) -> bool:
return self in [GuidedCapability.ZFS] return self in [
GuidedCapability.ZFS,
GuidedCapability.ZFS_LUKS,
]
class GuidedDisallowedCapabilityReason(enum.Enum): class GuidedDisallowedCapabilityReason(enum.Enum):

View File

@ -1068,10 +1068,11 @@ LUKS_OVERHEAD = 16 * (2**20)
@fsobj("dm_crypt") @fsobj("dm_crypt")
class DM_Crypt: class DM_Crypt(_Formattable):
volume: _Formattable = attributes.ref(backlink="_constructed_device") volume: _Formattable = attributes.ref(backlink="_constructed_device")
key: Optional[str] = attr.ib(metadata={"redact": True}, default=None) key: Optional[str] = attr.ib(metadata={"redact": True}, default=None)
keyfile: Optional[str] = None keyfile: Optional[str] = None
options: Optional[List[str]] = None
recovery_key: Optional[RecoveryKeyHandler] = None recovery_key: Optional[RecoveryKeyHandler] = None
_recovery_keyfile: Optional[str] = None _recovery_keyfile: Optional[str] = None
_recovery_live_location: Optional[str] = None _recovery_live_location: Optional[str] = None
@ -1142,6 +1143,29 @@ class DM_Crypt:
def size(self): def size(self):
return self.volume.size - LUKS_OVERHEAD return self.volume.size - LUKS_OVERHEAD
def available(self):
if self._is_in_use:
return False
if self._constructed_device is not None:
return False
if self._fs is None:
return True
return self._fs._available()
@property
def ok_for_raid(self):
if self._fs is not None:
if self._fs.preserve:
return self._fs._mount is None
return False
if self._constructed_device is not None:
return False
return True
@property
def ok_for_lvm_vg(self):
return self.ok_for_raid and self.size > LVM_OVERHEAD
@fsobj("device") @fsobj("device")
class ArbitraryDevice(_Device): class ArbitraryDevice(_Device):
@ -2081,9 +2105,11 @@ class FilesystemModel:
def add_dm_crypt( def add_dm_crypt(
self, self,
volume, volume,
key,
*, *,
recovery_key: Optional[RecoveryKeyHandler], key: Optional[str] = None,
keyfile: Optional[str] = None,
options: Optional[List[str]] = None,
recovery_key: Optional[RecoveryKeyHandler] = None,
root: Optional[pathlib.Path] = None, root: Optional[pathlib.Path] = None,
): ):
if not volume.available: if not volume.available:
@ -2093,6 +2119,8 @@ class FilesystemModel:
m=self, m=self,
volume=volume, volume=volume,
key=key, key=key,
keyfile=keyfile,
options=options,
recovery_key=recovery_key, recovery_key=recovery_key,
) )
self._actions.append(dm_crypt) self._actions.append(dm_crypt)

View File

@ -216,6 +216,7 @@ class VariationInfo:
GuidedCapability.LVM, GuidedCapability.LVM,
GuidedCapability.LVM_LUKS, GuidedCapability.LVM_LUKS,
GuidedCapability.ZFS, GuidedCapability.ZFS,
GuidedCapability.ZFS_LUKS,
] ]
), ),
) )
@ -563,13 +564,17 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
bootfs_size = align_up(sizes.get_bootfs_size(gap.size), part_align) bootfs_size = align_up(sizes.get_bootfs_size(gap.size), part_align)
gap_boot, gap_rest = gap.split(bootfs_size) gap_boot, gap_rest = gap.split(bootfs_size)
bpart = self.create_partition(device, gap_boot, dict(fstype=None)) bpart = self.create_partition(device, gap_boot, dict(fstype=None))
encrypted = choice.password is not None
avail = gap_rest.size - self._info.min_size avail = gap_rest.size - self._info.min_size
swap_size = align_down(swap.suggested_swapsize(avail=avail), part_align) swap_size = align_down(swap.suggested_swapsize(avail=avail), part_align)
if swap_size > 0: if swap_size > 0:
gap_swap, gap_rootfs = gap_rest.split(swap_size) gap_swap, gap = gap_rest.split(swap_size)
self.create_partition(device, gap_swap, dict(fstype="swap")) if encrypted:
gap = gap_rootfs part = self.create_partition(device, gap_swap, {})
self.create_cryptoswap(part)
else:
self.create_partition(device, gap_swap, dict(fstype="swap"))
else: else:
gap = gap_rest gap = gap_rest
rpart = self.create_partition(device, gap, dict(fstype=None)) rpart = self.create_partition(device, gap, dict(fstype=None))
@ -1386,7 +1391,10 @@ class FilesystemController(SubiquityController, FilesystemManipulator):
capability = GuidedCapability.DD capability = GuidedCapability.DD
assert mode == "reformat_disk" assert mode == "reformat_disk"
elif name == "zfs": elif name == "zfs":
capability = GuidedCapability.ZFS if password is not None:
capability = GuidedCapability.ZFS_LUKS
else:
capability = GuidedCapability.ZFS
else: else:
capability = GuidedCapability.DIRECT capability = GuidedCapability.DIRECT

View File

@ -75,6 +75,7 @@ default_capabilities = [
GuidedCapability.LVM, GuidedCapability.LVM,
GuidedCapability.LVM_LUKS, GuidedCapability.LVM_LUKS,
GuidedCapability.ZFS, GuidedCapability.ZFS,
GuidedCapability.ZFS_LUKS,
] ]
@ -567,6 +568,43 @@ class TestGuided(IsolatedAsyncioTestCase):
zfs_boot = self.model._mount_for_path("/boot") zfs_boot = self.model._mount_for_path("/boot")
self.assertEqual("zfs", zfs_boot.type) self.assertEqual("zfs", zfs_boot.type)
@parameterized.expand(boot_expectations)
async def test_guided_zfs_luks(self, bootloader, ptable, p1mnt):
await self._guided_setup(bootloader, ptable)
target = GuidedStorageTargetReformat(
disk_id=self.d1.id, allowed=default_capabilities
)
await self.controller.guided(
GuidedChoiceV2(
target=target,
capability=GuidedCapability.ZFS_LUKS,
password="passw0rd",
)
)
[firmware, boot, swap, root] = self.d1.partitions()
self.assertEqual(p1mnt, firmware.mount)
self.assertIsNone(boot.mount)
self.assertIsNone(root.mount)
self.assertFalse(firmware.preserve)
self.assertFalse(boot.preserve)
self.assertFalse(swap.preserve)
self.assertFalse(root.preserve)
self.assertIsNone(swap.fs())
[dmc] = self.model.all_dm_crypts()
self.assertEqual("/dev/urandom", dmc.keyfile)
self.assertEqual(["swap", "initramfs"], dmc.options)
self.assertEqual("swap", dmc.fs().fstype)
[rpool] = self.model._all(type="zpool", pool="rpool")
self.assertIsNone(rpool.path)
self.assertEqual([root], rpool.vdevs)
[bpool] = self.model._all(type="zpool", pool="bpool")
self.assertIsNone(bpool.path)
self.assertEqual([boot], bpool.vdevs)
zfs_rootfs = self.model._mount_for_path("/")
self.assertEqual("zfs", zfs_rootfs.type)
zfs_boot = self.model._mount_for_path("/boot")
self.assertEqual("zfs", zfs_boot.type)
async def test_guided_zfs_BIOS_MSDOS(self): async def test_guided_zfs_BIOS_MSDOS(self):
await self._guided_setup(Bootloader.BIOS, "msdos") await self._guided_setup(Bootloader.BIOS, "msdos")
target = GuidedStorageTargetReformat( target = GuidedStorageTargetReformat(