Reference

Fully Encrypted ArchLinux with Secure Boot on Yoga 920

The first laptops with the latest 8th generation Intel processors began to ship a few weeks ago. One of the strongest contenders is the Lenovo Yoga 920. The top end model features a 4K screen, 16GB of RAM, a 1TB NVMe drive, two Thunderbolt and one USB 3.0 ports, and is yet about the same thickness as the current MacBooks. It ships with Windows 10 out of the box, but that doesn’t mean you’re stuck with it.

Tags: archlinux yoga920

Overview

This article explains how to install a fully encrypted ArchLinux on NVMe with Btrfs and detached LUKS headers and LUKS encrypted UEFI boot partition on a USB dongle. For the benefit of inexperienced users, I explain in detail what I did and why. If reading is not your thing, there are also condensed instructions available.

ArchLinux will be the only installation on my Yoga, and I want it to be fully encrypted in case it gets lost or stolen. There are several ways to encrypt a Linux system. I will describe a more complex, but more secure one using a variation of LVM on LUKS with detached LUKS headers and Encypted boot partition with GRUB.

These are the high level goals for the setup:

  • Fully encrypted system drive with Btrfs file system
  • Encrypted kernel, ramdisk images and bootloader configuration
  • Two-factor authentication via detached LUKS header on USB dongle
  • Volume snapshots via Snapper
  • 8GB of encrypted swap space
  • Secure boot enabled

My laptop’s NVMe drive will be partition-less and only contain the encrypted system installation. Contrary to common setups, the bootloader will not be on the system drive, but reside on a USB stick that serves as a dongle and is required to boot the system. The bootloader is the only component that remains unencrypted, but we’ll digitally sign it and use Secure Boot to prevent tampering and evil maid attacks.

In the past, most users encrypted only the primary file system, because bootloaders did not support encrypted containers. While that protected system and user files, it left the bootloader configuration, kernel and ramdisk image exposed. More recent versions of the GRUB bootloader have encryption support, which allows us to store the /boot directory in an encrypted partition as well.

LUKS is the hard disk encryption standard on Linux. It integrates cryptographic key management with support for multiple keys and passphrase revocation. By adding a second factor for authentication, the system remains secure even if one or more of the passphrases are compromised. One way to accomplish this is to put a key file on a USB stick that must be present on boot. A simpler and even more secure option is to store the entire LUKS header on the stick instead.

The Btrfs file system is particularly well suited for SSD storage devices, because it implements copy on write (COW), compression and SSD TRIM. It also has support for subvolumes, which allow us to create a number of simulated partitions for the root file system, home directory, and file system snapshots via Snapper.

Booting from flash memory is very fast, so I will only be using suspend to RAM and never suspend to disk (aka. hybernation). This means that my swap partition can be kept small. Ideally, we won’t be using the swap space at all, because it will just wear out the SSD, but it is good to be prepared for memory hungry software.

Prerequisites

Before you can start, you need to download ArchLinux. You will need two USB sticks, one for the installer and one that will become the boot dongle. I created my installer from the latest ArchLinux ISO with Rufus on Windows. If you only have a single USB stick, you may consider creating an Archboot installer, which loads the entire OS into RAM and frees up the stick and port once it’s loaded up.

Firmware Update

Before you do anything else, you should update the laptop’s UEFI firmware. Boot into the pre-installed Windows 10 and run the Lenovo firmware update tool. It will reboot your laptop once to flash the NVRAM. Power off your laptop when it is done!

Boot USB Installer

The Yoga 920 has Secure Boot enabled by default, which prevents booting from USB media. We will temporarily disable it in the BIOS, so that we can boot the installer. While most computers have a keyboard shortcut to get into the BIOS, the Yoga 900 series requires you to push a pin into a tiny hole to the left of the power button. This will power up the laptop and present the boot menu:

  1. Select the BIOS Setup menu option and press Enter
  2. Navigate to the Security page using the right arrow key
  3. Go to Secure Boot and press Enter to toggle the option to Disabled
  4. Save the changes and exit BIOS Setup

Yoga Novo Button

Boot Menu

BIOS Setup

There doesn’t appear to be a way to boot from within the BIOS Setup, and after exiting it, the laptop will automatically boot into Windows instead. You have to:

  1. Hold the power button for five seconds to turn off the laptop
  2. Plug in only the USB stick containing the ArchLinux installer
  3. Repeat the pin hole procedure to bring up the boot menu
  4. Select the Boot Menu option to bring up the Boot Manager
  5. Select your USB device to boot from

The ArchLinux installer should now boot up. If you’re lucky to have a 4K screen on your Yoga, the first thing you’ll notice is the tiny font. The largest font available, sun12x22, is still fairly small, but readable enough to get us started:

# setfont sun12x22

You can now plug-in the second USB stick that will become the boot dongle. In the following sections we assume that the installer USB stick is mounted as /dev/sda and the dongle USB stick as /dev/sdb. You can verify this with:

# lsblk
 loop0     7:0    0  408.5M  1 loop  /run/archiso/sfs/airootfs
 sda       8:0    1  14.9G   0 disk
 |_ sda1   8:1    1  14.9G   0 part  /run/archiso/bootmnt
 sdb       8:16   1  57.7G   0 disk
 nvme0n1 259:3    0 953.9G   0 disk

My installer stick's sda is 16 GB, my boot dongle's sdb is 64 GB in size. Your specific sizes may differ.

Wireless Network Setup

The ArchLinux installer will download packages from the internet. Before going any further, let’s make sure that we have network connectivity. If you’re using a wired network adapter you can skip this section, otherwise read on.

The Yoga’s WiFi interface works out of the box. First, make sure that it is detected:

# iw dev

Remember its name as we will need it for the remainder of this section. Mine is called wlp107s0. Next, unblock the interface and bring it up:

# rfkill unblock all
# ip link set wlp107s0 up

I haven’t been able to set up WiFi using built-in wifi-menu user interface, so I ended up configuring it manually. If you are not sure which WiFi network to connect to, you can scan for available hotspots with:

# iw dev wlp107s0 scan | less

Then find your network’s SSID and connect to it. Take a look at ArchWiki for information on wireless network configurations. Mine is secured with a WPA2 passphrase key, so I’m using wpa_supplicant:

# wpa_supplicant -i wlp107s0 -c <(wpa_passphrase "YourSSID" "YourKey")

You can ignore the failure messages for creating an interface and P2P device. The final output lines should indicate a successful connection. Press Ctrl+C to exit wpa_supplicant and run the command again with the -B parameter to fork the process into the background:

# wpa_supplicant -i wlp107s0 -c <(wpa_passphrase "YourSSID" "YourKey") -B

Verify that the connection is up:

# iw dev wlp107s0 link

The output should say that it is connected to your WiFi network. You can now start the DHCP client daemon to receive an IP address from your router and use the internet:

# dhcpcd wlp107s0

If you are unable to complete the entire system installation in a single sitting, repeat these steps every time you reboot the installer USB stick.

Verify Network Connectivity

Verify that the installer is able to connect to the internet:

# ping archlinux.org

Once your laptop is connected, you should also synchronize its clock:

# timedatectl set-ntp true

Prepare USB Dongle

We will set up the second USB stick as a dongle that is required to boot the system. An unencrypted EFI system partition contains the bootloader. The remaining space is used for an encrypted partition that holds the /boot directory, the detached LUKS header of the encypted system drive, as well as general purpose storage space for personal files and other live boot operating systems, such as Kali and Tails.

There are a number of tools, such as fdisk and parted to partition storage devices, but the quickest way is to use the built-in user interface:

# cgdisk /dev/sdb

Create the following partitions on the USB stick:

  • Size: 100M, Hex Code: ef00, Name: ESP
  • Size: 512M, Hex Code: default (8300), Name: Boot
  • Size: default (remaining space), Hex Code: default (8300), Name: Storage

Use the suggested default values for First sector (press Enter). You can ignore the small unusable free space at the beginning of the drive. The partition table in cgdisk should look similar to this:

Part. #     Size        Partition Type            Partition Name
----------------------------------------------------------------
            1007.0 KiB  free space
   1         100.0 MiB  EFI System                ESP
   2         512.0 MiB  Linux filesystem          Boot
   3          57.1 GiB  Linux filesystem          Storage

Don’t forget to Write out the new partition table to apply all changes, then Quit the disk partitioning utility to return to the root shell.

We can now format the EFI System partition:

# mkfs.fat -F32 /dev/sdb1

Then create and open an encrypted container on the boot partition:

# cryptsetup luksFormat /dev/sdb2
# cryptsetup open /dev/sdb2 cryptboot

Lastly, create a file system in the encrypted container and mount it:

# mkfs.ext2 /dev/mapper/cryptboot
# mount /dev/mapper/cryptboot /mnt

If you set up a storage partition, you can now format it as well. I formatted mine with FAT32 for easy use in other operating system:

# mkfs.fat -F32 /dev/sdb3

Prepare System Drive

The Yoga 920 has Windows 10 pre-installed. This is the partition layout:

# fdisk -l
 ...
 /dev/nvme0n1p1  256M   EFI System
 /dev/nvme0n1p2  16M    Microsoft Reserved
 /dev/nvme0n1p3         Microsoft Basic Data
 /dev/nvme0n1p4         Microsoft Basic Data
 /dev/nvme0n1p5  1000M  Windows recovery environment
 ...

Remember the name of the drive as we will need it below. Mine is called nvme0n1. It should contain an EFI system partition, a reserved 16MB Microsoft partition, two Microsoft basic data partitions whose size may vary depending on the size of your SSD, and a Windows recovery environment.

We are going to wipe the entire drive, but before making any changes, you may want to clone it, so it can be restored later if you change your mind. I decided to only keep the Windows recovery environment as it may come in handy in case I want to factory reset the laptop. Lenovo offers recovery DVD and USB media, but it is not free, and saving one gigabyte of storage is not worth the hassle.

For SSDs that have been in use for a while, it is recommended to perform a memory cell clearing prior to erasure in order to restore the factory default write performance. You can skip this step on a brand new Yoga 920. We will simply mark all blocks on the drive as unused:

# blkdiscard /dev/nvme0n1

To decrease the attack surface of encrypted drives, it is often recommended to fill them with random data that cannot be distinguished from encrypted data that is written later. This makes sense for magnetic storage devices where data is overwritten in place for free. For SSD drives the situation is much less clear, because they are based on different physical principles.

NAND flash memory cells can only be written to when they are empty (i.e. zero). Overwrites to used blocks are considerably more expensive, because they have to be cleared first. Writing random data to the entire drive will mark every block as used. On older SSDs this has been shown to reduce write performance. On newer drives this is somewhat mitigated by a certain amount of hidden storage used for recycling blocks in the background.

The operating system can issue TRIM commands to mark memory areas that are no longer used. These can then be cleared in the background by the SSD’s garbage collector, so that they become available for fast write operations in the future. While TRIM greatly improves the performance and wear of SSDs, it reveals which blocks are in use. This decreases the amount of encrypted data that an attacker needs to consider and possibly leaks information.

SSD drives are a can of worms when it comes to encryption. Since much of their internal workings are undisclosed and the security implications are not clear, the best recommendation is to not store any highly sensitive data on SSD storage in the first place. The rest of this article will therefore focus on making the Yoga 920 more tamper resistant while also retaining its performance and life span.

Encrypt System Drive

We are going to use cryptsetup to create an encrypted container on the system drive. Note that there are many possible combinations of cypher, hash and key length to choose from, but the default settings are sufficient to protect from most adversaries in the foreseeable future. To learn more about these options, check out the Gentoo Wiki and the official cryptsetup FAQ.

The LUKS header contains all password derivation and key slot information of an encrypted container. Since the data on SSD drives cannot be erased reliably due to wear-leveling and defect management algorithms, older versions of the header containing revoked keys can remain on the drive for a potentially very long time. It is therefore advisable not to store the LUKS header on the SSD itself, but to detach it to an external device. We will store it in the encrypted USB /boot partition.

Storing the header on a USB stick has the extra benefit that it adds a second factor of authentication: without access to the USB stick, the system cannot be booted. It also makes a brute force attack even more difficult. However, keep in mind that a detached LUKS headers does not support re-encryption with a different key in case both the header and one of the passphrases get compromised.

Create the encrypted container with a detached LUKS header and open it:

# truncate -s 2M /mnt/luksheader
# cryptsetup luksFormat /dev/nvme0n1 --align-payload 4096 --header /mnt/luksheader
# cryptsetup open --type luks --header /mnt/luksheader /dev/nvme0n1 cryptroot

Verify that the containers were opened and mapped (your sizes may vary):

# fdisk -l
 ...
 Disk /dev/mapper/cryptboot: 57.6 GiB
 ...
 Disk /dev/mapper/cryptroot: 953.9 GiB

You can now unmount the boot partition as we no longer need it:

# umount /mnt

Unfortunately, Btrfs does not support swap files yet, so the swap space needs to be a separate partition for now. We’ll use LVM on LUKS to configure two logical volumes inside the container, one for the root file system, and one for swap.

First create the physical volume and an associated volume group on top of the LUKS container:

# pvcreate /dev/mapper/cryptroot
# vgcreate System /dev/mapper/cryptroot

Then create the logical volumes on the volume group:

# lvcreate -L 8G System -n swap
# lvcreate -l 100%FREE System -n root

And then format and mount the logical volumes:

# mkswap /dev/mapper/System-swap
# swapon -d /dev/mapper/System-swap
# mkfs.btrfs /dev/mapper/System-root
# mount /dev/mapper/System-root /mnt

Root File System Setup

The root volume is formatted using the Btrfs file system, which will host the subvolumes for the system. Subvolumes allow us to simulate partitions within the encrypted container. We will use the suggested layout for use with Snapper:

# btrfs subvolume create /mnt/@
# btrfs subvolume create /mnt/@home
# btrfs subvolume create /mnt/@snapshots

Now unmount the system partition and mount the created subvolumes instead:

# umount /mnt
# mount -o compress=lzo,discard,noatime,nodiratime,subvol=@ /dev/mapper/System-root /mnt
# mkdir /mnt/home
# mkdir /mnt/.snapshots
# mount -o compress=lzo,discard,noatime,nodiratime,subvol=@home /dev/mapper/System-root /mnt/home
# mount -o compress=lzo,discard,noatime,nodiratime,subvol=@snapshots /dev/mapper/System-root /mnt/.snapshots

The @ subvolume will serve as root, @home will contain the home directory, and @snapshots will store volume snapshots created by Snapper. The compress and discard options enable data compression and TRIM support respectively. The noatime and nodiratime disable the writing of last access times on files and directories to improve the life time of the SSD drive.

There are certain directories that we do not wish to include in snapshots, such as log files and the package repository. For these we will create nested subvolumes, which are automatically excluded from snapshots by default, and mount them:

# mkdir -p /mnt/var/cache/pacman
# btrfs subvolume create /mnt/var/cache/pacman/pkg
# btrfs subvolume create /mnt/var/log
# btrfs subvolume create /mnt/var/tmp

Lastly, mount the /boot partition and the ESP into the system root:

# mkdir /mnt/boot
# mount /dev/mapper/cryptboot /mnt/boot
# mkdir /mnt/boot/efi
# mount /dev/sdb1 /mnt/boot/efi

Everything is ready now for the system installation.

ArchLinux Installation

In order to speed up the installation process, you should edit the list of package mirror servers. Move entries that are geographically closest to your location to the top of the file and comment out servers that you are not using. The list will be copied automatically to the installed system:

# nano /etc/pacman.d/mirrorlist

The bootstrapper will update the package repository and proceed with installing the initial packages:

# pacstrap /mnt base btrfs-progs efibootmgr grub-efi-x86_64 intel-ucode

Depending on your internet connection speed this should be finished in about five minutes. After that, generate an fstab file, so that the system knows how to mount the various volumes at boot time:

# genfstab -Up /mnt >> /mnt/etc/fstab

We can edit the generated file and make the /tmp directory a ramdisk. This will help preserve the SSD and speed up operations that use temporary files:

# nano /mnt/etc/fstab
 tmpfs     /tmp         tmpfs  defaults,noatime,mode=1777  0 0

Let’s also disable auto mount for the boot partition and ESP, so we can remove the USB dongle right after booting. This can be accomplished by adding noauto to the /boot and /boot/efi entries in fstab.

Your fstab file should have the following entries (comments/UUIDs omitted):

# cat /mnt/etc/fstab
 UUID=...  /            btrfs  rw,noatime,nodiratime,compress=lzo,ssd,discard,space_cache,subvolid=257,subvol=/@,subvol=@  0 0
 UUID=...  /home        btrfs  rw,noatime,nodiratime,compress=lzo,ssd,discard,space_cache,subvolid=258,subvol=/@home,subvol=@home  0 0
 UUID=...  /.snapshots  btrfs  rw,noatime,nodiratime,compress=lzo,ssd,discard,space_cache,subvolid=259,subvo=/@snapshots,subvol=@snapshots 0 0
 UUID=...  /none        swap   defaults  0 0
 UUID=...  /boot        ext2   noauto,rw,relatime,block_validity,barrier,user_xattr,acl 0 2
 UUID=...  /boot/efi    vfat   noauto,rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro  0 2
 tmpfs     /tmp         tmpfs  defaults,noatime,mode=1777  0 0

Add the boot partition to the crypttab file as well. The entry is needed for setting up Secure Boot later. Get the boot partition’s identifier via:

# blkid /dev/sda2

Then replace <identifier> with the boot partition's UUID in the file:

# nano /mnt/etc/crypttab
 cryptboot    UUID=<identifier>    none    noauto,luks

Lastly, change into the root directory of the installation to configure it:

# arch-chroot /mnt

The base system is now installed on the drive, but not able to boot yet.

Initial Ramdisk Configuration

The purpose of initramfs is to bootstrap the system to the point where the root filesystem is accessible. It is able to unlock encrypted root partitions via the encrypt hook, but it currently does not support detached LUKS headers. We have to modify the hook slightly for that to work.

First, create copies of the existing build and run-time hook files, so that they are not overwritten on future mkinitcpio updates:

# cp /lib/initcpio/hooks/encrypt{,2}
# cp /usr/lib/initcpio/install/encrypt{,2}

Then make the following changes to encrypt2 at around line 53:

# nano /lib/initcpio/hooks/encrypt2
 ...
 warn_deprecated() {
     echo "The syntax 'root=${root}' where '${root}' is an encrypted volume is deprecated"
     echo "Use 'cryptdevice=${root}:root root=/dev/mapper/root' instead."
 }

 local headerFlag=false
 for cryptopt in ${cryptoptions//,/ }; do
     case ${cryptopt} in
         allow-discards)
             cryptargs="${cryptargs} --allow-discards"
             ;;
         header)
             cryptargs="${cryptargs} --header /boot/luksheader"
             headerFlag=true
             ;;
         *)  
             echo "Encryption option '${cryptopt}' not known, ignoring." >&2 
             ;;  
     esac
 done

 if resolved=$(resolve_device "${cryptdev}" ${rootdelay}); then
     if $headerFlag || cryptsetup isLuks ${resolved} >/dev/null 2>&1; then
         [ ${DEPRECATED_CRYPT} -eq 1 ] && warn_deprecated
         dopassphrase=1

Now add the new encrypt2 hook and the path to the LUKS header to the mkinitcpio configuration file. We are also adding the keyboard, keymap and lvm2 hooks. Their ordering relative to block and filesystems is important:

# nano /etc/mkinitcpio.conf
 ...
 MODULES=(i915 loop)
 ...
 BINARIES=(/usr/bin/btrfs)
 ...
 FILES=(/boot/luksheader)
 ...
 HOOKS=(base ... keyboard keymap ... block ... encrypt2 lvm2 ... filesystems ...)

Now regenerate the initial ramdisk image:

# mkinitcpio -p linux

Bootloader Installation

We will now install the GRUB bootloader to the USB dongle. Grub needs to pass special parameters to the kernel that allow the latter to unlock the encrypted root partition. Since we set up the LUKS container on the entire disk, we cannot use a partition UUID, but will pass a persistent name based on the device serial number:

# ls -l /dev/disk/by-id | grep nvme0n1

Take note of the disk name. Mine begins with nvme-SAMSUNG_MZVLW1T0HLMH, because that is the serial number of my disk.

The following changes in /etc/default/grub will allow it to recognize the encrypted boot partition and pass the correct parameters to the kernel:

# nano /etc/default/grub
 ...
 GRUB_CMDLINE_LINUX="cryptdevice=/dev/disk/by-id/YourDiskId:cryptroot:allow-discards,header"
 GRUB_PRELOAD_MODULES="part_gpt part_msdos lvm"
 GRUB_ENABLE_CRYPTODISK=y
 GRUB_GFXMODE=1024x768x32

Generate the GRUB configuration file and install GRUB to the mounted ESP:

# grub-mkconfig -o /boot/grub/grub.cfg
# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id="grub"

GRUB will now ask for the passphrase to unlock the boot partition from where it will read its configuration files, load the kernel and unpack initramfs.

System Configuration

Set your time zone and configure the hardware clock:

# ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime
# hwclock --systohc --utc

Uncomment all desired localizations, i.e. en_US.UTF-8 UTF-8 in locale.gen, generate them, and set the LANG environment variable:

# nano /etc/locale.gen
# locale-gen
# echo LANGUAGE=en_US >> /etc/locale.conf
# echo LANG=en_US.UTF-8 >> /etc/locale.conf

Set the host name for your machine. Add a matching entry to the hosts file:

# echo myyoga920 > /etc/hostname
# nano /etc/hosts
 ...
 127.0.0.1 myyoga920.localdomain myyoga920
 # End of file

Update all installed packages, install the packages required for WiFi, as well as auto completion for bash, so it is easier to find packages:

# pacman -Suy iw wpa_supplicant bash-completion

Create a new user account that you will use to log in from now on:

# useradd -m -g users -G wheel,storage,power -s /bin/bash your_new_user_name
# passwd your_new_user_name

Install sudo, which will allow you to perform administrative tasks using the previously created new user account:

# pacman -S sudo
# EDITOR=nano visudo
  uncomment the following line
  %wheel ALL=(ALL) ALL

To further reduce the attack surface of your device, it is recommended to lock the root account. Note that you can always re-enable it later by setting a password for it:

# passwd -l root

Exit the chroot environment, unmount all partitions, disable swap and reboot the machine. After the screen goes black, remove the installer USB stick:

# exit
# umount -R /mnt
# swapoff -a
# reboot

If you did everything correctly, the laptop will restart, EFI will detect the USB dongle and start Grub, which will then ask you for the password to unlock the boot partition and show the bootloader menu. When selecting the ArchLinux operating system, the kernel and initial ramdisk are loaded, execution is transferred to the kernel, and a second password prompt is shown to unlock the root file system. After that, you should be presented with a login prompt into your brand new system.

Secure Boot

Encrypting the boot partition does not necessarily increase security and simply shifts the problem to the bootloader, which must remain unencrypted on today’s hardware. However, with UEFI Secure Boot we can cryptographically sign the bootloader and whitelist it, which adds an additional layer of protection, because the system can:

  • Detect tampering with the bootloader
  • Refuse to boot any but the correct boot partition

Why is this important? Because an encrypted root disk does not prevent attackers with access to your laptop (i.e. in a hotel room) from booting their own operating system from USB and manipulating the firmware in various hardware components. Should attackers gain access to your USB boot dongle, they can also install a hacked bootloader on it (see Evil Maid attacks).

Setting up Secure Boot is not exactly straightforward, but there are tools to simplify the process. I used cryptboot to take care of everything. First you need to put Secure Boot into setup mode. Power down the laptop, activate the BIOS menu, and then:

  1. Select the BIOS Setup menu option and press Enter
  2. Navigate to the Security page using the right arrow key
  3. Go to Set Administrator Password and enter a strong password
  4. Go to Reset to Setup Mode, press Enter and select Yes
  5. Save the changes and exit BIOS Setup

At this point, the preloaded Secure Boot keys for Microsoft/OEM have been deleted. You can always restore the factory keys in BIOS Setup, if needed. The next step is to install the cryptboot package, which is not part of the core repository yet, so we have to build it manually from GitHub.

Install the development base package and the Git client:

# sudo pacman -S base-devel git

Clone the GitHub repository, build and install the package:

# git clone https://github.com/xmikos/cryptboot
# cd cryptboot
# makepkg -si --skipchecksums

cryptboot currently does not provide source checksums and PGP signatures, so we have to skip the checksum verification altogether. You should take a look at the cloned script files and make sure that they match the ones shown on GitHub and do not contain any malicious code.

Mount the boot partition and the ESP:

# sudo cryptboot mount

Generate and enroll new UEFI Secure Boot keys (common name is the name under which the key will be stored, i.e. "ArchLinux"):

# sudo cryptboot-efikeys create
# sudo cryptboot-efikeys enroll

Update and sign the Grub bootloader, unmount the boot partitions, and shut down the system:

# sudo cryptboot update-grub
# sudo cryptboot umount
# sudo shutdown -P now

The final step is to re-enable Secure Boot. Activate the BIOS menu and:

  1. Select the BIOS Setup menu option and press Enter
  2. Navigate to the Security page using the right arrow key
  3. Go to Secure Boot and press Enter to toggle the option to Enabled
  4. Save the changes and exit BIOS Setup

The USB dongle is now the only media able to boot on the laptop, and it is also protected against tampering.

Post-install Steps

The laptop is now ready for every day use. If this is your first time installing ArchLinux, read the general recommendations and perform any steps that you deem necessary.

My laptop is going to be used as a development machine. Over time I’ll be installing and uninstalling a lot of packages and drivers. I set up Snapper for regular snapshots of the file system, so that I can easily revert it to an earlier state in case it breaks:

# sudo pacman -S snapper
# sudo umount /.snapshots
# sudo rm -r /.snapshots
# sudo snapper -c root create-config /
# sudo mount -o compression=lzo,discard,noatime,nodiratime,subvol=@snapshots /dev/mapper/System-root /.snapshots
# sudo systemctl start snapper-timeline.timer

This will initialize the snapshots volume and use the default settings to create and keep ten hourly, ten daily, ten monthly and ten annual snapshots of the disk. Check out the Snapper documentation for these and other configuration options.

Setting up a desktop environment is most likely your next big task. There are quite a few to chose from. Due to the popularity of Ubuntu, the Gnome desktop manager has excellent out of the box support for modern hardware, and it is probably your best choice if you’d like to get up and running quickly.

The installation of Gnome amounts to:

# sudo pacman -S gnome

A couple tweaks are needed to make gnome-terminal work properly:

# sudo localectl set-locale LANG=en_US.UTF-8
# sudo echo LC_ALL= >> /etc/locale.conf

During the installation we used wpa_supplicant to establish connections to wireless networks, but this is rather cumbersome. The NetworkManager service can perform these things automatically and will also be exposed in the user interface:

# sudo pacman -S networkmanager
# sudo systemctl enable --now NetworkManager.service

Edit its configuration file to let it manage network interfaces on demand:

# sudo nano /etc/NetworkManager/NetworkManager.conf
 [ifupdown]
 managed=true

To increase battery life, install and enable TLP power management:

# sudo pacman -S tlp x86_energy_perf_policy
# sudo systemctl enable tlp.service
# sudo systemctl enable tlp-sleep.service

There is also an optional package for optimizing the power consumption of the WiFi and Bluetooth devices that requires the NetworkManager dispatcher service to be enabled, and the rfkill services to be masked:

# sudo pacman -S tlp-rdw
# sudo systemctl enable NetworkManager-dispatcher.service
# sudo systemctl mask systemd-rfkill.service
# sudo systemctl mask systemd-rfkill.socket

No further configuration is required, but you can visit the TLP project page for a description of all features and options.

Finally, enable and start the Gnome desktop manager service:

# sudo systemctl enable --now gdm.service

The login screen should appear and take you to your shiny new desktop.

Yoga 920 Gnome Desktop

Yoga Specifics

The ideapad_laptop kernel module has caused problems with Lenovo laptops and tablets in the past and still does with the Yoga 920. In particular, it prevents the WiFi from being turned on. It is best to blacklist it:

# sudo nano /etc/modprobe.d/blacklist.conf
# install ideapad_laptop /bin/false

Once connected to a wireless network, it appears to have poor performance, showing only 6 Mb/s on 5GHz and 1 Mb/s on 2.4GHz networks. The actual throughput is much higher though. This might be a bug in the Atheros QCA6174 firmware or the kernel. Note that the current version of ArchLinux (currently 4.14.3-1) already contains the latest firmware from kvalo, so an update won’t fix it yet.

The Bluetooth service is installed with Gnome, but you need to enable and start it:

# sudo systemctl enable --now bluetooth.service

The specific fingerprint reader built into the Yoga 920 is currently not supported. However, there is some ongoing work in the community to reverse engineer its protocol.

Whenever you power the laptop on or off with the boot dongle unplugged, the Yoga’s EFI boot manager removes the dongle from its list of bootable partitions. When plugging the stick in again, the firmware is unable to detect the bootloader and shows a message that no bootable device was found. This particularly annoying issue might be specific to Grub or cryptboot as I have not seen it happen with other bootloaders.

In the meantime, it is best to leave the dongle plugged in during power transitions. To make a forgotten dongle bootable again, launch into the shell of an ArchLinux live USB and manually add its boot partition to the firmware. Assuming that the dongle is mounted as /dev/sdb:

# efibootmgr -c -d /dev/sdb1 -p 1 -l "\EFI\grub\grubx64.efi" -L "ArchLinux"

Conclusions

In this article we have gone to great lengths to encrypt the root filesystem and boot partition, to detach cryptographically relevant data to a USB dongle, and to protect the dongle via Secure Boot. However, no level of encryption can fully protect your computer system. With the presence of features like Intel Management Engine and AMD Platform Security Processor on most modern processors, as well as many other possible backdoors, it is likely compromised straight out of the factory.

The mechanisms discussed here also don’t protect from vulnerabilities in the OS and the software that will be running on your system. Our goal is to not leave the door wide open for opportunistic attacks by ordinary people. If you are concerned about sophisticated criminal, corporate or governmental organizations, you are best advised to not store highly sensitive data on your laptop in the first place.

When running ArchLinux on the Yoga 920, most features work out of the box. The only exceptions are the fingerprint reader, which is currently not supported, and the wireless adapter, which has performance issues that will hopefully be resolved soon.

Appendix A: Resume Installation

If you need to proceed with the installation at a later time, you can resume it by first booting the ArchLinux live USB and then inserting the USB dongle. If you already created the encrypted boot partition, unlock and mount it:

# cryptsetup open /dev/sdb2 cryptboot
# mount /dev/mapper/cryptboot /mnt

If you already encrypted the system drive, unlock it:

# cryptsetup open --type luks --header /mnt/luksheader /dev/nvme0n1 cryptroot

If you already created the root file system, mount the root BTRFS subvolume:

# swapon -d /dev/mapper/System-swap
# mount -o compress=lzo,discard,noatime,nodiratime,subvol=@ /dev/mapper/System-root /mnt

If you have not completed the ArchLinux installation, mount the other subvolumes:

# mount -o compress=lzo,discard,noatime,nodiratime,subvol=@home /dev/mapper/System-root /mnt/home
# mount -o compress=lzo,discard,noatime,nodiratime,subvol=@snapshots /dev/mapper/System-root /mnt/.snapshots

If you have not completed the bootloader installation, remount the USB dongle into the installed root:

# mount /dev/mapper/cryptboot /mnt/boot
# mount /dev/sdb1 /mnt/boot/efi

If you completed the ArchLinux installation, change into the installed root via:

# arch-chroot /mnt

Appendix B: Keep TRIM Disabled

Should you decide to keep TRIM disabled for improved data security, you can fill an entire drive or partition with random data by performing the following steps. First create a temporary container and make sure it exists:

# cryptsetup open --type plain /dev/nvme0n1 container --key-file /dev/random
# fdisk -l
 ...
 Disk /dev/mapper/container: 953.9GiB
 ...

Then write zeros to the container until it is full and close it:

# dd if=/dev/zero of=/dev/mapper/container bs=1M status=progress
# cryptsetup close container

This will take about 18 minutes for the 1TB drive.

TRIM is disabled for LUKS volumes by default. Make sure that you do not pass the allow-discards parameter to the kernel in /etc/default/grub (run grub-mkconfig again if needed), and do not specify the discard flag for the Btrfs subvolumes in /etc/fstab.

Appendix C: Auto-mount Boot Partition

Before performing a full system upgrade, you must mount the boot partition, so that the package manager can access the kernel and initial RAM filesystem, if needed. You can always mount it manually via:

# cryptsetup open /dev/sda2 cryptboot
# mount /dev/mapper/cryptboot /boot
# mount /dev/sda1 /boot/efi

You can also mount the boot partition automatically on system startup and during reboot by adding the encrypted container to crypttab, which is read after bootup, but before fstab. To prevent another password prompt, you can set up a keyfile in the root file system that auto-unlocks the boot partition for you.

First, create the keyfile and set the correct file permissions:

# dd bs=512 count=4 if=/dev/urandom of=/etc/cryptboot.key
# chmod 000 /etc/cryptboot.key
# chmod 600 /boot/initramfs-linux*

Then add the keyfile to the LUKS header of the boot partition:

# cryptsetup luksAddKey /dev/sda2 /cryptboot.key

The LUKS header now contains two key slots, one for the initial passphrase, and one for the key file we added. You can verify that this is the case:

# cryptsetup luksDump /dev/sda2

Modify crypttab to unlock the boot partition to /dev/mapper/cryptboot using the key file that we just created:

# nano /etc/crypttab
 cryptboot    UUID=<identifier>    /etc/cryptboot.key    luks

Finally, make sure that the boot partition and the ESP do not use the noauto flag in /etc/fstab. The corresponding entries should look as follows:

# cat /mnt/etc/fstab
 ...
 UUID=...  /boot        ext2   rw,relatime,block_validity,barrier,user_xattr,acl 0 2
 UUID=...  /boot/efi    vfat   rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro  0 2
 ...

After rebooting, the partitions on the USB dongle are mounted automatically.

Appendix D: Auto-unlock Root Partition

First, create the keyfile and set the correct file permissions:

# dd bs=512 count=4 if=/dev/urandom of=/crypto_keyfile.bin
# chmod 000 /crypto_keyfile.bin
# chmod 600 /boot/initramfs-linux*

Then add the keyfile to the detached LUKS header of the root partition:

# cryptsetup luksAddKey /boot/luksheader /crypto_keyfile.bin

The LUKS header now contains two key slots, one for the initial passphrase, and one for the key file we added. You can verify that this is the case:

# cryptsetup luksDump /boot/luksheader

Add the keyfile to the initial ramdisk image:

# nano /etc/mkinitcpio.conf
 ...
 FILES=(/boot/luksheader /crypto_keyfile.bin)

Regenerate the initial ramdisk image:

# mkinitcpio -p linux

The kernel automatically looks for a /crypto_keyfile.bin on boot, so it does not need to be added to the kernel command line.

After rebooting the system, you should now be taken directly to the root login prompt after entering the password for unlocking the boot partition.

Appendix E: Future Firmware Updates

At the time of this writing it was not possible to update the UEFI firmware outside of Windows, and with Lenovo’s track record on Linux support you certainly shouldn’t hold your breath for this to change. There are few ways we can work around it though, but they all require to boot into Windows 10.

The easiest option is to leave the pre-installed Windows and resize its partition to make room for an ArchLinux install in an encrypted partition. You can set it up for dual-boot and fire up Windows when a new firmware has been released. Obviously, this will prevent you from using the entire SSD for ArchLinux.

Another option is to clone the pre-installed Windows to a Windows To Go live USB stick, from which you can boot Windows instead. This can be accomplished with the free version of WinToUSB. Make sure to use a USB stick with fast read and write speeds, or you’ll spend a lot of time waiting. I personally used a SanDisk Ultra Dual Drive USB Type-C 32GB with a read speed of 150MB/s.

If your Yoga is already wiped clean, but you have access to another Windows PC, you can download the Windows 10 ISO for free and turn it into a Windows To Go USB stick using a variety of free utilities. If you run Enterprise or Education Edition, you can also use the built-in utility.

If you don’t have access to a Windows PC, you can install the Windows 10 ISO into VirtualBox and clone that installation onto a Windows To Go USB from there. This is more involved and time consuming, but I’ve tried it, and it works. Note that you cannot flash the firmware directly from within the guest operating system as it does not have access to the actual hardware.

Related Resources

Although the individual parts of this setup are well documented on the ArchLinux Wiki, it took me quite a while to get the boot configuration to work properly, and I couldn’t have done it without the help of TJ from the #grub FreeNode IRC channel. Make sure to check out his excellent Illustrated Guide to Grub that describes the inner workings of the Linux boot process on BIOS and UEFI in detail.

A Yoga 920 specific installation overview for Ubuntu is on Lafaspot.

A review of Fedora can be found on Kevin’s blog.

Minimal instructions for installing ArchLinux using EXT4/LVM/LUKS/NVMe on an UEFI system and the systemd-boot bootloader are in binaerbaum’s gist. The hint that Grub will not work with NVMe drives is no longer valid though. Also, systemd-boot doesn’t support encrypted boot partitions at this time.