diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index ea9ead6af8..19e6050c25 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -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 @@ def unsafe_json(self) -> dict[str, Any]: '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 @@ def safe_json(self) -> dict[str, Any]: 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 @@ def from_config(cls, args_config: dict[str, Any]) -> '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 @@ def from_config(cls, args_config: dict[str, Any]) -> '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 diff --git a/archinstall/lib/disk/disk_menu.py b/archinstall/lib/disk/disk_menu.py index 24affcc5ad..77a32b7a62 100644 --- a/archinstall/lib/disk/disk_menu.py +++ b/archinstall/lib/disk/disk_menu.py @@ -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 @@ def __init__(self, disk_layout_config: DiskLayoutConfiguration | None): 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 @@ def __init__(self, disk_layout_config: DiskLayoutConfiguration | None): 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 @@ def _define_menu_options(self) -> list[MenuItem]: 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 @@ def run(self) -> DiskLayoutConfiguration | None: 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 @@ def _check_dep_btrfs(self) -> bool: 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 @@ def _prev_btrfs_snapshots(self, item: MenuItem) -> str | None: 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 diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py index 77d539e534..3726606a68 100644 --- a/archinstall/lib/disk/filesystem.py +++ b/archinstall/lib/disk/filesystem.py @@ -27,13 +27,9 @@ 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: diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 7ecd43a4c7..25be5bd7a8 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -3,8 +3,7 @@ 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 @@ def _get_menu_options(self) -> list[MenuItem]: 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 @@ def _update_lang_text(self) -> 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 @@ def _prev_disk_config(self, item: MenuItem) -> str | 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 @@ def _prev_bootloader(self, item: MenuItem) -> str | 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 @@ def _select_disk_config( ) -> 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: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 41d1768e96..73c5db3007 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -62,7 +62,6 @@ def __init__( self, target: Path, disk_config: DiskLayoutConfiguration, - disk_encryption: DiskEncryption | None = None, base_packages: list[str] = [], kernels: list[str] | None = None, ): @@ -74,7 +73,7 @@ def __init__( 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') diff --git a/archinstall/lib/models/device_model.py b/archinstall/lib/models/device_model.py index 6d5ef0ae2e..3714c1e796 100644 --- a/archinstall/lib/models/device_model.py +++ b/archinstall/lib/models/device_model.py @@ -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 @@ def json(self) -> _DiskLayoutConfigurationSerialization: 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 @@ def parse_arg(cls, disk_config: _DiskLayoutConfigurationSerialization) -> DiskLa 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) diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index f778ed717e..51b1264fed 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -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) diff --git a/archinstall/scripts/minimal.py b/archinstall/scripts/minimal.py index 90ef541fe5..d3be42f4fa 100644 --- a/archinstall/scripts/minimal.py +++ b/archinstall/scripts/minimal.py @@ -4,7 +4,6 @@ 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) diff --git a/archinstall/scripts/only_hd.py b/archinstall/scripts/only_hd.py index c3977497d3..48205e264c 100644 --- a/archinstall/scripts/only_hd.py +++ b/archinstall/scripts/only_hd.py @@ -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) diff --git a/examples/full_automated_installation.py b/examples/full_automated_installation.py index 181d20c8d4..d172978388 100644 --- a/examples/full_automated_installation.py +++ b/examples/full_automated_installation.py @@ -87,8 +87,10 @@ 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 @@ with Installer( mountpoint, disk_config, - disk_encryption=disk_encryption, kernels=['linux'], ) as installation: installation.mount_ordered_layout() diff --git a/tests/test_args.py b/tests/test_args.py index e6308925e9..772e4d00c6 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -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!'"],