Tutorials
0x53.net

Custom minimal initramfs

An initramfs, short for initial ram file system, is a mini userspace that is directly "baked" into the kernel. After the kernel has been loaded by the UEFI/BIOS, it itself loads the the initramfs into memory. Thus, a small userspace is available before any storage medium has been mounted.

The initramfs can be useful to e.g. decrypt storage media that hold the actual userspace of the machine.

How to create a minimal initramfs

Create the following directory structure:

/usr/src/initramfs
├── bin
├── init
├── dev
├── lib
├── proc
└── sys
			

The directories can then be populated with the desired userspace software. The file "init" needs to be executable and contain the program to initially start.

Additionally, the kernel configuration must be adapted so the initramfs is actually "baked" into the kernel image. This is done as follows:

Then, recompile the kernel. Note: if the initramfs is changed, make sure to run make clean before recompiling the kernel. Otherwise, the changes are not applied.

Examples

Minimal initrafms to decrypt an encrypted rootfs

Compile and statically link the following software:

To prepare the directory tree:

  1. Copy the binaries busybox and cryptsetup to the directory /usr/src/initramfs/bin.
  2. make sure /usr/src/initramfs/dev/console exists: mknod -m 600 /usr/src/initramfs/dev/console c 5 1
  3. Change the working directory to /usr/src/initramfs/bin.
  4. run for f in $(./busybox --list); do ln -s busybox $f; done, to create the symlinks of the individual tools to the busybox multicall binary.

Add the following code to /usr/src/initramfs/init and make sure it is executable:

#!/bin/sh

die() {
	unset i
	echo "Something went wrong. Dropping to a shell."
	exec sh
}

# Mount the /proc, /sys and /dev filesystems.
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev

# Be a little bit more quiet.
echo 0 > /proc/sys/kernel/printk

wait # For dev.

# Decrypt root partition and possible secondary partition.
i=0
while true; do
	# Open encrypted partition, and place at /dev/mapper/root.
	read -s -p "Password: " password
	echo $password | cryptsetup open --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue /path/to/rootdevnode root && break
	i=$((i+1))
	[ $i -gt 4 ] && break && die
done
mount -o ro /dev/mapper/root /mnt/root

echo $password | cryptsetup open --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue /path/to/devnode2 @NAME2@ && break
echo $password | cryptsetup open --allow-discards --perf-no_read_workqueue --perf-no_write_workqueue /path/to/devnode3 @NAME3@ && break
# ... As many as desired

unset password
unset i

mount -o ro /dev/mapper/@NAME2@ /path/to/mountpoint2
mount -o ro /dev/mapper/@NAME3@ /path/to/mountpoint3
# ... As many as desired

# It's okay to speak again.
echo 1 > /proc/sys/kernel/printk

# Clean up
umount /proc
umount /sys
umount /dev

# Switch to real root.
exec switch_root /mnt/root /sbin/init || die
    		

Here, /path/to/devnode{2,3,...} are the device nodes of an additional storage devices encrypted with the same password.

Finally, recompile the kernel to have it contain the initramfs.

Minimal initramfs with a dropbear SSH server

Compile and statically link the following software:

To prepare the directory tree:

  1. Copy the binaries busybox, dropbear, dbclient and dropbearkey to the directory /usr/src/initramfs/bin.
  2. Change the working directory to /usr/src/initramfs/bin.
  3. run for f in $(./busybox --list); do ln -s busybox $f; done, to create the symlinks of the individual tools to the busybox multicall binary.
  4. Create the file /etc/dropbear/dropbear_ed25519_host_key by running dropbearkey -t ecdsa -f /etc/dropbear/dropbear_ed25519_host_key.
  5. Make sure /etc/passwd exists and contains at least:
    root:x:0:0:root:/home/root:/bin/sh
                		
  6. Create /etc/group and have it contain at least:
    root:x:0:root
                		
  7. Create /home/root/.ssh/authorized_keys and add the auth key of the form:
    ssh-ed25519 AAAAB3NzaC1yc2EAAAABIwAAAIEAwVa6M6cGVmUcLl2cFzkxEoJd06Ub4bVDsYrWvXhvUV+ZAM9uGuewZBDoAqNKJxoIn0Hyd0NkyU99UVv6NWV/5YSHtnf35LKds56j7cuzoQpFIdjNwdxAN0PCET/MG8qyskG/2IE2DPNIaJ3Wy+Ws4IZEgdJgPlTYUBWWtCWOGc= USER@HOST
                		

Add the following code to /usr/src/initramfs/init and make sure it is executable:

#!/bin/sh

die() {
	unset i
	unset pwd
	echo "Something went wrong. Dropping to a shell."
	exec sh
}

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
mkdir -p /dev/pts
mount -t devpts none /dev/pts

# Be a little bit more quiet.
echo 0 > /proc/sys/kernel/printk

ifconfig @IFACE@ up
ifconfig @IFACE@ @IP@ netmask @NETMASK@ broadcast @BRD@
route add default gw @GATEWAY@

wait

/bin/dropbear -s | die