Encrypted Arch Linux Setup

January 1, 2021

This blog post describes how to set up an Arch Linux installation with Full Disk Encryption on a PCEngines APU2 board.

The same principles also apply when installing Arch for other systems.

The APU2 is a 64bit system with an AMD 1 GHz quad core and hardware accelerated AES support, it has multiple mSATA SSD slots for storage and a DB9 serial port for maintenance.

Once the system is set up, you will be prompted for the decryption password before booting the OS, and you will need to enter that password by connecting via serial.

[0x0] Index

TODO: mention dropbear SSHD

[0x1] Disk Partitioning

First we need to format the mSATA SSD and prepare the disk layout.

Preparation

Install a Virtual Machine with arch linux, we will use this for bootstrapping the Arch installation on the mSATA disk.

Place the mSATA SSD in the USB connector, insert the USB drive and connect it to the VM when prompted by your virtualization software.

Tip: If you use a USB hub, make sure to unplug other devices that draw excessive power, e.g: USB fans. I got weird errors in the past when trying to mount the SSD volume, because of other devices attached to the same USB hub.

Overview

+-----------------------+------------------------+-----------------------+
| Boot partition        | Boot Partition         | LUKS2 encrypted system |
|                       |                        | partition              |
|                       |                        |                        |
| GRUB                  | /boot                  | /                      |
|                       |                        |                        |
|                       |                        | /dev/mapper/cryptroot  |
| 1M                    | 300M                   | ---------------------- |
| /dev/sdX1             | /dev/sdX2              | /dev/sdX3              |
+-----------------------+------------------------+-----------------------+

Determine target disk:

# fdisk -l

Begin partitioning target disk:

# fdisk /dev/sdX

Create a new GPT header:

g

Create a new boot partition for GRUB:

n
default(1) [ENTER]
default [ENTER]
# this creates a partition of desired size 1000.5 KiB ~= 1MB
+2000

## TODO: using +1M resulted in an error during grub-install, that the partition is too small?
# led to size 512 bytes instead of one megabyte?
#+1M

Change partition type to Boot / BIOS:

t [ENTER]
4 [ENTER]

A BIOS boot partition is only required when using GRUB for BIOS booting from a GPT disk. The partition has nothing to do with /boot, and it must not be formatted with a file system or mounted.

Create the main boot partition:

n [ENTER]
default(2) [ENTER]
default [ENTER]
+300M [ENTER]

Create the root partition:

n [ENTER]
default(3) [ENTER]
default [ENTER]
default (till the end, or leave space for SWAP) [ENTER]

Show all partitions:

p [ENTER]

Write changes:

w [ENTER]

SWAP will be implemented in a file later. Keep in mind that swap space also needs to be encrypted, as it can contain sensitive information from the RAM.

Tip: One may want to enable Discard/TRIM support for solid state drives (SSD).

Prepare the root partition

Setup the LUKS volume:

cryptsetup -y -v luksFormat /dev/sdX3

Open it:

cryptsetup open /dev/sdX3 cryptroot

Format it as EXT4:

mkfs.ext4 /dev/mapper/cryptroot

Mount the new filesystem:

mount /dev/mapper/cryptroot /mnt

Check if everything worked, by:

Unmounting the volume:

umount /mnt

Closing the crypt volume:

cryptsetup close cryptroot

Re-opening it (you will be prompted for the password):

cryptsetup open /dev/sdX3 cryptroot

Mount the decrypted volume again:

mount /dev/mapper/cryptroot /mnt

If everything works at this point, unmount and close the crypt volume again and proceed to the boot partition preparation section.

NOTE: Write down the UUID (not PARTUUID!) of the root partition (/dev/sdX3) for later!

Find the UUID with:

ls -la /dev/disk/by-uuid

or use

blkid /dev/sdX3

Preparing the boot partition

What you do have to setup is a non-encrypted /boot partition, which is needed for a encrypted root. For an ordinary boot partition on BIOS systems, for example, execute:

mkfs.ext4 /dev/sdX2

or for an EFI system partition on UEFI systems:

mkfs.fat -F32 /dev/sdX2

Afterwards create the directory for the mounpoint and mount the partition:

mkdir /mnt/boot
mount /dev/sdX2 /mnt/boot

Now we will bootstrap the arch linux installation following the guide from the Arch linux wiki.

Install the base packages

Use the pacstrap script to install the base package group:

pacstrap /mnt base

Tip: If something goes wrong here, remove all folders from /mnt except for lost+found and boot and delete the pacman lockfile at /mnt/var/lib/pacman/db.lck then repeat the pacstrap command.

[0x2] Configure the system

Next, the system needs to be configured.

Fstab

Generate a fstab file (use -U or -L to define by UUID or labels, respectively):

genfstab -U /mnt >> /mnt/etc/fstab

Check the resulting file in /mnt/etc/fstab afterwards, and edit it in case of errors.

Chroot

Change root into the new system:

arch-chroot /mnt

Packages

Install some common tools (adjust this list for your use case, you can also do this later of course):

pacman -Sy vim docker rsync tree zip unzip screen openssh ntp

Time zone

Set the time zone (replace Region/City with the timezone you want to use):

ln -sf /usr/share/zoneinfo/Region/City /etc/localtime

Check system time:

date

If the system time is not correct, set time via ntp:

ntpd -qg

Run hwclock(8) to generate /etc/adjtime:

hwclock --systohc --utc

Localization

Uncomment en_US.UTF-8 UTF-8 and other needed locales in /etc/locale.gen, and regenerate the locales with:

locale-gen

Create the locale.conf(5) file, and set the LANG variable accordingly:

Create file /etc/locale.conf and enter:

LANG=en_US.UTF-8

If you set the keyboard layout, make the changes persistent in vconsole.conf(5):

Create file /etc/vconsole.conf:

KEYMAP=de-latin1

Network configuration

Create the hostname file /etc/hostname:

myhostname

Add matching entries to hosts(5) in file /etc/hosts:

127.0.0.1	localhost
::1		localhost
127.0.1.1	myhostname.localdomain	myhostname

If the system has a static IP address, it should be used instead of 127.0.1.1.

Mounting at boot time

If you want to mount an encrypted drive at boot time, enter the device’s UUID in /etc/crypttab. You get the UUID (partition) by using the command lsblk -f and adding it to crypttab in the following form:

Create file /etc/crypttab:

externaldrive         UUID=<UUID-sdX3>        none    luks,timeout=180

Initramfs

Creating a new initramfs is usually not required, because mkinitcpio was run on installation of the linux package with pacstrap.

Configuring mkinitcpio

Add the keyboard, keymap and encrypt hooks to mkinitcpio.conf. If the default US keymap is fine for you, you can omit the keymap hook.

In file /etc/mkinitcpio.conf:

HOOKS=(base udev block autodetect keyboard keymap consolefont modconf encrypt filesystems fsck)

Depending on which other hooks are used, the order may be relevant. See dm-crypt/System configuration for details and other hooks that you may need.

For LVM, system encryption or RAID, modify mkinitcpio.conf(5) and recreate the initramfs image:

pacman -Sy linux linux-firmware mkinitcpio
mkinitcpio -p linux

Install GRUB

Now we need to install a bootloader, I’ve chosen GRUB here.

Install grub via pacman:

pacman -Sy grub efibootmgr

Install GRUB to the disk for BIOS booting:

grub-install --target=i386-pc --recheck /dev/sdX

Configure GRUB

Wiki Links:

To make the change persistent after reboot, you could manually edit /boot/grub/grub.cfg with the exact line from above, but the best practice is to:

In order to unlock the encrypted root partition at boot, the following kernel parameters need to be set by the boot loader.

Tip: When using GRUB and generating grub.cfg with grub-mkconfig, the ‘root’ parameter does not need to be specified manually. grub-mkconfig will determine the correct UUID of the decrypted root filesystem and add it to grub.cfg automatically.

Edit /etc/default/grub and append your kernel options to the GRUB_CMDLINE_LINUX_DEFAULT line:

GRUB_CMDLINE_LINUX_DEFAULT="rootdelay=20 audit=0 loglevel=3 quiet console=tty0 console=ttyS0,115200n8 cryptdevice=UUID=<UUID-sdX3>:cryptroot"

In order to make Grub work over serial console add the following:

GRUB_TERMINAL="serial"
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"

And then generate the grub.cfg file with:

grub-mkconfig -o /boot/grub/grub.cfg

See dm-crypt/System configuration#Boot loader for details.

The device-UUID refers to the UUID of /dev/sdX3 (root partition). See Persistent block device naming for details.

Root password

Set the root password:

passwd

SWAP to file

Install systemd-swap package:

pacman -S systemd-swap

Edit the config. I recommend setting zram_enabled to 1 (compressed RAM) and swapfc_enabled to 1 (auto-managed swap files).

# Enable `zram_enabled=1` and `swapfc_enabled=1`
vim /etc/systemd/swap.conf

Start it now and enable it on every reboot

systemctl start systemd-swap
systemctl enable systemd-swap

Add your swapfile to /etc/fstab so it’ll be used on every boot.

Edit your fstab partitions:

vim /etc/fstab

add this to the end:

/swapfile none swap defaults 0 0

Boot loader

See Arch boot process for a list of Linux-capable boot loaders.

Note: If you have an Intel or AMD CPU, enable microcode updates.

Reboot

Exit the chroot environment by typing exit or by pressing Ctrl+d.

Run sync to ensure all changes are flushed to disk:

sync

Optionally manually unmount all the partitions with:

umount -R /mnt

This allows noticing any “busy” partitions, and finding the cause with fuser(1).

Close volume:

cryptsetup close cryptroot

Finally, restart the machine by typing:

reboot

Any partitions still mounted will be automatically unmounted by systemd. Remember to remove the installation media and then login into the new system with the root account.

To connect via serial console:

minicom -D /dev/tty.usb<TAB>

Securing the unencrypted boot partition

Wiki Links:

[0x3] Post install

swap error on boot?

# dd if=/dev/zero of=/swapfile count=XXX bs=XXX
# chmod 0600 /swapfile
# mkswap /swapfile
mkswap: /swapfile: warning: wiping old swap signature.
Setting up swapspace version 1, size = 5.1 GiB (5468319744 bytes)
no label, UUID=f2184ade-3067-4404-b96a-c89972154551
# swapon /swapfile

Host entry in /etc/hosts on local workstation:

192.168.178.18	yourhost

Insert ethernet cable, then run:

dhcpcd

to get an IP address assigned from your router.

Update the system:

pacman -Syu

Install deps:

pacman -Sy vim docker rsync tree zip unzip screen openssh ntp

Setup SSH:

mkdir ~/.ssh
cd ~/.ssh
ssh-keygen -t rsa -C ""
touch ~/.ssh/authorized_keys
# ... add public key
systemctl start sshd
systemctl enable sshd

Prevent logins via password in /etc/ssh/sshd_config:

PubkeyAuthentication yes
PasswordAuthentication no

Restart ssh daemon:

systemctl restart sshd

Consider to change the SSH daemon to run on a different port then 22, and disable SSH login as root, especially if you will make the system publicly reachable.

Finally, test connecting via SSH:

ssh root@yourhost

[0x4] Resources