This article is mainly based on this tutorial and is motivated by two objectives:
- Make sure that this knowledge is replicated where I could always find it
- Provide a variation with a second encrypted partition (
/data
) and a two stages decryption process
I detail here the installation process of Armbian on a SSD (/dev/nvme0n1
) of a rock-5-itx board: adapt it to your board and target device. All the steps are executed on an Armbian (or any Debian based distro) booted from the SD card where we prepare the target device aiming for this set up and no LVM:
partition | crypt mapper | decryption mode | mount point |
---|---|---|---|
/dev/nvme0n1p1 |
None | None | /boot |
/dev/nvme0n1p2 |
/dev/mapper/rootfs |
LUKS, passphrase | / |
/dev/nvme0n1p3 |
/dev/mapper/datafs |
Binary key file | /data |
Installation from live SD card
Update your system and install cryptsetup
apt update && apt upgrade
apt install cryptsetup
Download the armbian image for your board
Create a build directory
mkdir armbenc-build && cd armbenc-build
download and extract the image for your board
wget https://dl.armbian.com/rock-5-itx/Bookworm_vendor_minimal
mv Bookworm_vendor_minimal Armbian_24.11.1_Rock-5-itx_noble_vendor_6.1.75_minimal.img.xz
xz -dv *.img.xz
Create mount directories and set up the loop mount
mkdir -p mnt boot root
Identify the first free loop device (most probably /dev/loop0
)
losetup -f
Associate the device with the image
losetup -P /dev/loop0 *.img
From the output of this command, determine the Disklabel type (MBR vs GPT) and the start sector of the partition
fdisk -l /dev/loop0
Here, the image uses a GPT start disk label and starts at 32768
Disk /dev/loop0: 1,46 GiB, 1564475392 bytes, 3055616 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 1C84A660-73BD-9C4C-95E1-E3EC148A5EBF
Device Start End Sectors Size Type
/dev/loop0p1 32768 3055582 3022815 1,4G Linux root (ARM-64)
Mount the loop device
mount /dev/loop0p1 mnt
Copy the boot loader to the target device
From this point, we start to alter the target device (
/dev/nvme0n1
) and any data it might contain. Identify your target device usingfdisk -l
and adapt if necessary.
Determine the partition label expected by the image
fdisk -l *.img
### MBR (DOS) images:
dd if=$(echo *.img) of=/dev/nvme0n1 bs=512 count=32768
### GPT images:
dd if=$(echo *.img) of=/dev/nvme0n1 bs=512 skip=64 seek=64 count=32704
Partition the target device
fdisk /dev/nvme0n1
disklabel
Create a new disklabel using:
o
for MBR images ORg
for GPT images
first partition: boot
Press n
to create a new 400M partition starting at the same sector of our image (in our case: 32768)
Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-7814037134, default 2048): 32768
Last sector, +/-sectors or +/-size{K,M,G,T,P} (32768-7814037134, default 7814035455): +400M
Created a new partition 1 of type 'Linux filesystem' and of size 400 MiB.
Then, set the partition type using t
(you can list all the partition types using l
). In our case, we are looking for Linux root (ARM-64) (number 27).
Command (m for help): t
Selected partition 1
Partition type or alias (type L to list all): 27
Changed type of partition 'Linux filesystem' to 'Linux root (ARM-64)'.
check with p
that you have the following
Command (m for help): p
Disk /dev/nvme0n1: 3,64 TiB, 4000787030016 bytes, 7814037168 sectors
Disk model: Samsung SSD 990 EVO Plus 4TB
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 6534C15B-008E-CC49-968E-6B8C9E033F3F
Device Start End Sectors Size Type
/dev/nvme0n1p1 32768 851967 819200 400M Linux root (ARM-64)
Second partition: system
Press n
again to create a new 120G partition (recommended by openmediavault, adapt to your needs).
Command (m for help): n
Partition number (2-128, default 2):
First sector (2048-7814037134, default 851968):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (851968-7814037134, default 7814035455): +120G
Created a new partition 2 of type 'Linux filesystem' and of size 120 GiB.
set also the partition type using t
to Linux root (ARM-64)
Command (m for help): t
Partition number (1,2, default 2):
Partition type or alias (type L to list all): 27
Changed type of partition 'Linux filesystem' to 'Linux root (ARM-64)'.
third partition: data
Create a final partition filling the rest of the disk
Command (m for help): n
Partition number (3-128, default 3):
First sector (2048-7814037134, default 252510208):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (252510208-7814037134, default 7814035455):
Created a new partition 3 of type 'Linux filesystem' and of size 3,5 TiB.
However, this time, we do need a different partition type. We will use Linux server data (option 21)
Command (m for help): t
Partition number (1-3, default 3):
Partition type or alias (type L to list all): 21
Changed type of partition 'Linux filesystem' to 'Linux server data'
Check and apply
Check with p
that you have the following
Device Start End Sectors Size Type
/dev/nvme0n1p1 32768 851967 819200 400M Linux root (ARM-64)
/dev/nvme0n1p2 851968 252510207 251658240 120G Linux root (ARM-64)
/dev/nvme0n1p3 252510208 7814035455 7561525248 3,5T Linux server data
If so, apply the changes with w
, if not, reset with d
and start over
Copy the system to the target device
copy the boot partition (/dev/nvme0n1p1)
mkfs.ext4 /dev/nvme0n1p1
e2label /dev/nvme0n1p1 CRYPTO_BOOT
mount /dev/nvme0n1p1 boot
cp -av mnt/boot/* boot
(cd boot; ln -s . boot)
encrypt and format the system partition (/dev/nvme0n1p2)
Encrypt the partition. You will be asked to choose a LUKS passphrase that you will need every time you reboot your system.
cryptsetup luksFormat /dev/nvme0n1p2
Decrypt the very same partition and format it in EXT4 (or any journaling file system but you will need to adapt the rest of the procedure).
cryptsetup luksOpen /dev/nvme0n1p2 rootfs
mkfs.ext4 /dev/mapper/rootfs
Mount the partition and copy the system to it.
mount /dev/mapper/rootfs root
(cd mnt && rsync -a --info=progress2 --exclude=boot * ../root)
sync
mkdir root/boot
mkdir root/data
touch root/root/.no_rootfs_resize
encrypt and format the data partition (/dev/nvme0n1p3)
To avoid to get a second prompt to unlock this second partition, we will create a key stored in the encrypted system partition. This key will live in /root/.luks_keys
and should remain readable and writable only by root (600)
mkdir -p root/root/.luks_keys
chmod u=rw,g=,o= root/root/.luks_keys
dd if=/dev/urandom of=root/root/.luks_keys/data_part.key bs=1024 count=1
chmod u=rw,g=,o= root/root/.luks_keys/data_part.key
Use it to encrypt this third partition
cryptsetup luksFormat /dev/nvme0n1p3 root/root/.luks_keys/data_part.key
then open it and format it
cryptsetup luksOpen -d root/root/.luks_keys/data_part.key /dev/nvme0n1p3 datafs
mkfs.ext4 /dev/mapper/datafs
we will edit /etc/crypttab
later to automate the decryption once the system partition is decrypted
unmount the boot partition
umount mnt boot
losetup -d /dev/loop0
Prepare the target system chroot
Define practical env vars for later partition identification
BOOT_PART=($(lsblk -l -o NAME,LABEL | grep CRYPTO_BOOT))
ROOT_PART=${BOOT_PART%1}2
DATA_PART=${BOOT_PART%1}3
ROOT_UUID="$(lsblk --nodeps --noheadings --output=UUID /dev/$ROOT_PART)"
DATA_UUID="$(lsblk --nodeps --noheadings --output=UUID /dev/$DATA_PART)"
BOOT_UUID="$(lsblk --noheadings --output=UUID /dev/$BOOT_PART)"
Make sure all UUID
are properly set by comparing the following with blkid
’s output
echo $BOOT_UUID
echo $ROOT_UUID
echo $DATA_UUID
Then
cd root
mount /dev/$BOOT_PART boot
mount -o rbind /dev dev
mount -t proc proc proc
mount -t sysfs sys sys
In order to have a working internet connection inside the chroot, copy the following files
rm etc/resolv.conf && cat /etc/resolv.conf > etc/resolv.conf
rm etc/hosts && cat /etc/hosts > etc/hosts
Edit or create required configuration files in the target system
We then need to configure initramfs in order to get a minimal dropbear SSH server to unlock the system in case of reboot (console from screen and keyboard will also be available).
Armbian env setting
If boot/armbianEnv.txt
exists, edit console
and rootdev
using nano or vi. Modulo the board’s name in overlay_prefix
and fdtfile
, the file should look like this
verbosity=1
bootlogo=false
console=display
extraargs=cma=256M
overlay_prefix=rockchip-rk3588
fdtfile=rockchip/rk3588-rock-5-itx.dtb
rootdev=/dev/mapper/rootfs
rootfstype=ext4
If boot/armbianEnv.txt
does not exist, please refer to the original tutorial (step 9)
initramfs configuration
Identify the default network interface using ip addr
(in my case, enP3p49s0
). Edit etc/initramfs-tools/initramfs.conf
.
For fixed IP, you can set it together with the network mask using, for instance
IP=192.168.1.49:::255.255.255.0::enP3p49s0:off
For DHCP, simply set the network interface using
DEVICE=enP3p49s0
adapt to your NIC name
We also need to create etc/initramfs-tools/hooks/cryptroot-unlock.sh
content of which should be:
#!/bin/sh
if [ "$1" = 'prereqs' ]; then echo 'dropbear-initramfs'; exit 0; fi
. /usr/share/initramfs-tools/hook-functions
source='/tmp/cryptroot-unlock-profile'
root_home=$(echo $DESTDIR/root-*)
root_home=${root_home#$DESTDIR}
echo 'if [ "$SSH_CLIENT" ]; then /usr/bin/cryptroot-unlock; fi' > $source
copy_file ssh_login_profile $source $root_home/.profile
exit 0
and make it executable
chmod 755 'etc/initramfs-tools/hooks/cryptroot-unlock.sh'
Dropbear configuration
Create an authorized_keys
file that will contain the public keys of all the clients allowed to unlock the server (I recommend minimum 2).
mkdir -p etc/dropbear/initramfs
touch etc/dropbear/initramfs/authorized_keys
Add the keys using
echo "<public_key>" >> etc/dropbear/initramfs/authorized_keys
Create dropbear configuration file
echo 'DROPBEAR_OPTIONS="-I 180 -p 2222"' > etc/dropbear/initramfs/dropbear.conf
echo 'DROPBEAR=y' >> etc/dropbear/initramfs/dropbear.conf
Here we set the timeout to 180 seconds using
-I
as an extra layer of security but you are free to remove it if you find it ridiculously paranoid.
Edit fstab
echo '/dev/mapper/rootfs / ext4 defaults,noatime,nodiratime,commit=600,errors=remount-ro 0 1' > etc/fstab
echo '/dev/mapper/datafs /data ext4 defaults,noatime,nodiratime,commit=600,errors=remount-ro 0 2' >> etc/fstab
echo "UUID=$BOOT_UUID /boot ext4 defaults,noatime,nodiratime,commit=600,errors=remount-ro 0 2" >> etc/fstab
echo 'tmpfs /tmp tmpfs defaults,nosuid 0 0' >> etc/fstab
Crypttab configuration
echo "rootfs UUID=$ROOT_UUID none initramfs,luks" > etc/crypttab
echo "datafs UUID=$DATA_UUID /root/.luks_keys/data_part.key noauto" >> etc/crypttab
Make sure the file looks like this
cat etc/crypttab
rootfs UUID=d3d5d09e-9774-4b64-b6fa-209ed62eca6e none initramfs,luks
datafs UUID=8bfda2c2-8037-4be0-8be7-63ddeac03e1b /root/.luks_keys/data_part.key noauto
rootfs is unlocked at boot time using
initramfs
,luks
. systemd will look for data_part.key when it becomes available after boot and rootfs decryption thanks tonoauto
.
Chroot into the target system, install packages and configure
chroot .
Update the system and install cryptsetup-initramfs
and dropbear-initramfs
apt update
echo 'force-confdef' > /root/.dpkg.cfg
apt --yes install cryptsetup-initramfs dropbear-initramfs
rm /root/.dpkg.cfg
Make sure that initramfs is properly set up (all 3 commands should produce an output)
lsinitramfs /boot/initrd.img-* | grep 'usr.*cryptsetup'
lsinitramfs /boot/initrd.img-* | grep dropbear
lsinitramfs /boot/initrd.img-* | grep authorized_keys
generate SSH host keys
ssh-keygen -A
Exit the chroot and shut the system down
exit
halt -p
Owner’s turn
Remove the SD card, start your system and, ssh to port 2222. You should get a prompt
ssh root@192.168.1.49 -p 2222
To unlock root partition, and maybe others like swap, run `cryptroot-unlock`.
BusyBox v1.35.0 (Debian 1:1.35.0-4+b3) built-in shell (ash)
Enter 'help' for a list of built-in commands.
Please unlock disk rootfs:
cryptsetup: rootfs set up successfully
~ # Connection to 192.168.1.49 closed by remote host.
Connection to 192.168.1.49 closed.
If everything went well, you should be able to ssh root@192.168.1.49
. Your fully encrypted armbian is ready
Troubleshooting
After boot, I can’t find dropbear listening to 2222
There is probably something wrong with initramfs.conf
or the dropbear configuration. Check th
I managed to get a dropbear SSH session, succesfully unlocked rootfs but get an other prompt later in the boot sequence
Make sure the entry for datafs in /etc/crypttab
uses noauto
, that the key exists and is properly set. With this option, systemd will then not open the encrypted device on boot, but instead wait until it is actually accessed.