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 ========================================================================
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:
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.