Kickstart installations let us easily script and replicate unattended or semi-unattended installations of Fedora, Red Hat Enterprise Linux or CentOS. The instructions needed to install the operating system are specified, with a dedicated syntax, inside a Kickstart file which is passed to the Anaconda installer. In this tutorial we will see how to reuse an already existing LUKS (Linux Unified Keys Setup) container when performing a Kickstart installation: this is something that cannot be achieved just with Kickstart instructions and requires some extra steps.
In this tutorial you will learn:
- How to use an existing LUKS container when performing a Kickstart installation of Fedora, RHEL or CentOS
- How to create and use an updates.img file to be used with the Anaconda installer.
Software Requirements and Conventions Used
| Category | Requirements, Conventions or Software Version Used |
|---|---|
| System | Fedora/Rhel/CentOS |
| Software | No specific software is needed to follow this tutorial. |
| Other |
|
| Conventions | # – requires given linux commands to be executed with root privileges either directly as a root user or by use of sudo command$ – requires given linux commands to be executed as a regular non-privileged user |
Introduction
Kickstart let us easily replicate and customize operating system installations in ways that are simply impossible to achieve from the Anaconda graphical installer. We can, for example, declare what packages or package groups should be installed on the system and what should be excluded instead.
We also have the chance to execute custom commands before or after the installation is performed, specifying them inside the dedicated %pre and %post sections of the Kickstart file respectively. We will take advantage of this last mentioned feature to use an already existing LUKS device during the installation process.
Encryption with native Kickstart syntax
Creating LUKS containers is quite easy, and can be done by just using native kickstart instructions. Here is an example:
part pv.01 --ondisk=sda --encrypted --luks-type=luks1 --cipher=aes-xts-plain64 --pbkdf-time=5000 --passphrase=secretpassphrase
In the above example, by using the part instruction, we create an encrypted lvm physical volume on the /dev/sda disk. We specify the LUKS version to use (luks1 in this case – at least in recent versions of Fedora luks2 has become the default), the cipher, and the time, expressed in milliseconds, to spend for PBKDF ( Password-Based Key Derivation Function) passphrase processing (it is the equivalent of using the --iter-time option of cryptsetup).
Even if it is not a safe habit, we used also the --passphrase to provide the encryption passphrase: without this option, the installation process would be interrupted, and we would be prompted to provide one interactively.
We can clearly see how, using Kickstart, we get a lot more flexibility compared to a traditional installation; why would we need to perform extra steps, then? There are still some tasks we cannot achieve using just the standard Kickstart syntax. Among other things, we cannot create LUKS containers on raw devices (only on partitions) or specify the hashing algorithm to use for the LUKS key setup, which by default is set to sha256 (nothing wrong with it).
For these reasons we may want to create our partition setup before performing the installation, either manually or by using tools like parted inside the %pre section of the kickstart file itself. We may also just have an existing LUKS setup we don’t want to destroy. In all these cases we must performs the extra steps we will see in a moment.
The kickstart %pre section
The %pre section of a kickstart file is the first one to be parsed when the file is retrieved. It is used to perform custom commands before the installation starts and must be closed explicitly with the %end instruction.
In %pre, the bash shell interpreter is used by default, but others can be specified via the --interpreter option (to use python we would write %pre --interpreter /usr/bin/python). We can use this section to run the commands required to open the existent LUKS container. Here is what we can write:
%pre
iotty="$(tty)"
exec > "${iotty}" 2> "${iotty}"
while true; do
cryptsetup luksOpen /dev/sda1 cryptroot - && break
done
%end
Let’s take a look at the code above. First of all, we store the result of the tty command, which prints the file name of the terminal connected to standard input, into the iotty variable.
With the exec > "${iotty}" 2> "${iotty}" command we redirected standard output and standard error to the same terminal:
this way we will be able to enter the container password when the crytpsetup luksOpen command will be executed and the prompt will be displayed on screen. The command is launched in an infinite loop which is interrupted only if the LUKS container is successfully opened.
If we want need to run a completely unattended installation, we must pass the passphrase directly to cryptsetup (again, this is not recommended). We would write:
%pre echo -n "ourverysecretpassphrase" | cryptsetup luksOpen /dev/sda1 cryptroot - %end
In the example above we passed the passphrase to the standard input of the cryptsetup command via a pipe |: we used the echo command with the -n option to avoid a newline character to be appended at the end of the passphrase.
Patching Fedora 31 anaconda installer
If we try to use an unlocked LUKS container when installing Fedora 31 via Kickstart, we will receive the following
message, and the process will be aborted:
The existing unlocked LUKS device cannot be used for the installation without an encryption key specified for this
device. Please, rescan the storage.
This happens due to this commit introduced in the Fedora 31 version of the Anaconda installer. The code basically checks that an existing LUKS device has a registered key, if it doesn’t the installation is aborted. The problem is that blivet, the python library used by Anaconda to manage partition acquires the key only if the container is opened by it: this can be done from the graphical installer but there is not, at the moment of writing, a Kickstart instruction to unlock an existing LUKS container. I personally commented the commit explaining the situation, and a bug has been opened on red hat bugzilla.
Creating an updates.img file
At the moment the only workaround (that I know of) is to patch the Anaconda source code, commenting the line which executes the control introduced with the commit we mentioned above. The good news is that it is a very simple to operation.
As a first thing, we need to clone the Anaconda git repository, specifically the f31-release branch:
$ git clone https://github.com/rhinstaller/anaconda -b f31-release
Once the repo is cloned, we enter the anaconda directory and modify the pyanaconda/storage/checker.py file: all we have to do is to comment line 619:
def set_default_checks(self):
"""Set the default checks."""
self.checks = list()
self.add_check(verify_root)
self.add_check(verify_s390_constraints)
self.add_check(verify_partition_formatting)
self.add_check(verify_partition_sizes)
self.add_check(verify_partition_format_sizes)
self.add_check(verify_bootloader)
self.add_check(verify_gpt_biosboot)
self.add_check(verify_swap)
self.add_check(verify_swap_uuid)
self.add_check(verify_mountpoints_on_linuxfs)
self.add_check(verify_mountpoints_on_root)
#self.add_check(verify_unlocked_devices_have_key)
self.add_check(verify_luks_devices_have_key)
self.add_check(verify_luks2_memory_requirements)
self.add_check(verify_mounted_partitions)
We save the modification and, from the root of the repository, we launch the makeupdates script which is found in the scripts directory. For the script to be executed we must have python2 installed:
$ ./scripts/makeupdates
The script will generate the updates.img file which will contain our modifications. To check its content we can use the lsinitrd command:
$ lsinitrd updates.img Image: updates.img: 8.0K ======================================================================== Version: Arguments: dracut modules: ======================================================================== drwxr-xr-x 3 egdoc egdoc 0 Jan 30 09:29 . drwxr-xr-x 3 egdoc egdoc 0 Jan 30 09:29 run drwxr-xr-x 3 egdoc egdoc 0 Jan 30 09:29 run/install drwxr-xr-x 3 egdoc egdoc 0 Jan 30 09:29 run/install/updates drwxr-xr-x 3 egdoc egdoc 0 Jan 30 09:29 run/install/updates/pyanaconda drwxr-xr-x 2 egdoc egdoc 0 Jan 30 09:29 run/install/updates/pyanaconda/storage -rw-r--r-- 1 egdoc egdoc 25443 Jan 30 09:29 run/install/updates/pyanaconda/storage/checker.py ========================================================================
The image contains standard dracut modules. We will use this file to “patch” the installer of Fedora 31.
Applying the patch
To apply the modifications contained in the file we just generated, we need to place it somewhere where we can easily access it, perhaps via ftp or http, or even on a local block device, and use the inst.updates parameter to reference it from the Fedora installer image. From the grub menu we highlight the “Install Fedora” menu entry:
Once the menu line is selected, we press the Tab key: the kernel command line associated with the entry is displayed on the bottom of the screen, including the initrd.img parameter:
vmlinuz initrd=initrd.img inst.stage2=hd:LABEL=Fedora-S-dvd-x86_31-31 quiet inst.updates=http://192.168.0.37/updates.img inst.ks=http://192.168.0.37/ks.cfg
At this point we can press enter to boot. With the above modification the installer will not complain anymore about
the unlocked LUKS device, and the installation will proceed without problems.
Conclusions
In this article we saw how to tune a kickstart installation in order to reuse an already existing LUKS device, unlocking it in the %pre section of the kickstart file, and how to apply a small workaround to the Fedora 31 Anaconda installer which would otherwise fail when such type of installation is attempted. If you are curious about the Kickstart syntax please take a look at the online documentation.


