How to Install ArchLinux With Full Disk Encryption on an ODROID-C2

ODROID Magazine ArchLinux With Full Disk Encryption Dropbear ssh

Full Disk Encryption (FDE) protects our data against unauthorised access in case someone gains physical access to the storage media. In this article, I will describe how to install ArchLinux with Full Disk Encryption on ODROID-C2. The encryption method is LUKS with XTS key-size 512 bit (AES-256).

In a nutshell, Full Disk Encryption requires the following:

  • Encrypting a partition and copying the root filesystem to it.
  • The kernel to include the dm_crypt kernel module. In our case, this is already included by default, therefore we won’t need to re-compile the kernel.
  • The initramfs to include the dm_crypt kernel module and the cryptsetup binary. We use a tool called dracut to generate the required initramfs. Dracut supports the required functionality via the additional modules crypt and lvm.
  • Passing the dracut options for LUKS to the initramfs via the bootargs property inside boot.ini. For example, say that in our case, we want the initramfs to unlock a LUKS volume with UUID ae51db2d-0890-4b1b-abc5-8c10f01da353 and load the root filesystem from the device mapper /dev/mapper/vg-root. To pass these dracut options we configure the following:
sudo nano /boot/boot.ini
setenv bootargs "rd.luks.uuid=ae51db2d-0890-4b1b-abc5-8c10f01da353 root=/dev/mapper/vg-root rootwait < leave the rest as is >"

Note
A lot of the steps throughout this document involve editing configuration files. To keep the words to the minimum, we use the above notation as a very concise way to describe such file editing steps. The above notation means:

  • You need to edit the file /boot/boot.ini with root privileges (hence sudo nano /boot/boot.ini). Nano is the command line editor, however feel free to use another editor of your choice.
  • Find the line starting with setenv bootargs and add or edit the configuration options rd.luks.uuid=ae51db2d-0890-4b1b-abc5-8c10f01da353 root=/dev/mapper/vg-root rootwait. Some files mentioned throughout this document might have the corresponding line being commented out or not present at all. If that’s the case you will need to uncomment or append the line into the file, respectively.
  • Leave the rest of the line after rootwait as is.

Additionally, for a headless setup, you will need to enable remote unlocking via SSH as described in “Remotely unlock the LUKS rootfs during boot using Dropbear sshd” article at http://bit.ly/2g6qjDv. Last but not least, if you prefer to use the described functionality out of the box, simply download the OS image at http://bit.ly/2xR8LDe. Either way, the current document will provide more technical details in regards to the underlying components and how they work together in a Full Disk Encryption environment.

Hardware requirements

  • ODROID-C2
  • A Linux box from which you will flash the OS image and interact with the ODROID-C2
  • USB disk with at least 4GB capacity
  • A microSD card or eMMC module with at least 4GB capacity
  • (Optional) A USB-UART module kit for connecting with the ODROID-C2’s serial console. Refer to the post at http://bit.ly/2fM29BB for instructions on how to connect along with explanation why the serial console is highly recommended in this case.

Flash the OS image and boot ODROID-C2

Flash the OS image to the USB disk by following the instructions from http://bit.ly/2fGKEik. Replace /dev/mmcblk0 in the following instructions with the device name for the microSD card as it appears on your computer. If mounted, unmount the partitions of the microSD card:

$ lsblk
$ umount /dev/mmcblk0p1
$ umount /dev/mmcblk0p2

Zero the beginning of the microSD card:

$ sudo dd if=/dev/zero bs=1M count=8 of=/dev/mmcblk0
$ sync

Using a tool like GParted, create an MBR/msdos partition table and two partitions on the microSD card:

  • ext4 partition with 128M size
  • lvm2 partition occupying the rest of the space (no need to format yet)

Next, copy the contents of the /boot directory from the USB disk into the first partition of the microSD card:

$ sudo cp -R /media/user/usb-disk/boot/* /media/user/micro-sd-card-part1/

Create a symbolic link as a workaround for the hardcoded boot.ini path of the alarm/uboot-odroid-c2 (http://bit.ly/2xbEdPo):

$ cd /media/user/micro-sd-card-part1
$ sudo ln -s . boot

Then, flash the bootloader files:

$ sudo ./sd_fusing.sh /dev/mmcblk0

Determine the UUID of the USB disk:

$ sudo lsblk -o name,uuid,mountpoint
NAME UUID MOUNTPOINT
sdb
└─sdb1 2b53696c-2e8e-4e61-a164-1a7463fd3785 /media/user/usb-disk

Note that If there are duplicate UUIDs among the partitions of the USB disk and the microSD card, then remove the duplicates to avoid future conflicts:

$ sudo tune2fs /dev/sda2 -U $(uuidgen)

Configure the boot.ini to boot from the USB disk. To do so, use the UUID from the previous step to configure the boot.ini of the microSD card:

$ sudo nano /media/user/micro-sd-card-part1/boot.ini
$ setenv bootargs "root=UUID=2b53696c-2e8e-4e61-a164-1a7463fd3785 rootwait "

Unmount, run sync few times, and remove the microSD card and the USB disk from the Linux box. Plug the microSD card and the USB disk to the ODROID-C2, then boot the ODROID-C2 and connect to its serial console. If you need instructions on how to connect to the serial console, please refer to the article at http://bit.ly/2fM29BB.

If all goes, well you should boot into the USB disk. Note that if root=UUID=2b53696c-2e8e-4e61-a164-1a7463fd3785 doesn’t work, then try root=/dev/sda1, root=/dev/sdb1 or whatever device name you see in the console prior to the failed boot (e.g,. [ 14.812393] sd 1:0:0:0: [sda] Attached SCSI removable disk). If you are still having issues try restarting a few times and/or repositioning the USB disk into a different USB port on the ODROID-C2. Don’t worry if it seems to be giving you trouble, as you won’t have to boot to the USB disk again after the first successful boot.

Next, verify that the root filesystem is mounted from the USB disk:

$ df -h

Change passwords

Change the passwords for the alarm and the root user. The default credentials are alarm/alarm and root/root.

$ passwd
$ su
$ passwd

Install required packages

$ su
$ pacman -Syu
$ pacman -S --needed sudo python git rsync lvm2 cryptsetup

(Optional) Setup passwordless sudo for the user alarm:

$ echo 'alarm ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/010_alarm-nopasswd

Install dracut
Install pacaur (http://bit.ly/2yEjAaY):

$ sudo pacman -S --needed base-devel cower
$ mkdir -p ~/.cache/pacaur && cd "$_"
$ cower -d pacaur
$ cd pacaur
$ makepkg -si --noconfirm --needed

Install dracut using the pacaur tool:

$ pacaur -S dracut

Verify the dracut installation by listing modules

$ dracut --list-modules

If the “pacaur -S dracut” command reports an error that aarch64 architecture is not supported by the package, then follow these steps to configure support for aarch64:

$ cd ~/.cache/pacaur/dracut/
$ nano PKGBUILD # replace `arch=("i686" "x86_64")` with `arch=("aarch64")`
$ makepkg -si --noconfirm --needed

If the makepkg reports an error like dracut-046.tar …​ FAILED (unknown public key 340F12141EA0994D), then type these commands and try again:

$ gpg --full-gen-key
$ gpg --recv-key 340F12141EA0994D

Refer to Makepkg signature checking for more details at http://bit.ly/2wuuBe6.

If the “gpg –full-gen-key” command reports the error Key generation failed: No pinentry, then follow the below steps to configure gpg as described at http://bit.ly/2yDAJBy and try again. The gpg-agent needs to know how to ask the user for the password:

$ nano ~/.gnupg/gpg-agent.conf
$ pinentry-program /usr/bin/pinentry-curses
$ gpg-connect-agent reloadagent /bye

If makepkg reports missing dependencies error, then upgrade the packages and try again.

$ sudo pacman -Syu
$ pacaur -Syua

Prepare the LUKS rootfs
Encrypt the second partition of the microSD card (see also Recommended options for LUKS at http://bit.ly/2yF15D2):

$ sudo cryptsetup -v -y -c aes-xts-plain64 -s 512 -h sha512 -i 5000 --use-random luksFormat /dev/mmcblk0p2

-v = verbose
-y = verify passphrase, ask twice, and complain if they don’t match
-c = specify the cipher used
-s = specify the key size used
-h = specify the hash used
-i = number of milliseconds to spend passphrase processing (if using anything more than sha1, must be great than 1000)
–use-random = which random number generator to use
luksFormat = to initialize the partition and set a passphrase
/dev/mmcblk0p2 = the partition to encrypt

Unlock the LUKS device and mount it at /dev/mapper/lvm:

$ sudo cryptsetup luksOpen /dev/mmcblk0p2 lvm

Create primary volume, volume group, and logical volume:

$ sudo pvcreate /dev/mapper/lvm
$ sudo vgcreate vg /dev/mapper/lvm
$ sudo lvcreate -l 100%FREE -n root vg

Create the filesystem:

$ sudo mkfs.ext4 -O ^metadata_csum,^64bit /dev/mapper/vg-root

Mount the new encrypted root volume (logical volume):

$ sudo mount /dev/mapper/vg-root /mnt

Copy the existing root volume to the new, encrypted root volume. With a 1.5GB installation, it completes in about 6 minutes on an average microSD:

$ sudo rsync -av \
--exclude=/boot \
--exclude=/mnt \
--exclude=/proc \
--exclude=/dev \
--exclude=/sys \
--exclude=/tmp \
--exclude=/run \
--exclude=/media \
--exclude=/var/log \
--exclude=/var/cache/pacman/pkg \
--exclude=/usr/src/linux-headers* \
--exclude=/home/*/.gvfs \
--exclude=/home/*/.local/share/Trash \
/ /mnt

If the SSH host keys are empty, remove them so that they will be regenerated the next time the sshd starts. This will prevent the memory leak issue as described at http://bit.ly/2xQxGqe.

$ sudo rm /mnt/etc/ssh/ssh_host*key*

Create some directories and mount the boot partition:

$ sudo mkdir -p /mnt/boot /mnt/mnt /mnt/proc /mnt/dev /mnt/sys /mnt/tmp
$ sudo mount -t ext4 /dev/mmcblk0p1 /mnt/boot

Register the encrypted volume in crypttab

$ sudo bash -c 'echo lvm UUID=$(cryptsetup luksUUID /dev/mmcblk0p2) none luks>> /mnt/etc/crypttab'

Configure fstab:

$ sudo nano /mnt/etc/fstab
$ /dev/mapper/vg-root / ext4 errors=remount-ro,noatime,discard 0 1
$ /dev/mmcblk0p1 /boot ext4 noatime,discard 0 2
Next, generate a new initramfs using dracut. The following commands will add the dracut modules crypt and lvm to the initramfs. These modules will prompt for LUKS password during boot and unlock the LUKS volume. Note that the order of the modules is important:

$ sudo dracut --force --hostonly -a "crypt lvm" /mnt/boot/initramfs-linux.img

Next, determine the LUKS UUID:

$ sudo cryptsetup luksUUID /dev/mmcblk0p2
470cc9eb-f36b-40a2-98d8-7fce3285bb89

Configure the rd.luks.uuid and root dracut options in bootargs. These will unlock the LUKS volume and load the rootfs from it during boot:

$ sudo nano /mnt/boot/boot.ini
$ setenv bootargs "rd.luks.uuid=470cc9eb-f36b-40a2-98d8-7fce3285bb89 root=/dev/mapper/vg-root rootwait "

Note that in the above step, do NOT delete the rest of bootargs, essentially replace root=UUID=2b53696c-2e8e-4e61-a164-1a7463fd3785 with rd.luks.uuid=470cc9eb-f36b-40a2-98d8-7fce3285bb89 root=/dev/mapper/vg-root and leave the rest of bootargs untouched. Then, unmount and reboot into the LUKS rootfs:

$ sudo umount /mnt/boot
$ sudo umount /mnt
$ sudo reboot

If all goes well you will be prompted to enter the LUKS password during boot. Next, verify the LUKS rootfs:

df -h
output
Filesystem Size Used Avail Use% Mounted on
devtmpfs 714M 0 714M 0% /dev
tmpfs 859M 0 859M 0% /dev/shm
tmpfs 859M 8.3M 851M 1% /run
tmpfs 859M 0 859M 0% /sys/fs/cgroup
/dev/mapper/vg-root 1.7G 1.4G 256M 85% /
tmpfs 859M 0 859M 0% /tmp
/dev/mmcblk0p1 120M 26M 86M 23% /boot
tmpfs 172M 0 172M 0% /run/user/1000

Next, remotely unlock the LUKS rootfs during boot using Dropbear sshd. Replace 10.0.0.100 in the following instructions with the IP address assigned to the ODROID-C2 by your local DHCP server. Use the fing tool to find the assigned IP address (e.g. sudo fing 10.0.0.1/24). Then, make sure the SSH daemon is running:

$ sudo systemctl status sshd
$ journalctl -u sshd -n 100

If the above commands report that sshd fails with memory allocation error, then enter the following commands:

$ sudo rm /etc/ssh/ssh_host*key*
$ sudo systemctl start sshd

Refer to the article at http://bit.ly/2xQxGqe for more information about memory leaks in sshd.

Install and configure Dropbear
Install the dracut module crypt-ssh:

$ pacaur -S dracut-crypt-ssh-git

From your Linux box, copy the public SSH key to the appconf/dracut-crypt-ssh/authorized_keys file on the remote ODROID-C2 server:

$ cat ~/.ssh/*.pub | ssh alarm@10.0.0.100 'umask 077; mkdir -p appconf/dracut-crypt-ssh; touch appconf/dracut-crypt-ssh/authorized_keys; cat >>appconf/dracut-crypt-ssh/authorized_keys'

Next, configure the crypt-ssh module:

$ sudo nano /etc/dracut.conf.d/crypt-ssh.conf
$ dropbear_acl="/home/alarm/appconf/dracut-crypt-ssh/authorized_keys"

Generate a new initramfs using dracut. The following commands will add the dracut modules network and crypt-ssh to the initramfs. Note that the order of the modules is important:

$ sudo dracut --force --hostonly -a "network crypt lvm crypt-ssh" /boot/initramfs-linux.img

Enable network access during boot by adding rd.neednet and ip dracut options to bootargs:

$ sudo nano /boot/boot.ini
setenv bootargs "rd.neednet=1 ip=10.0.0.100::10.0.0.1:255.255.255.0:archlinux-luks-host:eth0:off rd.luks.uuid=ae51db2d-0890-4b1b-abc5-8c10f01da353 root=/dev/mapper/vg-root rootwait "

If you prefer DHCP instead of static ip, simply replace with ip=dhcp. Refer to network documentation of dracut at http://bit.ly/2g6XCXk and dracut options at http://bit.ly/2yUBFT6 for more options (man dracut.cmdline). Reboot so that Dropbear starts, allowing for remote unlocking:

$ sudo reboot

From your Linux box, connect to the remote Dropbear SSH server running on the ODROID-C2:

$ ssh -p 222 root@10.0.0.100

Unlock the volume (asks you for the passphrase and sends it to console):

$ console_auth
Passphrase:

If unlocking the device succeeded, the initramfs will clean up itself and Dropbear terminates itself and your connection.

You can also type “console_peek” which prints what’s on the console. There is also the unlock command, but we encountered an issue while testing as described at http://bit.ly/2fHB2nw.

Some use cases require feeding input automatically to the interactive command console_auth. From your Linux box, unlock the volume:

$ ssh -p 222 root@10.0.0.100 console_auth < password-file

or

$ gpg2 --decrypt password-file.gpg | ssh -p 222 root@10.0.0.100 console_auth
For additional security, you might want to only allow the execution of the command console_auth and nothing else. To achieve this, you need to configure the SSH key with restricting options in the authorized_keys file. From your Linux box, copy the public SSH key, with restricting options, to the appconf/dracut-crypt-ssh/authorized_keys file on the remote ODROID-C2 server:
$ (printf 'command="console_auth",no-agent-forwarding,no-port-forwarding,no-pty,no-X11-forwarding ' && cat ~/.ssh/*.pub) | ssh alarm@10.0.0.100 'umask 077; mkdir -p appconf/dracut-crypt-ssh; touch appconf/dracut-crypt-ssh/authorized_keys; cat >appconf/dracut-crypt-ssh/authorized_keys'

Refer to the Dropbear documentation for a full list of restricting options. Prior to continuing, it might be a good idea to create a copy of the initramfs:

$ sudo cp /boot/initramfs-linux.img /boot/initramfs-linux.img-`date +%y%m%d-%H%M%S`

In a headless setup, carefully examine the restricting options to avoid locking yourself out.

Finally, generate a new initramfs using dracut:

$ sudo dracut --force --hostonly -a "network crypt lvm crypt-ssh" /boot/initramfs-linux.img

In this case, you can unlock the volume interactively by simply typing the following command:

$ ssh -p 222 root@10.0.0.100

Note that when typing the above command, the console_auth command is automatically invoked on the remote server and immediately prompts for password, as if you just typed ssh -p 222 root@10.0.0.100 console_auth. While you type the password, it will be displayed on the screen in plain text. Therefore, you should avoid unlocking interactively when the access is restricted to the console_auth command. When you press enter you will be disconnected no matter whether the password was correct or not. Whereas with the non-restricted login (see http://bit.ly/2hHAGl0), you would only be disconnected if the password was correct, meaning that you would have feedback for whether the unlocking was successful or not. On the other hand, to unlock the volume using a password file, from your Linux box, type the following command:

$ ssh -p 222 root@10.0.0.100 < password-file

or

$ gpg2 --decrypt password-file.gpg | ssh -p 222 root@10.0.0.100

For comments, questions, or suggestions, please visit the original blog post at http://bit.ly/2xMQE3I.

References

ArchLinux dm-crypt/Encrypting an entire system (http://bit.ly/2xPaybR)

How to install Debian with Full Disk Encryption on ODROID-C2 (http://bit.ly/2g6JtcF)

Be the first to comment

Leave a Reply