Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/luks.py
diff options
context:
space:
mode:
authorAnton Hvornum <anton@hvornum.se>2021-11-23 23:09:33 +0000
committerGitHub <noreply@github.com>2021-11-24 00:09:33 +0100
commite729457b6c12a00b17207254ee72e98b78912f8d (patch)
tree554c13c7a363ccc5ef2c25873af15598df9a2ba3 /archinstall/lib/luks.py
parent29736c4a051d2c72bcbf0b20abdebc6992a92e4b (diff)
Support encrypting multiple partitions (#759)
* Added support for storing disk encryption keyfiles and add them to a keyslot. * Added a luks2().add_key() function in order to inject a keyfile into a keyslot on a encrypted volume. * Simplified 'missing encryption password' logic in Filesystem(). Added a call to luks2().add_key() after the root-password is set on the volume, to add the keyfile in slot 2 * Adding in password handling in luks2().add_key(). It's required to enter a previous passphrase to unlock the volume and add a new keyslot. Also simplified the handling of partition in Installer().mount_ordered_layout() * Adding in encryption on all partitions except /boot when encryption is opted in * Removed setting size on Partition() as it's a read only value. No idea how Partition().size = size hasn't caused an issue before. Removed size=X argument to Partition() * Added a uniqueness to the loopdevice name. This should ensure that multiple encrypted volumes can be opened at the same time, except for Partition().detect_inner_filesystem() operations which can only happen one at a time since they share namespace. This should never be an issue since archinstall is single threaded and no concurrent operations can/should happen. * Added partprobe() as part of disk/helpers.py, added a /dev/ -> UUID mapper function called convert_device_to_uuid(path). Added a luks2().crypttab() function that sets up a /etc/crypttab entry. * Moved the responsability for telling archinstall to generate a keyfile from Filesystem() to user_interaction.py. This should in the future be a user-input based value, and not something the Filesystem() automatically dictates. * Added a retry mechanism to luks2().encrypt() to avoid having to re-start the installation when a device simply wasn't up yet. * Swapping UUID= lookup from loopdev to physdev.
Diffstat (limited to 'archinstall/lib/luks.py')
-rw-r--r--archinstall/lib/luks.py43
1 files changed, 35 insertions, 8 deletions
diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py
index b93403ef..55eaa62f 100644
--- a/archinstall/lib/luks.py
+++ b/archinstall/lib/luks.py
@@ -4,11 +4,11 @@ import os
import pathlib
import shlex
import time
-
-from .disk import Partition
-from .general import SysCommand
+from .disk import Partition, convert_device_to_uuid
+from .general import SysCommand, SysCommandWorker
from .output import log
from .exceptions import SysCallError, DiskError
+from .storage import storage
class luks2:
def __init__(self, partition, mountpoint, password, key_file=None, auto_unmount=False, *args, **kwargs):
@@ -78,8 +78,17 @@ class luks2:
])
try:
- # Try to setup the crypt-device
- cmd_handle = SysCommand(cryptsetup_args)
+ # Retry formatting the volume because archinstall can some times be too quick
+ # which generates a "Device /dev/sdX does not exist or access denied." between
+ # setting up partitions and us trying to encrypt it.
+ for i in range(storage['DISK_RETRY_ATTEMPTS']):
+ if (cmd_handle := SysCommand(cryptsetup_args)).exit_code != 0:
+ time.sleep(storage['DISK_TIMEOUTS'])
+ else:
+ break
+
+ if cmd_handle.exit_code != 0:
+ raise DiskError(f'Could not encrypt volume "{partition.path}": {b"".join(cmd_handle)}')
except SysCallError as err:
if err.exit_code == 256:
log(f'{partition} is being used, trying to unmount and crypt-close the device and running one more attempt at encrypting the device.', level=logging.DEBUG)
@@ -108,9 +117,6 @@ class luks2:
else:
raise err
- if cmd_handle.exit_code != 0:
- raise DiskError(f'Could not encrypt volume "{partition.path}": {b"".join(cmd_handle)}')
-
return key_file
def unlock(self, partition, mountpoint, key_file):
@@ -146,3 +152,24 @@ class luks2:
def format(self, path):
if (handle := SysCommand(f"/usr/bin/cryptsetup -q -v luksErase {path}")).exit_code != 0:
raise DiskError(f'Could not format {path} with {self.filesystem} because: {b"".join(handle)}')
+
+ def add_key(self, path :pathlib.Path, password :str):
+ if not path.exists():
+ raise OSError(2, f"Could not import {path} as a disk encryption key, file is missing.", str(path))
+
+ log(f'Adding additional key-file {path} for {self.partition}', level=logging.INFO)
+
+ worker = SysCommandWorker(f"/usr/bin/cryptsetup -q -v luksAddKey {self.partition.path} {path}")
+ pw_injected = False
+ while worker.is_alive():
+ if b'Enter any existing passphrase' in worker and pw_injected is False:
+ worker.write(bytes(password, 'UTF-8'))
+ pw_injected = True
+
+ if worker.exit_code != 0:
+ raise DiskError(f'Could not add encryption key {path} to {self.partition} because: {worker}')
+
+ def crypttab(self, installation, key_path :str, options=["luks", "key-slot=1"]):
+ log(f'Adding a crypttab entry for key {key_path} in {installation}', level=logging.INFO)
+ with open(f"{installation.target}/etc/crypttab", "a") as crypttab:
+ crypttab.write(f"{self.mountpoint} UUID={convert_device_to_uuid(self.partition.path)} {key_path} {','.join(options)}\n") \ No newline at end of file