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