Save encryption configuration (#1672)
* Save encryption configuration * Fix deserialization problem * Added .part_uuid to MapperDev --------- Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com> Co-authored-by: Anton Hvornum <anton@hvornum.se> Co-authored-by: Anton Hvornum <anton.feeds+github@gmail.com>
This commit is contained in:
parent
b2fc71c9e5
commit
83f4b4178f
|
|
@ -240,6 +240,14 @@ def load_config():
|
||||||
superusers = arguments.get('!superusers', None)
|
superusers = arguments.get('!superusers', None)
|
||||||
arguments['!users'] = User.parse_arguments(users, superusers)
|
arguments['!users'] = User.parse_arguments(users, superusers)
|
||||||
|
|
||||||
|
if arguments.get('disk_encryption', None) is not None and arguments.get('disk_layouts', None) is not None:
|
||||||
|
password = arguments.get('encryption_password', '')
|
||||||
|
arguments['disk_encryption'] = DiskEncryption.parse_arg(
|
||||||
|
arguments['disk_layouts'],
|
||||||
|
arguments['disk_encryption'],
|
||||||
|
password
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def post_process_arguments(arguments):
|
def post_process_arguments(arguments):
|
||||||
storage['arguments'] = arguments
|
storage['arguments'] = arguments
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class ConfigurationOutput:
|
||||||
self._disk_layout_file = "user_disk_layout.json"
|
self._disk_layout_file = "user_disk_layout.json"
|
||||||
|
|
||||||
self._sensitive = ['!users']
|
self._sensitive = ['!users']
|
||||||
self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run', 'disk_encryption']
|
self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run']
|
||||||
|
|
||||||
self._process_config()
|
self._process_config()
|
||||||
|
|
||||||
|
|
@ -71,6 +71,9 @@ class ConfigurationOutput:
|
||||||
else:
|
else:
|
||||||
self._user_config[key] = self._config[key]
|
self._user_config[key] = self._config[key]
|
||||||
|
|
||||||
|
if key == 'disk_encryption': # special handling for encryption password
|
||||||
|
self._user_credentials['encryption_password'] = self._config[key].encryption_password
|
||||||
|
|
||||||
def user_config_to_json(self) -> str:
|
def user_config_to_json(self) -> str:
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
'config_version': storage['__version__'], # Tells us what version was used to generate the config
|
'config_version': storage['__version__'], # Tells us what version was used to generate the config
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class DiskEncryptionMenu(AbstractSubMenu):
|
||||||
Selector(
|
Selector(
|
||||||
_('Partitions'),
|
_('Partitions'),
|
||||||
func=lambda preset: select_partitions_to_encrypt(self._disk_layouts, preset),
|
func=lambda preset: select_partitions_to_encrypt(self._disk_layouts, preset),
|
||||||
display_func=lambda x: f'{len(x)} {_("Partitions")}' if x else None,
|
display_func=lambda x: f'{sum([len(y) for y in x.values()])} {_("Partitions")}' if x else None,
|
||||||
dependencies=['encryption_password'],
|
dependencies=['encryption_password'],
|
||||||
default=self._preset.partitions,
|
default=self._preset.partitions,
|
||||||
preview_func=self._prev_disk_layouts,
|
preview_func=self._prev_disk_layouts,
|
||||||
|
|
@ -86,9 +86,14 @@ class DiskEncryptionMenu(AbstractSubMenu):
|
||||||
def _prev_disk_layouts(self) -> Optional[str]:
|
def _prev_disk_layouts(self) -> Optional[str]:
|
||||||
selector = self._menu_options['partitions']
|
selector = self._menu_options['partitions']
|
||||||
if selector.has_selection():
|
if selector.has_selection():
|
||||||
partitions: List[Any] = selector.current_selection
|
partitions: Dict[str, Any] = selector.current_selection
|
||||||
|
|
||||||
|
all_partitions = []
|
||||||
|
for parts in partitions.values():
|
||||||
|
all_partitions += parts
|
||||||
|
|
||||||
output = str(_('Partitions to be encrypted')) + '\n'
|
output = str(_('Partitions to be encrypted')) + '\n'
|
||||||
output += current_partition_layout(partitions, with_title=False)
|
output += current_partition_layout(all_partitions, with_title=False)
|
||||||
return output.rstrip()
|
return output.rstrip()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -132,7 +137,7 @@ def select_hsm(preset: Optional[Fido2Device] = None) -> Optional[Fido2Device]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def select_partitions_to_encrypt(disk_layouts: Dict[str, Any], preset: List[Any]) -> List[Any]:
|
def select_partitions_to_encrypt(disk_layouts: Dict[str, Any], preset: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
# If no partitions was marked as encrypted, but a password was supplied and we have some disks to format..
|
# If no partitions was marked as encrypted, but a password was supplied and we have some disks to format..
|
||||||
# Then we need to identify which partitions to encrypt. This will default to / (root).
|
# Then we need to identify which partitions to encrypt. This will default to / (root).
|
||||||
all_partitions = []
|
all_partitions = []
|
||||||
|
|
@ -153,10 +158,17 @@ def select_partitions_to_encrypt(disk_layouts: Dict[str, Any], preset: List[Any]
|
||||||
|
|
||||||
match choice.type_:
|
match choice.type_:
|
||||||
case MenuSelectionType.Reset:
|
case MenuSelectionType.Reset:
|
||||||
return []
|
return {}
|
||||||
case MenuSelectionType.Skip:
|
case MenuSelectionType.Skip:
|
||||||
return preset
|
return preset
|
||||||
case MenuSelectionType.Selection:
|
case MenuSelectionType.Selection:
|
||||||
return choice.value # type: ignore
|
selections: List[Any] = choice.value # type: ignore
|
||||||
|
partitions = {}
|
||||||
|
|
||||||
return []
|
for path, device in disk_layouts.items():
|
||||||
|
for part in selections:
|
||||||
|
if part in device.get('partitions', []):
|
||||||
|
partitions.setdefault(path, []).append(part)
|
||||||
|
|
||||||
|
return partitions
|
||||||
|
return {}
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ class Filesystem:
|
||||||
format_options = partition.get('options',[]) + partition.get('filesystem',{}).get('format_options',[])
|
format_options = partition.get('options',[]) + partition.get('filesystem',{}).get('format_options',[])
|
||||||
disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption')
|
disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption')
|
||||||
|
|
||||||
if disk_encryption and partition in disk_encryption.partitions:
|
if disk_encryption and partition in disk_encryption.all_partitions:
|
||||||
if not partition['device_instance']:
|
if not partition['device_instance']:
|
||||||
raise DiskError(f"Internal error caused us to loose the partition. Please report this issue upstream!")
|
raise DiskError(f"Internal error caused us to loose the partition. Please report this issue upstream!")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import getpass
|
import getpass
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List, Dict
|
||||||
|
|
||||||
from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes
|
from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes
|
||||||
from ..disk.partition import Partition
|
from ..disk.partition import Partition
|
||||||
|
|
@ -16,6 +18,21 @@ class Fido2Device:
|
||||||
manufacturer: str
|
manufacturer: str
|
||||||
product: str
|
product: str
|
||||||
|
|
||||||
|
def json(self) -> Dict[str, str]:
|
||||||
|
return {
|
||||||
|
'path': str(self.path),
|
||||||
|
'manufacturer': self.manufacturer,
|
||||||
|
'product': self.product
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse_arg(cls, arg: Dict[str, str]) -> 'Fido2Device':
|
||||||
|
return Fido2Device(
|
||||||
|
Path(arg['path']),
|
||||||
|
arg['manufacturer'],
|
||||||
|
arg['product']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Fido2:
|
class Fido2:
|
||||||
_loaded: bool = False
|
_loaded: bool = False
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ class Installer:
|
||||||
|
|
||||||
# we manage the encrypted partititons
|
# we manage the encrypted partititons
|
||||||
if self._disk_encryption:
|
if self._disk_encryption:
|
||||||
for partition in self._disk_encryption.partitions:
|
for partition in self._disk_encryption.all_partitions:
|
||||||
# open the luks device and all associate stuff
|
# open the luks device and all associate stuff
|
||||||
loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['device_instance'].path).name}"
|
loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['device_instance'].path).name}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -279,8 +279,8 @@ class GlobalMenu(AbstractMenu):
|
||||||
output = str(_('Encryption type')) + f': {enc_type}\n'
|
output = str(_('Encryption type')) + f': {enc_type}\n'
|
||||||
output += str(_('Password')) + f': {secret(encryption.encryption_password)}\n'
|
output += str(_('Password')) + f': {secret(encryption.encryption_password)}\n'
|
||||||
|
|
||||||
if encryption.partitions:
|
if encryption.all_partitions:
|
||||||
output += 'Partitions: {} selected'.format(len(encryption.partitions)) + '\n'
|
output += 'Partitions: {} selected'.format(len(encryption.all_partitions)) + '\n'
|
||||||
|
|
||||||
if encryption.hsm_device:
|
if encryption.hsm_device:
|
||||||
output += f'HSM: {encryption.hsm_device.manufacturer}'
|
output += f'HSM: {encryption.hsm_device.manufacturer}'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum, auto
|
from enum import Enum
|
||||||
from typing import Optional, List, Dict, TYPE_CHECKING, Any
|
from typing import Optional, List, Dict, TYPE_CHECKING, Any
|
||||||
|
|
||||||
from ..hsm.fido import Fido2Device
|
from ..hsm.fido import Fido2Device
|
||||||
|
|
@ -9,8 +11,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class EncryptionType(Enum):
|
class EncryptionType(Enum):
|
||||||
Partition = auto()
|
Partition = 'partition'
|
||||||
# FullDiskEncryption = auto()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _encryption_type_mapper(cls) -> Dict[str, 'EncryptionType']:
|
def _encryption_type_mapper(cls) -> Dict[str, 'EncryptionType']:
|
||||||
|
|
@ -35,9 +36,55 @@ class EncryptionType(Enum):
|
||||||
class DiskEncryption:
|
class DiskEncryption:
|
||||||
encryption_type: EncryptionType = EncryptionType.Partition
|
encryption_type: EncryptionType = EncryptionType.Partition
|
||||||
encryption_password: str = ''
|
encryption_password: str = ''
|
||||||
partitions: List[str] = field(default_factory=list)
|
partitions: Dict[str, List[Dict[str, Any]]] = field(default_factory=dict)
|
||||||
hsm_device: Optional[Fido2Device] = None
|
hsm_device: Optional[Fido2Device] = None
|
||||||
|
|
||||||
def generate_encryption_file(self, partition) -> bool:
|
@property
|
||||||
return partition in self.partitions and partition['mountpoint'] != '/'
|
def all_partitions(self) -> List[Dict[str, Any]]:
|
||||||
|
_all: List[Dict[str, Any]] = []
|
||||||
|
for parts in self.partitions.values():
|
||||||
|
_all += parts
|
||||||
|
return _all
|
||||||
|
|
||||||
|
def generate_encryption_file(self, partition) -> bool:
|
||||||
|
return partition in self.all_partitions and partition['mountpoint'] != '/'
|
||||||
|
|
||||||
|
def json(self) -> Dict[str, Any]:
|
||||||
|
obj = {
|
||||||
|
'encryption_type': self.encryption_type.value,
|
||||||
|
'partitions': self.partitions
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.hsm_device:
|
||||||
|
obj['hsm_device'] = self.hsm_device.json()
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse_arg(
|
||||||
|
cls,
|
||||||
|
disk_layout: Dict[str, Any],
|
||||||
|
arg: Dict[str, Any],
|
||||||
|
password: str = ''
|
||||||
|
) -> 'DiskEncryption':
|
||||||
|
# we have to map the enc partition config to the disk layout objects
|
||||||
|
# they both need to point to the same object as it will get modified
|
||||||
|
# during the installation process
|
||||||
|
enc_partitions: Dict[str, List[Dict[str, Any]]] = {}
|
||||||
|
|
||||||
|
for path, partitions in disk_layout.items():
|
||||||
|
conf_partitions = arg['partitions'].get(path, [])
|
||||||
|
for part in partitions['partitions']:
|
||||||
|
if part in conf_partitions:
|
||||||
|
enc_partitions.setdefault(path, []).append(part)
|
||||||
|
|
||||||
|
enc = DiskEncryption(
|
||||||
|
EncryptionType(arg['encryption_type']),
|
||||||
|
password,
|
||||||
|
enc_partitions
|
||||||
|
)
|
||||||
|
|
||||||
|
if hsm := arg.get('hsm_device', None):
|
||||||
|
enc.hsm_device = Fido2Device.parse_arg(hsm)
|
||||||
|
|
||||||
|
return enc
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ def select_harddrives(preset: List[str] = []) -> List[str]:
|
||||||
selected_harddrive = Menu(
|
selected_harddrive = Menu(
|
||||||
title,
|
title,
|
||||||
list(options.keys()),
|
list(options.keys()),
|
||||||
preset_values=preset,
|
|
||||||
multi=True,
|
multi=True,
|
||||||
allow_reset=True,
|
allow_reset=True,
|
||||||
allow_reset_warning_msg=warning
|
allow_reset_warning_msg=warning
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue