Move disk encryption into disk config menu (#3502)

This commit is contained in:
Daniel Girtler 2025-05-25 21:23:37 +10:00 committed by GitHub
parent 5a54902935
commit cdb1debe2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 103 additions and 107 deletions

View File

@ -76,7 +76,6 @@ class ArchConfig:
# Special fields that should be handle with care due to security implications
users: list[User] = field(default_factory=list)
disk_encryption: DiskEncryption | None = None
root_enc_password: Password | None = None
def unsafe_json(self) -> dict[str, Any]:
@ -85,8 +84,10 @@ class ArchConfig:
'root_enc_password': self.root_enc_password.enc_password if self.root_enc_password else None,
}
if self.disk_encryption and self.disk_encryption.encryption_password:
config['encryption_password'] = self.disk_encryption.encryption_password.plaintext
if self.disk_config:
disk_encryption = self.disk_config.disk_encryption
if disk_encryption and disk_encryption.encryption_password:
config['encryption_password'] = disk_encryption.encryption_password.plaintext
return config
@ -113,9 +114,6 @@ class ArchConfig:
if self.disk_config:
config['disk_config'] = self.disk_config.json()
if self.disk_encryption:
config['disk_encryption'] = self.disk_encryption.json()
if self.profile_config:
config['profile_config'] = self.profile_config.json()
@ -137,7 +135,23 @@ class ArchConfig:
arch_config.archinstall_language = translation_handler.get_language_by_name(archinstall_lang)
if disk_config := args_config.get('disk_config', {}):
arch_config.disk_config = DiskLayoutConfiguration.parse_arg(disk_config)
enc_password = args_config.get('encryption_password', '')
password = Password(plaintext=enc_password) if enc_password else None
arch_config.disk_config = DiskLayoutConfiguration.parse_arg(disk_config, password)
# DEPRECATED
# backwards compatibility for main level disk_encryption entry
disk_encryption: DiskEncryption | None = None
if args_config.get('disk_encryption', None) is not None and arch_config.disk_config is not None:
disk_encryption = DiskEncryption.parse_arg(
arch_config.disk_config,
args_config['disk_encryption'],
Password(plaintext=args_config.get('encryption_password', '')),
)
if disk_encryption:
arch_config.disk_config.disk_encryption = disk_encryption
if profile_config := args_config.get('profile_config', None):
arch_config.profile_config = ProfileConfiguration.parse_arg(profile_config)
@ -171,13 +185,6 @@ class ArchConfig:
if audio_config := args_config.get('audio_config', None):
arch_config.audio_config = AudioConfiguration.parse_arg(audio_config)
if args_config.get('disk_encryption', None) is not None and arch_config.disk_config is not None:
arch_config.disk_encryption = DiskEncryption.parse_arg(
arch_config.disk_config,
args_config['disk_encryption'],
Password(plaintext=args_config.get('encryption_password', '')),
)
if hostname := args_config.get('hostname', ''):
arch_config.hostname = hostname

View File

@ -1,10 +1,13 @@
from dataclasses import dataclass
from typing import override
from archinstall.lib.disk.encryption_menu import DiskEncryptionMenu
from archinstall.lib.models.device_model import (
BtrfsOptions,
DiskEncryption,
DiskLayoutConfiguration,
DiskLayoutType,
EncryptionType,
LvmConfiguration,
SnapshotConfig,
SnapshotType,
@ -25,6 +28,7 @@ class DiskMenuConfig:
disk_config: DiskLayoutConfiguration | None
lvm_config: LvmConfiguration | None
btrfs_snapshot_config: SnapshotConfig | None
disk_encryption: DiskEncryption | None
class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]):
@ -34,6 +38,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]):
disk_config=None,
lvm_config=None,
btrfs_snapshot_config=None,
disk_encryption=None,
)
else:
snapshot_config = disk_layout_config.btrfs_options.snapshot_config if disk_layout_config.btrfs_options else None
@ -41,6 +46,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]):
self._disk_menu_config = DiskMenuConfig(
disk_config=disk_layout_config,
lvm_config=disk_layout_config.lvm_config,
disk_encryption=disk_layout_config.disk_encryption,
btrfs_snapshot_config=snapshot_config,
)
@ -70,6 +76,13 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]):
dependencies=[self._check_dep_lvm],
key='lvm_config',
),
MenuItem(
text=tr('Disk encryption'),
action=self._disk_encryption,
preview_action=self._prev_disk_encryption,
dependencies=['disk_config'],
key='disk_encryption',
),
MenuItem(
text='Btrfs snapshots',
action=self._select_btrfs_snapshots,
@ -87,6 +100,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]):
if self._disk_menu_config.disk_config:
self._disk_menu_config.disk_config.lvm_config = self._disk_menu_config.lvm_config
self._disk_menu_config.disk_config.btrfs_options = BtrfsOptions(snapshot_config=self._disk_menu_config.btrfs_snapshot_config)
self._disk_menu_config.disk_config.disk_encryption = self._disk_menu_config.disk_encryption
return self._disk_menu_config.disk_config
return None
@ -107,11 +121,25 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]):
return False
def _disk_encryption(self, preset: DiskEncryption | None) -> DiskEncryption | None:
disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value
if not disk_config:
# this should not happen as the encryption menu has the disk_config as dependency
raise ValueError('No disk layout specified')
if not DiskEncryption.validate_enc(disk_config):
return None
disk_encryption = DiskEncryptionMenu(disk_config, preset=preset).run()
return disk_encryption
def _select_disk_layout_config(self, preset: DiskLayoutConfiguration | None) -> DiskLayoutConfiguration | None:
disk_config = select_disk_config(preset)
if disk_config != preset:
self._menu_item_group.find_by_key('lvm_config').value = None
self._menu_item_group.find_by_key('disk_encryption').value = None
return disk_config
@ -217,3 +245,29 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]):
snapshot_config: SnapshotConfig = item.value
return tr('Snapshot type: {}').format(snapshot_config.snapshot_type.value)
def _prev_disk_encryption(self, item: MenuItem) -> str | None:
disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value
enc_config: DiskEncryption | None = item.value
if disk_config and not DiskEncryption.validate_enc(disk_config):
return tr('LVM disk encryption with more than 2 partitions is currently not supported')
if enc_config:
enc_type = EncryptionType.type_to_text(enc_config.encryption_type)
output = tr('Encryption type') + f': {enc_type}\n'
if enc_config.encryption_password:
output += tr('Password') + f': {enc_config.encryption_password.hidden()}\n'
if enc_config.partitions:
output += f'Partitions: {len(enc_config.partitions)} selected\n'
elif enc_config.lvm_volumes:
output += f'LVM volumes: {len(enc_config.lvm_volumes)} selected\n'
if enc_config.hsm_device:
output += f'HSM: {enc_config.hsm_device.manufacturer}'
return output
return None

View File

@ -27,13 +27,9 @@ from .device_handler import device_handler
class FilesystemHandler:
def __init__(
self,
disk_config: DiskLayoutConfiguration,
enc_conf: DiskEncryption | None = None,
):
def __init__(self, disk_config: DiskLayoutConfiguration):
self._disk_config = disk_config
self._enc_config = enc_conf
self._enc_config = disk_config.disk_encryption
def perform_filesystem_operations(self, show_countdown: bool = True) -> None:
if self._disk_config.config_type == DiskLayoutType.Pre_mount:

View File

@ -3,8 +3,7 @@ from __future__ import annotations
from typing import override
from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu
from archinstall.lib.disk.encryption_menu import DiskEncryptionMenu
from archinstall.lib.models.device_model import DiskEncryption, DiskLayoutConfiguration, DiskLayoutType, EncryptionType, FilesystemType, PartitionModification
from archinstall.lib.models.device_model import DiskLayoutConfiguration, DiskLayoutType, EncryptionType, FilesystemType, PartitionModification
from archinstall.lib.packages import list_available_packages
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
@ -79,13 +78,6 @@ class GlobalMenu(AbstractMenu[None]):
mandatory=True,
key='disk_config',
),
MenuItem(
text=tr('Disk encryption'),
action=self._disk_encryption,
preview_action=self._prev_disk_encryption,
dependencies=['disk_config'],
key='disk_encryption',
),
MenuItem(
text=tr('Swap'),
value=True,
@ -270,19 +262,6 @@ class GlobalMenu(AbstractMenu[None]):
if o.key is not None:
self._item_group.find_by_key(o.key).text = o.text
def _disk_encryption(self, preset: DiskEncryption | None) -> DiskEncryption | None:
disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value
if not disk_config:
# this should not happen as the encryption menu has the disk_config as dependency
raise ValueError('No disk layout specified')
if not DiskEncryption.validate_enc(disk_config):
return None
disk_encryption = DiskEncryptionMenu(disk_config, preset=preset).run()
return disk_encryption
def _locale_selection(self, preset: LocaleConfiguration) -> LocaleConfiguration:
locale_config = LocaleMenu(preset).run()
return locale_config
@ -335,6 +314,9 @@ class GlobalMenu(AbstractMenu[None]):
if disk_layout_conf.lvm_config:
output += '{}: {}'.format(tr('LVM configuration type'), disk_layout_conf.lvm_config.config_type.display_msg())
if disk_layout_conf.disk_encryption:
output += tr('Disk encryption') + ': ' + EncryptionType.type_to_text(disk_layout_conf.disk_encryption.encryption_type)
if disk_layout_conf.btrfs_options:
btrfs_options = disk_layout_conf.btrfs_options
if btrfs_options.snapshot_config:
@ -391,32 +373,6 @@ class GlobalMenu(AbstractMenu[None]):
return f'{tr("Bootloader")}: {item.value.value}'
return None
def _prev_disk_encryption(self, item: MenuItem) -> str | None:
disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value
enc_config: DiskEncryption | None = item.value
if disk_config and not DiskEncryption.validate_enc(disk_config):
return tr('LVM disk encryption with more than 2 partitions is currently not supported')
if enc_config:
enc_type = EncryptionType.type_to_text(enc_config.encryption_type)
output = tr('Encryption type') + f': {enc_type}\n'
if enc_config.encryption_password:
output += tr('Password') + f': {enc_config.encryption_password.hidden()}\n'
if enc_config.partitions:
output += f'Partitions: {len(enc_config.partitions)} selected\n'
elif enc_config.lvm_volumes:
output += f'LVM volumes: {len(enc_config.lvm_volumes)} selected\n'
if enc_config.hsm_device:
output += f'HSM: {enc_config.hsm_device.manufacturer}'
return output
return None
def _validate_bootloader(self) -> str | None:
"""
Checks the selected bootloader is valid for the selected filesystem
@ -515,9 +471,6 @@ class GlobalMenu(AbstractMenu[None]):
) -> DiskLayoutConfiguration | None:
disk_config = DiskLayoutConfigurationMenu(preset).run()
if disk_config != preset:
self._menu_item_group.find_by_key('disk_encryption').value = None
return disk_config
def _select_bootloader(self, preset: Bootloader | None) -> Bootloader | None:

View File

@ -62,7 +62,6 @@ class Installer:
self,
target: Path,
disk_config: DiskLayoutConfiguration,
disk_encryption: DiskEncryption | None = None,
base_packages: list[str] = [],
kernels: list[str] | None = None,
):
@ -74,7 +73,7 @@ class Installer:
self.kernels = kernels or ['linux']
self._disk_config = disk_config
self._disk_encryption = disk_encryption or DiskEncryption(EncryptionType.NoEncryption)
self._disk_encryption = disk_config.disk_encryption or DiskEncryption(EncryptionType.NoEncryption)
self.target: Path = target
self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S')

View File

@ -41,6 +41,7 @@ class _DiskLayoutConfigurationSerialization(TypedDict):
lvm_config: NotRequired[_LvmConfigurationSerialization]
mountpoint: NotRequired[str]
btrfs_options: NotRequired[_BtrfsOptionsSerialization]
disk_encryption: NotRequired[_DiskEncryptionSerialization]
@dataclass
@ -48,6 +49,7 @@ class DiskLayoutConfiguration:
config_type: DiskLayoutType
device_modifications: list[DeviceModification] = field(default_factory=list)
lvm_config: LvmConfiguration | None = None
disk_encryption: DiskEncryption | None = None
btrfs_options: BtrfsOptions | None = None
# used for pre-mounted config
@ -68,13 +70,21 @@ class DiskLayoutConfiguration:
if self.lvm_config:
config['lvm_config'] = self.lvm_config.json()
if self.disk_encryption:
config['disk_encryption'] = self.disk_encryption.json()
if self.btrfs_options:
config['btrfs_options'] = self.btrfs_options.json()
return config
@classmethod
def parse_arg(cls, disk_config: _DiskLayoutConfigurationSerialization) -> DiskLayoutConfiguration | None:
def parse_arg(
cls,
disk_config: _DiskLayoutConfigurationSerialization,
enc_password: Password | None = None,
disk_encryption: _DiskEncryptionSerialization | None = None,
) -> DiskLayoutConfiguration | None:
from archinstall.lib.disk.device_handler import device_handler
device_modifications: list[DeviceModification] = []
@ -179,6 +189,9 @@ class DiskLayoutConfiguration:
if (lvm_arg := disk_config.get('lvm_config', None)) is not None:
config.lvm_config = LvmConfiguration.parse_arg(lvm_arg, config)
if (enc_config := disk_config.get('disk_encryption', None)) is not None:
config.disk_encryption = DiskEncryption.parse_arg(config, enc_config, enc_password)
if config.is_default_btrfs():
if (btrfs_arg := disk_config.get('btrfs_options', None)) is not None:
config.btrfs_options = BtrfsOptions.parse_arg(btrfs_arg)

View File

@ -53,14 +53,12 @@ def perform_installation(mountpoint: Path) -> None:
disk_config = config.disk_config
run_mkinitcpio = not config.uki
locale_config = config.locale_config
disk_encryption = config.disk_encryption
optional_repositories = config.mirror_config.optional_repositories if config.mirror_config else []
mountpoint = disk_config.mountpoint if disk_config.mountpoint else mountpoint
with Installer(
mountpoint,
disk_config,
disk_encryption=disk_encryption,
kernels=config.kernels,
) as installation:
# Mount all the drives to the desired mountpoint
@ -70,7 +68,7 @@ def perform_installation(mountpoint: Path) -> None:
installation.sanity_check()
if disk_config.config_type != DiskLayoutType.Pre_mount:
if disk_encryption and disk_encryption.encryption_type != EncryptionType.NoEncryption:
if disk_config.disk_encryption and disk_config.disk_encryption.encryption_type != EncryptionType.NoEncryption:
# generate encryption key files for the mounted luks devices
installation.generate_key_files()
@ -190,11 +188,7 @@ def guided() -> None:
guided()
if arch_config_handler.config.disk_config:
fs_handler = FilesystemHandler(
arch_config_handler.config.disk_config,
arch_config_handler.config.disk_encryption,
)
fs_handler = FilesystemHandler(arch_config_handler.config.disk_config)
fs_handler.perform_filesystem_operations()
perform_installation(arch_config_handler.args.mountpoint)

View File

@ -4,7 +4,6 @@ from archinstall.default_profiles.minimal import MinimalProfile
from archinstall.lib.args import arch_config_handler
from archinstall.lib.configuration import ConfigurationOutput
from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu
from archinstall.lib.disk.encryption_menu import DiskEncryptionMenu
from archinstall.lib.disk.filesystem import FilesystemHandler
from archinstall.lib.installer import Installer
from archinstall.lib.models import Bootloader
@ -23,13 +22,11 @@ def perform_installation(mountpoint: Path) -> None:
return
disk_config = config.disk_config
disk_encryption = config.disk_encryption
mountpoint = disk_config.mountpoint if disk_config.mountpoint else mountpoint
with Installer(
mountpoint,
disk_config,
disk_encryption=disk_encryption,
kernels=config.kernels,
) as installation:
# Strap in the base system, add a boot loader and configure
@ -64,13 +61,7 @@ def perform_installation(mountpoint: Path) -> None:
def _minimal() -> None:
with Tui():
disk_config = DiskLayoutConfigurationMenu(disk_layout_config=None).run()
disk_encryption = None
if disk_config:
disk_encryption = DiskEncryptionMenu(disk_config).run()
arch_config_handler.config.disk_config = disk_config
arch_config_handler.config.disk_encryption = disk_encryption
config = ConfigurationOutput(arch_config_handler.config)
config.write_debug()
@ -86,11 +77,7 @@ def _minimal() -> None:
_minimal()
if arch_config_handler.config.disk_config:
fs_handler = FilesystemHandler(
arch_config_handler.config.disk_config,
arch_config_handler.config.disk_encryption,
)
fs_handler = FilesystemHandler(arch_config_handler.config.disk_config)
fs_handler.perform_filesystem_operations()
perform_installation(arch_config_handler.args.mountpoint)

View File

@ -17,7 +17,6 @@ def ask_user_questions() -> None:
global_menu.set_enabled('archinstall_language', True)
global_menu.set_enabled('disk_config', True)
global_menu.set_enabled('disk_encryption', True)
global_menu.set_enabled('swap', True)
global_menu.set_enabled('__config__', True)
@ -37,13 +36,11 @@ def perform_installation(mountpoint: Path) -> None:
return
disk_config = config.disk_config
disk_encryption = config.disk_encryption
mountpoint = disk_config.mountpoint if disk_config.mountpoint else mountpoint
with Installer(
mountpoint,
disk_config,
disk_encryption=disk_encryption,
kernels=config.kernels,
) as installation:
# Mount all the drives to the desired mountpoint
@ -78,11 +75,7 @@ def _only_hd() -> None:
_only_hd()
if arch_config_handler.config.disk_config:
fs_handler = FilesystemHandler(
arch_config_handler.config.disk_config,
arch_config_handler.config.disk_encryption,
)
fs_handler = FilesystemHandler(arch_config_handler.config.disk_config)
fs_handler.perform_filesystem_operations()
perform_installation(arch_config_handler.args.mountpoint)

View File

@ -87,8 +87,10 @@ disk_encryption = DiskEncryption(
hsm_device=None,
)
disk_config.disk_encryption = disk_encryption
# initiate file handler with the disk config and the optional disk encryption config
fs_handler = FilesystemHandler(disk_config, disk_encryption)
fs_handler = FilesystemHandler(disk_config)
# perform all file operations
# WARNING: this will potentially format the filesystem and delete all data
@ -99,7 +101,6 @@ mountpoint = Path('/tmp')
with Installer(
mountpoint,
disk_config,
disk_encryption=disk_encryption,
kernels=['linux'],
) as installation:
installation.mount_ordered_layout()

View File

@ -211,7 +211,6 @@ def test_config_file_parsing(
groups=['wheel'],
),
],
disk_encryption=None,
services=['service_1', 'service_2'],
root_enc_password=Password(enc_password='password_hash'),
custom_commands=["echo 'Hello, World!'"],