User:AdaptL/Adam's EFI Install Guide
This page documents some weird features of linux/gentoo that I take advantage of in my EFI setup. Hopefully this is useful for others, because much of this knowledge cannot be easily intuited from publicly-facing documentation. Some of this is taken from Sasaki's Guide.
Windows dual booting
While desktop linux has become increasingly capable of new workflows, many closed-sourced programs still don't have open source alternates. Some programs like Matlab will have more efficient windows implementations. Many windows programs such as Fusion360 can be used within a Windows VM, but other programs (including most video games with anti-cheat software) will try resist virtualization. The VM-detection features of anti-cheat software can generally be bypassed, but not on a reliable basis because anti-cheat developers may discover new heuristics that can be used to distinguish VM users before they are patched. Remote-testing anti-cheat spyware like Honorlock will result dystopian real-world consequences if the use of a virtual machine is detected, so clearly some users *must* use occasionally windows.
If you *must* install windows on the same computer as linux, this can be accomplished through "Dual-Booting": the ability to boot either windows or linux from startup. Typically it is easier to use a separate old or used computer for this purpose; over time it will become more difficult to separate windows and linux installations on the same machine due to efforts from corporations and military-industrial complex to implement Trusted Computing features.
In order to dual-boot you must install windows first.
Note that a normal "offline" windows account can still be created by disconnecting the PC from the internet during the installation process.
Also note that it is no longer necessary to purchase a windows license key because microsoft has moved from license funding model to a subscription/advertisement/surveillance funding model. As such, the new windows installer has an option to install an unlicensed version. The only difference with the unlicensed version is that a watermark will be displayed in the corner of the screen. If you have removed windows from another computer where it was pre-installed (the cost of that license key has already been covered by the manufacturer), the motherboard's license key can be found (and re-used) with the sys-apps/dmidecode command, however note that licensed copies of windows still have the same advertisement/surveillance features as unlicensed copies.
After windows is installed, you can use diskmgr.exe (run as administrator) to shrink the windows installation to make room for the linux root partition on the same disk.
Elsewhere, the gentoo wiki describes how to dual-boot with GRUB on the same drive by using the sys-boot/os-prober package. However, I don't recommend this technique because windows has a tendency to tamper or otherwise overwrite the EFI partition, possibly bricking your dual-boot setup. I (and Sakaki) keep our linux bootloader on a removable (USB) drive. Setting the boot preference (in your BIOS) to prioritize booting from the USB over the internal drive will result in a system with the following properties:
- Without the USB drive inserted, windows boots and functions normally, and can't read the contents of the linux root partition if it is encrypted with LUKS.
- With the USB drive inserted, linux boots instead. It can read from the windows root partition unless it is encrypted with bitlocker.
- Windows cannot write to the linux boot drive (unless it is plugged in AFTER turning the computer on)
- No intermediate bootloader (GRUB) is necessary to select windows or linux.
Windows has access to a number of confusing and frustrating hibernation or sleep states, and the designers of windows seem to prefer making the computer sleep or hibernate-to-disk by default when the power button is pushed. Because of this, I recommend buying a USB drive with a physical write-protection switch, so you don't give windows the ability to write to your linux boot drive if you plug it in while falsely thinking that the computer was shut off.
In order to boot linux off a removable drive in this fashion, it needs to be in an EFI boot partition. An EFI boot partition is just a FAT32 filesystem partition (with the
esp flags set on) within a drive with a GUID partition table (GPT). The executable within the partition that actually gets booted is the /EFI/boot/bootx64.efi file. As far as I know, this is the only file of importance.
Hybernation with encryption
Most linux distributions support encryption of the root partition through LUKS. Most linux distributions also support hibernation (saving all of the memory to storage before shutting off, which would otherwise be lost) and resume (reading that data from storage and continuing the computer as it left off) with either a swapfile or swap partition. Gentoo Linux is the only linux distribution (as of April 2023, that I know of) that has a publicly documented way to have both on the same system: Doing so requires an Initramfs that will prompt the user to enter their decryption password, unlock the encrypted partitions, mount them, resume from the swap partition (if needed), and
kexec into the new kernel. Genkernel supports the creation of an initramfs with this functionality.
The implementation of these two features together is of particular importance because a common alternative to hibernation (suspend-to-disk) is sleep (suspend-to-ram). Suspend-to-ram will keep the encryption keys in volatile memory, which can be extracted through various methods including Coldboot Attacks, Direct-Memory Attacks, Electromagnetic Attacks, and other side-channels. Even if you are not worried about feds breaking into your computer, some of these methods are so simple that it's surprising that laptop thieves have not caught on to them yet (probably because most windows laptops are not even encrypted by default). Put simply, leaving a computer with the encryption keys in RAM is like leaving a car with the keys in the ignition. Hibernation+LUKS lets you take the keys out of the ignition and conveniently pick up later where you left off.
Lastly, messing around with hibernate (like writing to the disk between hibernate and resume) can lead to data loss. Heed this warning from the kernel docs:
Warning BIG FAT WARNING If you touch anything on disk between suspend and resume… …kiss your data goodbye. If you do resume from initrd after your filesystems are mounted… …bye bye root partition. [this is actually same case as above] If you have unsupported ( ) devices using DMA, you may have some problems. If your disk driver does not support suspend… (IDE does), it may cause some problems, too. If you change kernel command line between suspend and resume, it may do something wrong. If you change your hardware while system is suspended… well, it was not good idea; but it will probably only crash. ( ) suspend/resume support is needed to make it safe. If you have any filesystems on USB devices mounted before software suspend, they won’t be accessible after resume and you may lose data, as though you have unplugged the USB devices with mounted filesystems on them; see the FAQ below for details. (This is not true for more traditional power states like “standby”, which normally don’t turn USB off.)
Setting up LUKS
Create a new blank partition for the root and swap to reside, let's call it /dev/sdX for the purposes of this tutorial. If you are dual booting, it will take up all the free space you freed when you resized your windows installation.
First overwrite the partition with with random bits. Most LUKS tutorials will mention doing this to prevent "metadata leakage": I suppose some information such as the format and usage % of the disk and whatnot can be revealed if the attacker can distinguish used chunks from unused (zeroed) chunks.
dd if=/dev/urandom of=/dev/sdX bs=1M status=progress
Next, create a LUKS partition with luksFormat:
cryptsetup luksFormat /dev/sdX
This command will prompt you for an encryption password. Don't forget it. Try to use a very strong password here, as it is the only real thing protecting your computer in the case of theft, and the attacker has eternity to break it (a long time, and they will have much faster computers in the future!). I would go so far as to say that this encryption password is several times more important than your login password (which really offers no additional protection to any attacker of moderate sophistication, time, and physical access to your computer), so you might as well just use a Fingerprint_Reader for login. The encryption password you set here is the only *real* password your computer uses.
You can also use several passwords here. There is also a way to benchmark different LUKS crypto algorithms with
cryptsetup benchmark and select it with
cryptsetup luksFormat, but the default option is usually fine.
When unlocked with luksOpen, it exposes an unencrypted "virtual" partition at /dev/mapper/<name>. You can format and use this virtual partition like any other partition, like /dev/sda or /dev/nvme0n1p1.
Setting up LVM
LVM is a layer of indirection: it lets you create "virtual" partitions where data is split amongst multiple drives or partitions, and it lets you split partitions into multiple "virtual partitions". In this case we are using it to split up our virtual unencrypted partition from LUKS into two more virtual partitions: one to mount our root filesystem and another to mount the swap partition.
First, open the encrypted partition with a name (in this example, X):
cryptsetup luksOpen /dev/sdX X
Next, allocate /dev/sdX as an LVM physical volume (PV). And create an LVM volume group (VG) on it, named vg1 in this example. This basically tells LVM that you want to use /dev/sdX as a source of storage for the "virtual" partitions, which are called logical volumes (LV).
vgcreate vg1 /dev/mapper/X
Create a logical volume for the swap. I recommend making this slightly bigger than the amount of RAM you have. For example, I have 16GiB of ram, so I'll make a swap partition with 20GiB to account for memory that may be in swap before I hibernate.
lvcreate --size 20G --name swap vg1
Create a logical volume for the root and LVM to allocate the rest of the space on vg1 to it:
lvcreate --extents 100%FREE --name root --vg1
Mark the logical volumes as available so LVM exposes them at /dev/mapper:
vgchange --available y vg1
Format the swap partition and the root partition (I use EXT4 in this example, other options include BTRFS):
mkswap -L "swap" /dev/mapper/vg1-swap
mkfs.ext4 -L "root" /dev/mapper/vg1-root
I use an EFI stub kernel, such that it can be booted off a single signed .efi file, as described in EFI_stub. I also make sure any modules I use are built in (looks like
<*> instead of
<M> in menuconfig). I also build-in all the firmware using menuconfig's Firmware loading facility as described in Linux_firmware. For me, this includes firmware for intel graphics, ucode for intel wifi, and regulatory.db for wifi. If you're unsure what specific firmware file to use, just check dmesg after booting; The driver will usually complain tell you the correct file to use.
First, make your kernel.
cd /usr/src/linux && make && make modules && make modules_install
To get genkernel to generate the initramfs you need, run the following command:
genkernel --luks --lvm --busybox --no-compress-initramfs initramfs
This produces a .cpio file in /boot that you can extract into a directory like /usr/src/initramfs:
mkdir /usr/src/initramfs && cd /usr/src/initramfs && cpio --extract --make-directories --format=newc --no-absolute-filenames < initramfs.img
Now you can embed the initramfs into the same file as the kernel by including this directory in your kernel config:
General setup ---> [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support (/usr/src/initramfs) Initramfs source file(s)
You also have to set the kernel command line to get some stuff to work.
Processor type and features ---> [*] EFI runtime service support [*] EFI stub support ... [*] Built-in kernel command line ()
Set the built-in command line to
root=/dev/ram0 crypt_root=/dev/disk/by-uuid/YOUR_UUID dolvm real_root=/dev/mapper/vg1-root rootfstype=ext4 real_init=/sbin/init real_resume=/dev/mapper/vg1-swap. The crypt_root, real_root, real_init, and real_resume arguments are passed to the initramfs' script. YOUR_UUID is the UUID of your LUKS partition (/dev/sdX in previous examples) and can be found with:
Now, make your kernel again with:
cd /usr/src/linux && make && make modules && make modules_install
The executable "EFI Stub" kernel file is located in /usr/src/linux/arch/x86_64/boot/bzImage for me. If you can't find it, just use this command to recursively list all the files named bzImage in /usr/src/linux/:
find /usr/src/linux | grep bzImage
Now, copy it to EFI/boot/bootx64.efi of your EFI boot partition. Your Let's say your EFI boot partition /dev/sdX1 is mounted at /boot/efi:
cp /usr/src/linux/arch/x86_64/boot/bzImage /boot/efi/EFI/boot/bootx64.efi
With openrc, you can hibernate with the following command:
Test it, it should work now if you've set everything up right.
You can add this command as a keyboard shortcut in your DE such as sway.
Gnome 40 supports mapping the power button to hibernate at Settings->Power->Power button Behavior. It also allows you to add custom keyboard settings in Settings->Keyboard->Keyboard Shortcuts.
Even if your storage is encrypted and the decryption keys are not in memory, an attacker can still take advantage of their physical access to your computer by creating a trap that ensnares you the next time you decrypt your storage. For example, an attacker could save a copy of your encrypted storage and modify your boot partition to include a keylogger that delivers them your decryption password. Because such an attack can be conducted on any computer left unattended, such as laptop in a hotel room, this class of attack is referred to as an Evil Maid Attack.
One way to frustrate evil maid attacks is with "secure boot", wherein some low-level system checks that the OS has a valid cryptographic signature from the owner before booting it.
UEFI supports a version of secure boot where the BIOS does this signature check, see Sasaki's guide on UEFI secure boot which is more extensive than anything I can write on the topic. UEFI secure boot isn't designed for self-signing individuals, it's designed to prevent attackers from modifying the OS (usually MS Windows) after the manufacturer has shipped it. Because of this, it has 4 variables:
- The Signature Database (
db) which contains public keys, signatures, or hashes that confirm an OS is allowed to boot. We will use a single public key here. The signature will be included in our EFI stub.
- The Forbidden Signature Database (
dbx) which contains public keys, signatures, or hashes that confirm an OS is NOT allowed to boot. We don't use this at all.
- The Key Exchange Key (
KEK) which contains one or more keys used to sign the
dbxand is usually held by the OS developer (Microsoft, Red Hat, etc.)
- The Platform Key (
PK) which contains one or more keys used to sign the
KEKand is usually held by the Manufacturer (Framework, Asus, Lenovo, etc.)
If you can't change the
PK then you're simply out of luck. But if you *can* change the
PK, then you can sign a new
db. In the
db you can also include the device's original key alongside your own, allowing you still dual-boot MS Windows or whatever OS came with the computer.
You'll need to emerge app-crypt/efitools and app-crypt/sbsigntools for some of the following commands, but the process goes like this:
1. Generate your new keys
Sasaki recommends making a /etc/efikeys directory for the keys, with read permissions set so that only root can read the keys:
mkdir -p -v /etc/efikeys
chmod -v 700 /etc/efikeys
After that, you can use openssl to generate your keys:
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=my platform key/" -keyout /etc/efikeys/PK.key -out /etc/efikeys/PK.crt -days 3650 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=my key-exchange-key/" -keyout /etc/efikeys/KEK.key -out /etc/efikeys/KEK.crt -days 3650 -nodes -sha256
openssl req -new -x509 -newkey rsa:2048 -subj "/CN=my kernel-signing key/" -keyout /etc/efikeys/db.key -out /etc/efikeys/db.crt -days 3650 -nodes -sha256
Sasaki also recommends setting the write permissions on the individual .key files.
chmod -v 400 /etc/efikeys/*.key
2. (Dual Boot) Get windows certs
efi-readvar command can dump the previously-installed certs, which you'll need to sign if you still want to boot into windows with secure boot on.
efi-readvar -v PK -o /etc/efikeys/old_PK.esl
efi-readvar -v KEK -o /etc/efikeys/old_KEK.esl
efi-readvar -v db -o /etc/efikeys/old_db.esl
efi-readvar -v dbx -o /etc/efikeys/old_dbx.esl
3. Generate your new certs
cert-to-efi-sig-list -g "$(uuidgen)" /etc/efikeys/PK.crt /etc/efikeys/PK.esl
sign-efi-sig-list -k /etc/efikeys/PK.key -c /etc/efikeys/PK.crt PK /etc/efikeys/PK.esl /etc/efikeys/PK.auth
cert-to-efi-sig-list -g "$(uuidgen)" /etc/efikeys/KEK.crt /etc/efikeys/KEK.esl
sign-efi-sig-list -a -k /etc/efikeys/PK.key -c /etc/efikeys/PK.crt KEK /etc/efikeys/KEK.esl /etc/efikeys/KEK.auth
cert-to-efi-sig-list -g "$(uuidgen)" /etc/efikeys/db.crt /etc/efikeys/db.esl
sign-efi-sig-list -a -k /etc/efikeys/KEK.key -c /etc/efikeys/KEK.crt db /etc/efikeys/db.esl /etc/efikeys/db.auth
openssl x509 -outform DER -in /etc/efikeys/PK.crt -out /etc/efikeys/PK.cer
openssl x509 -outform DER -in /etc/efikeys/KEK.crt -out /etc/efikeys/KEK.cer
openssl x509 -outform DER -in /etc/efikeys/db.crt -out /etc/efikeys/db.cer
You can also sign the factory
dbx with your
sign-efi-sig-list -k /etc/efikeys/KEK.key -c /etc/efikeys/KEK.crt dbx /etc/efikeys/old_dbx.esl /etc/efikeys/old_dbx.auth
If you're Dual-booting, then you can create a compound
cat /etc/efikeys/old_KEK.esl /etc/efikeys/KEK.esl > /etc/efikeys/compound_KEK.esl
cat /etc/efikeys/old_db.esl /etc/efikeys/db.esl > /etc/efikeys/compound_db.esl
Otherwise, you can just use your own
cp -v /etc/efikeys/KEK.esl /etc/efikeys/compound_KEK.esl
cp -v /etc/efikeys/db.esl /etc/efikeys/compound_db.esl
sign-efi-sig-list -k /etc/efikeys/PK.key -c /etc/efikeys/PK.crt KEK /etc/efikeys/compound_KEK.esl /etc/efikeys/compound_KEK.auth
sign-efi-sig-list -k /etc/efikeys/KEK.key -c /etc/efikeys/KEK.crt db /etc/efikeys/compound_db.esl /etc/efikeys/compound_db.auth
4. Install your certs to UEFI
This process will vary from computer to computer. Usually you can enter "Setup Mode" by entering the UEFI settings menu (by pressing F2 when booting up) and removing all of the certs. Once you're in "Setup Mode", you can install your certs with the
efi-updatevar -e -f /etc/efikeys/old_dbx.esl dbx
efi-updatevar -e -f /etc/efikeys/compound_db.esl db
efi-updatevar -e -f /etc/efikeys/compound_KEK.esl KEK
efi-updatevar -f /etc/efikeys/PK.auth PK
If that doesn't work, you might need to copy your certs onto your EFI partition and enter them manually from the UEFI settings menu.
5. Sign your kernel (EFI Stub)
You will have to repeat this step every time you recompile the kernel:
sbsign /boot/efi/EFI/bootx64.efi --key /etc/efikeys/db.key --cert /etc/efikeys/db.crt /boot/efi/EFI/bootx64.efi
If you did everything right, it should boot now with secure boot on. You should also check that booting an unsigned image fails.
The central idea behind secure boot is that it protects against "evil maid" attackers trying to overwrite your boot partition. And while UEFI secure boot only takes a couple minutes to set up and considerably increases the complexity of an evil maid attack, it's not a silver bullet.
Firstly, there are many ways that an evil maid attack can be executed *without* simply overwriting the boot partition. For example, an attacker could simply replace your keyboard with a model that contains a keylogger. An attacker could replace your motherboard with a seemingly-identical-but-backdoored motherboard that contains seemingly-identical-but-backdoored BIOS that boots their own seemingly-identical-but-backdoored OS. An attacker could even replace your entire computer with an seemingly-identical-but-backdoored computer. The opportunities for the attacker to escalate an evil maid attack are endless, albeit often unfeasible.
Secondly, secure boot is enforced by the BIOS, which just isn't very authoritative in the first place. For example, on my laptop the secure boot options are held behind a single 8-char password in the UEFI settings menu. If an attacker is able to brute force that BIOS password or otherwise reset the BIOS to factory settings, then secure boot functionality is easily sidestepped: It's not like I check the secure boot variables every time I turn the computer on. And from what I have heard from security researchers, the UEFI standard itself is quite bloated and the implementations themselves are prone to side-channel attacks.
Also, there are other ways to prevent attackers writing to your boot partition. Just using a removable boot drive (as mentioned earlier) and carrying it with you on your keychain makes it pretty difficult to overwrite your boot partition.
Alternatives to UEFI include HEADS, which is more linux-focused and takes advantage of the TPM (trusted computing chip) that comes on most laptops these days. It's built on the open-source and lightweight coreboot. Adjacent to coreboot is the libreboot project which is completely free software; it's limited to older hardware due to Intel ME and AMD PSP.
A security guy on IRC told me that you can mitigate some of the aforementioned Direct-Memory Attacks with IOMMU, so you should probably turn that on, especially if you have a laptop that externally exposes thunderbolt ports.