Ubuntu Full Disk Encryption with LACP interfaces

When unlocking the full disk encryption of a server, you usually need to set the ip= kernel command line parameter and install dropbear into the initramfs.

After a bit fiddling, it should work.

When having an LACP interface as the main interface, there is no out of the box solution to configure the network. You need to assemble the configuration manually.

Other forums suggest just using the first interface. But this works only under some circumstances. You need to use a force-up or similar on your switch port to effectively disable LACP on the interface.

This guide steps you through a plain Ubuntu 22.04 setup to have SSH FDE unlock with LACP usable.

Installing dropbear

First install dropbear packages:

bebe@server:~$ sudo apt install -y dropbear dropbear-initramfs
[...]

Making bonding available in initrd

We then need to configure the available modules. The file is usually empty. If there are already some modules listed, append the bonding line.

bebe@server:~$ cat /etc/initramfs-tools/modules
bonding
bebe@server:~$

Injecting a bond interface

After having the bond module available in the initrd, we’re going to create a bond interface in it.

Since there is no construct to do it out of the box, self-assembled ip link commands must do the job, which are chained right before and after dropbear’s hooks.

For this we edit /etc/dropbear/initramfs/dropbear.conf with the following content. This is the configuration file, which is actually a sourced bash script.

Now comes the hacky part, we inject some actions into the init-premount and init-bottom initrd stages via the dropbear conf. This ensures much better dependency coupling, since initramfs-tools does not have a good dependency system.

bebe@server:~$ cat /etc/dropbear/initramfs/dropbear.conf
# We boot the server on Port 2222 to avoid known_hosts problems
DROPBEAR_OPTIONS="-p 2222"

# master must not be the same name as configured in netplan
BONDING_INTERFACE_MASTER="bondINITRD"
BONDING_INTERFACE_SLAVES="enp1s0f0 enp1s0f1"

# Create the bonding interface
# Only created when being in init-premount script
if [ "$(basename $(dirname $(readlink -f "${0}")))" = "init-premount" ]; then
	ip link add dev ${BONDING_INTERFACE_MASTER} type bond

	# busybox initrd `ip` does not translate most params and silently ignores it
	# If you have other required params, add them here:
	echo 802.3ad  > /sys/class/net/${BONDING_INTERFACE_MASTER}/bonding/mode

	# Now add the "slaves" to their bond
	for slave in ${BONDING_INTERFACE_SLAVES}; do
		ip link set dev ${slave} master ${BONDING_INTERFACE_MASTER}
	done
fi

# **Delete** the interface afterwards, otherwise systemd-networkd-wait-online will timeout
if [ "$(basename $(dirname $(readlink -f "${0}")))" = "init-bottom" ]; then
	ip link del dev ${BONDING_INTERFACE_MASTER}

	# set the slaves to down, so that the switch get's the signal
	# and re-initialises its own IPMI when we create the bond via netplan again
	for slave in ${BONDING_INTERFACE_SLAVES}; do
		ip link set dev ${slave} down
	done
fi
bebe@server:~$

IPs for dropbear

After having the bonding interface defined above, go on and assign the IPs normally.

Now let’s activate dropbear in the initramfs and also set the IP addresses. You probably need to copy them from /etc/netplan/... or /etc/network/interfaces.

To configure the IP= variable, consult nfsroot.txt of the kernel documentation. It’s analog to the ip= kernel command line.

bebe@server:~$ cat /etc/initramfs-tools/conf.d/dropbear.conf
# Activate dropbear in initramfs
DROPBEAR=y
# Must match the BONDING_INTERFACE_MASTER
DEVICE=bondINITRD
# See nfsroot.txt manpage for more details
#IP=<addr>::<gatew>:<netmask>::<interface>:off
IP=1.2.3.4::1.2.3.1:255.255.255.0::bondINITRD:off

Also you need to assemble a separate authorized_keys file. Just copy your user’s authorized_keys file and prepend with command="cryptroot-unlock" for easyness:

bebe@server:~$ cat /etc/dropbear/initramfs/authorized_keys
# Add your authorized keys here
command="cryptroot-unlock" ssh-ed25519 ....
bebe@server:~$

Re-build and Reboot

Lastly, we need to re-build the initramfs:

bebe@server:~$ sudo update-initramfs -u
update-initramfs: Generating /boot/initrd.img-5.15.0-101-generic
...

And now we can reboot and de-crypt the drive:

bebe@server:~$ sudo reboot
Connection to 1.2.3.4 closed by remote host.
Connection to 1.2.3.4 closed.
[bebe:~] % #sleep ....
[bebe:~] % ssh -p 2222 root@1.2.3.4
Please unlock disk dm_crypt-0:
cryptsetup: dm_crypt-0 set up successfully
Connection to 1.2.3.4 closed.
[bebe:~] %

Have fun with it!

Debugging

If something went south, you can easily debug the initramfs. The initramfs article in the Debian Wiki is quite helpful.

One thing I’ve learnt while setting up the LACP stuff: Use break=bottom in the kernel command line. This will drop you onto a busybox shell in the initrd after typing in the password on the TTY. To achieve it, press e when you’re in GRUB and then append it to the linux /vmlinuz... line and press Ctrl+X to boot with this option.