👩‍🏫[HowTo] Boot without a password for encrypted root partition

Difficulty: ★★★★☆
(According to Tutorial Content Creation Guide)


Needed background info:

Ok let me start by explaining some stuff first for those that need it.


Table of contents:

  1. Problem with respect to encryption and boot sequence
  2. Solution
  3. The password
  4. Securing the system
  5. Addendum’s
  6. Related interesting links

:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

4 Likes

The UEFI-Bios:

The UEFI-Bios of your motherboard will look for, and use, the ESP’s it finds on your system to load boot-loaders for your operating system(s).

  • The order of devices that are searched for that partition is dependent on the physical port your storage device connects to your motherboard.
    This order can be modified in some BIOS’es but not all.
  • This special purpose partition (ESP) is meant to hold all bootloaders for your system and must be formatted as a FAT file system(UEFI-Specs §12.3 File System Format) (eg. fat32(vfat)) without encryption.
  • It is important to note that the UEFI-Bios will only consider the first ESP per storage device.(UEFI-Specs §12.3.2 Partition Discovery)
  • The entries and order of the boot-loaders is kept in NVRAM and can be accesed via the efibootmgr command on most Linux distro’s these days, see man efibootmgr for it’s usage.
  • When Secure-Boot is enabled in the UEFI-Bios settings, these boot-loaders need to be cryptographically signed by at least one signature that is stored inside your NVRAM.
    Otherwise you will get a red message at system start and the UEFI-Bios will refuse to start that boot-loader.

:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

3 Likes

Boot sequence:

  1. Your UEFI-Bios needs to start a boot-loader readable from the ESP.
    These can not be encrypted.
  2. Your bootloader also needs to be able to read it’s configuration files, to show a boot menu of it’s own (this is a different menu as the boot menu provided by the UEFI-Bios), and load the kernel(s) and ramdisk(s).
    • Grub has build-in functionality to decrypt certain encrypted partitions that it supports.
      (But most other bootloaders don’t have that functionality)
  • When Secure-Boot is enabled in the UEFI-Bios settings, the kernel needs to be signed by, at least one signature that is stored inside your NVRAM, or the signature build-into the boot-loader itself, or a signature provided via the Shim-functionality.

So when your BIOS starts your Grub-booloader from the ESP;

  • Grub, in an effort to find and read it’s own configuration files, asks for your encryption password to decrypt any encrypted partitions it finds until it is able to find the files it needs.
  • Because the default install places files that are needed under /boot/grub, it is the default behavior for Grub to ask for the password in that scenario before it can display it’s own menu…

:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

2 Likes

Luks encryption:

Unlike file-systems inside a partition of a storage device, encryption is a virtual container that can not be mounted on it’s own.

  • To “Create” such a container you can use the whole storage device or a partition inside a storage device, or even a regular file.
  • You can afterwards “Open” and “Close” such a container, which will become visible as an entry under /dev/mapper and will be regarded as a raw storage device by the Operating System (kernel).

Because it is regarded as a raw storage device by the Operating System (kernel), you need to create a filesystem inside it to be able to mount that /dev/mapper/somename device…

Luks encryption functionality is accessed via the cryptsetup command.
See man cryptsetup for it’s usage and many pages on the internet that explain in details how that functionality is best used.


:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

2 Likes

Problem with respect to encryption and boot sequence:

We need all parts that are needed to boot our Operating system to be performed inside the initial ramdisk initrd for maximum flexibility.

  1. The initial ramdisk should be loaded from a non-encrypted storage device else we will have a “:chicken:&:egg: problem”…
  2. The initial ramdisk is loaded by the kernel, so the kernel will also need to be loaded from a non-encrypted storage device.
    • The kernel(s) and ramdisk(s) are installed under /boot by default.
  3. The kernel is loaded by the boot-loader
  4. The boot-loader is loaded by the UEFI-Bios from the ESP.
  5. The UEFI-Bios has no decryption functionality, so the ESP is a non-encrypted storage by definition.

Because the only storage that is guaranteed to be non-encrypted is the ESP in the UEFI era, we will use that as non-encrypted storage for all the above.

  1. The ESP itself will be unavailable to the operating system (kernel) until the system is booted enough to mount the ESP inside the root filesystem which we want to be encrypted.
    This results in a “:chicken:&:egg: problem”…
  2. Most distributions (Manjaro inclusive) mount the ESP at /boot/efi by default.
  3. Most boot-loaders need to access extra files for it’s functionality, like configurations and drivers etc, so these files will also need to be loaded from the ESP.
    • For example Grub needs to access files that are installed by default under /boot/grub .

We need a change so that the ESP is decoupled from the root file-system of our booted system and at same time allows the bootloaders to access their needed files.

So any needed decryption needs to be performed inside the initial ramdisk on it’s own.


:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

2 Likes

Solution:

The solution is actually rather logical :wink:

  1. Mount the ESP at /efi which is a recommended path in The Boot Loader Specification used by the Systemd system manager that most distributions are using these days. (Manjaro inclusive)
  2. Make the kernel(s) and ramdisk(s), which are installed by default under /boot available to the UEFI-Bios itself, by placing them inside the ESP.
  3. Make the needed files by the boot-loader available in non-encrypted form, by placing them inside the ESP.
    • The files needed by the Grub-bootloader are by default installed under /boot/grub
    • Other boot-loaders like sd-boot use a directory inside the ESP itself.

So what is needed to have a solution that combines all three logical parts above?

  • (1) is straight forward: (one of the below)
    • An entry inside the /etc/fstab
      These get automatically converted into the below form.
    • A separate mount unit for use by systemd directly.
  • (2+3) needs special magic to acomplish:
    The “special magic” is to mount a subdirectory of the ESP as /boot in such a way that it will prevent polution of other important files inside the ESP that are needed by the UEFI-Bios.
    This “magic” is done using the bind-mount functionality, which allows you to mount any subdirectory at a different path of your file-system. :face_with_hand_over_mouth:
    Using one of the below:
    • An entry inside the /etc/fstab
      These get automatically converted into the below form.
    • A separate mount unit for use by systemd directly.

So if we create a subdirectory in the root of the ESP to hold everything that is normally under /boot and mount that subdirectory at /boot we will have solved this “:chicken:&:egg: problem”.


:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

2 Likes

The password:

Ok so after doing the above we are still left with the functionality that asks for the decryption password.
We can provide the decryption password using a combination of a configuration inside the /etc/crypttab file plus a text file with the decryption password as content.
(See man crypttab)

To accomplish the final result of “booting without the need to provide a decryption password while using an encrypted root filesystem”:
We need to make it available inside the initial ramdisk. ( initrd )
This is accomplished by:

  1. Making use of /etc/crypttab.initramfs which will be copied into the initial ramdisk as /etc/crypttab by the mkinitcpio command.

    • Example /etc/crypttab.initramfs :
      # Configuration for encrypted block devices.
      # See crypttab(5) for details.
      
      # NOTE: Do not list your root (/) partition here, it must be set up
      #       beforehand by the initramfs (/etc/mkinitcpio.conf).
      luks2           UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx       /etc/cryptfs-keys.d/root
      
      This setting provides the decryption password used for the luks2 container inside the /etc/cryptfs-keys.d/root file.
      The UUID used here should be the UUID you get in the output of lsblk --fs for the physical storage you used to create the encrypted container in.
      This method should not make a difference weather you used a partition or a whole disk to create the encrypted container in.
  2. Making use of the FILES= array inside /etc/mkinitcpio.conf to copy, the text file holding the decryption password as content, into the initial ramdisk.
    :warning: Make sure the contents of these files don’t include a newline at end, because that will be provided as part of the password also !
    :information_source: Make use of printf “%s” “somepassword” > yourpasswordfile

    • Example snippet inside /etc/mkinitcpio.conf :
      FILES=(
          /etc/cryptfs-keys.d/root
      #    /etc/cryptfs-keys.d/swap
      )
      
      This instructs to copy the /etc/cryptfs-keys.d/root file into the initial ramdisk at same path.
  3. Making use of the encrypt or sd-encrypt hooks, depending on what you need, in your /etc/mkinitcpio.conf
    (See the addendum and https://wiki.archlinux.org/index.php/Mkinitcpio#Common_hooks)

  4. And the last step that is needed, is to tell the initial ramdisk which file-system to use as root file-system for the booted system.
    This is accomplished using the kernel parameter: root=/dev/mapper/container_name
    Yes you need to use a container name here because you want to access the file-system inside the encrypted container after it has been decrypted.

    • When using full-disk encryption this file-system can be one of:
      • An LV inside an LVM inside the encrypted container.
      • A sub-volume inside an BTRFS inside the encrypted container.
    • For cases where you created a regular file-system directly inside the encrypted container, you should use the same name as you used inside /etc/crypttab.initramfs , this would be luks2 in the example file above.

:notebook: After any changes don’t forget to update:

  • Your initial ramdisk(s) using sudo mkinitcpio -p :wink:
  • Your boot-loader configuration(s) :wink:

:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

2 Likes

Securing the system:

The only part left for those that are concerned with the security of this setup, because the decryption password is stored as plain text inside the initial ramdisk, is to have that part secured also.
The beauty of the above method is that you are able to “move” or “replace” the ESP on the static disk with one on a removable device like an USB-Stick.
With your ESP on a USB-Stick no one will be able to boot or read any data of your system without that USB-Stick.

Of course you need to:

  • create at least 2 copies of said USB-stick in case you lose it / it dies / …
  • securely wipe the contents of the ESP on the static disk after this move if you started this journey with an ESP on a static disk :wink:
    :warning: Be 100% sure that all your configuration is addressing the USB-Stick before this wipe and test it !

:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

2 Likes

Addendum’s:

My current /etc/mkinitcpio.conf
# vim:set ft=sh
# MODULES
# The following modules are loaded before any boot hooks are
# run.  Advanced users may wish to specify all system modules
# in this array.  For instance:
#     MODULES=(piix ide_disk reiserfs)
MODULES=(
    usbhid
    xhci_pci
    xhci_hcd
    crc32c-intel
    nvidia
    nvidia_modeset
    nvidia_uvm
    nvidia_drm
)

# BINARIES
# This setting includes any additional binaries a given user may
# wish into the CPIO image.  This is run last, so it may be used to
# override the actual binaries included by a given hook
# BINARIES are dependency parsed, so you may safely ignore libraries
BINARIES=()

# FILES
# This setting is similar to BINARIES above, however, files are added
# as-is and are not parsed in any way.  This is useful for config files.
FILES=(
    /etc/cryptfs-keys.d/root
#    /etc/cryptfs-keys.d/swap
)

# HOOKS
# This is the most important setting in this file.  The HOOKS control the
# modules and scripts added to the image, and what happens at boot time.
# Order is important, and it is recommended that you do not change the
# order in which HOOKS are added.  Run 'mkinitcpio -H <hook name>' for
# help on a given hook.
# 'base' is _required_ unless you know precisely what you are doing.
# 'udev' is _required_ in order to automatically load modules
# 'filesystems' is _required_ unless you specify your fs modules in MODULES
# Examples:
##   This setup specifies all modules in the MODULES setting above.
##   No raid, lvm2, or encrypted root is needed.
#    HOOKS=(base)
#
##   This setup will autodetect keyboard keymap all modules for your system and should
##   work as a sane default
#    HOOKS=(base udev autodetect keyboard keymap block encrypt lvm2 filesystems)
#
##   This setup will generate a 'full' image which supports most systems.
##   No autodetection is done.
#    HOOKS=(base udev block encrypt lvm2 filesystems)
#
##   This setup assembles a pata mdadm array with an encrypted root FS.
##   Note: See 'mkinitcpio -H mdadm' for more information on raid devices.
#    HOOKS=(base udev block lvm2 mdadm encrypt filesystems)
#
##   This setup loads an lvm2 volume group on a usb device.
#    HOOKS=(base udev block encrypt lvm2 filesystems)
#
##   NOTE: If you have /usr on a separate partition, you MUST include the
#    usr, fsck and shutdown hooks.
#HOOKS=(base udev autodetect keymap modconf block encrypt lvm2 filesystems keyboard fsck)
HOOKS=(
    base
    systemd
    autodetect
    modconf
    block
    keyboard
    keymap
#    sd-vconsole
    sd-encrypt
    sd-lvm2
#    lvm2
    filesystems
    bootsplash-manjaro
#    bootsplash-vendor
#    bootsplash-xfce
    fsck
#    consolefont
)

# COMPRESSION
# Use this to compress the initramfs image. By default, gzip compression
# is used. Use 'cat' to create an uncompressed image.
#COMPRESSION="gzip"
#COMPRESSION="bzip2"
#COMPRESSION="lzma"
#COMPRESSION="xz"
#COMPRESSION="lzop"
#COMPRESSION="lz4"
#COMPRESSION="zstd"

# COMPRESSION_OPTIONS
# Additional options for the compressor
#COMPRESSION_OPTIONS=()
Example mount units for the ESP
efi.mount
# /etc/systemd/system/efi.mount
#
# To install use:
#       systemctl enable --now efi.automount
#       systemctl disable --now efi.automount
#
[Unit]
Description=ESP
Documentation=man:systemd.mount(5)
Documentation=man:systemd.automount(5)
After=swap.target

# These get set automatically
#Conflicts=umount.target
#Before=umount.target
#After=-.mount

# We use noauto, so leave this commented.
#Before=local-fs.target

[Mount]
Where=/efi
Type=vfat
Options=noauto,nofail,rw,gid=adm,relatime,fmask=0002,dmask=0002,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro
TimeoutSec=30
efi.automount
# /etc/systemd/system/efi.automount
[Unit]
Description=ESP
Documentation=man:systemd.mount(5)
Documentation=man:systemd.automount(5)

[Automount]
Where=/efi
DirectoryMode=0775
TimeoutIdleSec=30

[Install]
#WantedBy=multi-user.target
WantedBy=local-fs.target
Also=boot.automount
efi.mount.d/hd.conf
# /etc/systemd/system/efi.mount.d/hd.conf
[Unit]
Description=ESP (HD)
After=blockdev@dev-disk-by\x2dlabel-ESP.target
BindsTo=dev-disk-by\x2dlabel-ESP.device

# Do a fsck on the partition
Requires=systemd-fsck@dev-disk-by\x2dlabel-ESP.service
After=systemd-fsck@dev-disk-by\x2dlabel-ESP.service

[Mount]
What=/dev/disk/by-label/ESP
Example mount units for the /boot
boot.mount
# /etc/systemd/system/boot.mount
#
# To install use:
#       systemctl enable --now boot.automount
#       systemctl disable --now boot.automount
#
[Unit]
Description=/boot
Documentation=man:systemd.mount(5)
Documentation=man:systemd.automount(5)
Conflicts=umount.target
Before=local-fs.target umount.target
RequiresMountsFor=/efi
After=efi.mount
BindsTo=efi.mount

[Mount]
Where=/boot
What=/efi/Manjaro
Type=none
Options=bind
TimeoutSec=30
boot.automount
# /etc/systemd/system/boot.automount
[Unit]
Description=/boot
Documentation=man:systemd.mount(5)
Documentation=man:systemd.automount(5)
After=efi.automount
BindsTo=efi.automount

[Automount]
Where=/boot
DirectoryMode=0775
TimeoutIdleSec=30

[Install]
#WantedBy=multi-user.target
WantedBy=local-fs.target
Also=efi.automount

:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

2 Likes

Related interesting links:


:woman_teacher: Feel free to comment with your opinions and eventual corrections. :raising_hand_man: :raising_hand_woman:

4 Likes

Dracut+ Clevis(-git) + secure boot or
Luks-tpm + mkinitcpio + secure boot + sbupdate
=
No need in usb sticks, Windows BitLocker-like experience.

2 Likes

You cought my eye, do you have any blog posts about it?

How can clevis be invoked during the boot phase? It looks like a tool used after boot.
And also it requires to trust the internal TPM?

1 Like

I’d suggest you to watch the explanation of implementation of that given by the one of Arch Linux supporters and maintainers: https://youtu.be/AyaOoNrYVkc
Watch from 16:35. It is comfortable to do it with x1.25 speed. Or even x1.5.
He’s also a member of the team working on tpm2 support in Linux, if I’m not mistaken.

As for my own description, I had a post on the old (now read-only) forum, but that was a time when tpm2 commands were slightly different, that was applicable to mkinitcpio and sbupdate only.

Clevis has hooks for dracut (and maybe mkinitcpio) so it embeds itself into unified kernel+initrd+cmdline and starts during the boot just like any other service. Of course first you need to set it up in userspace with binding it to luks device of your choice, and then rebuild a unified image with dracut.

Long story short, just watch the video above.

3 Likes