Home

Awesome

Full disk encryption for Kali on Raspberry (or other similar architecture) using LUKS

This HOWTO is dedicated to running Kali on Raspberry (or other similar architecture, e.g. ODROID-C2 using full disk encryption with LUKS. Based on the little bit outdated document here.

Motivation

Deploying Kali on Raspberry Pi (or other similar, small, low-powered hardware) in a LAN environment as a "throw-away-hackbox" is obviously useful. However, keeping the collected data in secret is an essential requirement. Here comes full disk encryption as a mandatory security concept.

Prerequisites

First we should setup an official current Kali image for the Raspberry Pi (without encryption) on an SD card.

The recommended option (now) is using a Raspberry Pi 3 custom build with the nexmon patch (for using the onboard WiFi monitoring and injecting capabilities). There is an automated build script in the official kali-arm-build-scripts repo which sets up the image from scratch (on a Kali distro). And here is a patched version which works on other distros than Kali, too (tested on Gentoo). Currently (2017-08) working build script for ODROID-C2 is also here in that forked repo.

Get the build tools and build the image from scratch as root (it can take couple of hours):

$ git clone https://github.com/tothi/kali-arm-build-scripts
$ cd kali-arm-build-scripts
$ sudo ./rpi3-nexmon.sh 2.0

The resulting image is rpi3-nexmon-2.0/kali-2.0-rpi3-nexmon.img.xz. Copy it on SD card (using pv for displaying progress):

# pixz -d rpi3-nexmon-2.0/kali-2.0-rpi3-nexmon.img.xz - | pv -treb | dd of=/dev/mmcblk0 bs=512k

(The image should work, it can be tested now.)

Preparing the RPi image for encrypted boot

We mount the image from the SD card and prepare an initramfs capable with LUKS features in a chroot environment. For making this work, an x86 (host) qemu-arm-static binary is needed on the SD card filesystem. The above patched rpi3-nexmon.sh build script installs and leaves it on the image, so things should work well. (Otherwise copying the appropriate qemu static binary to the image is needed.)

Initializing the chroot environment (on the host system as root):

# mkdir -p /mnt/chroot/boot
# mount /dev/mmcblk0p2 /mnt/chroot/
# mount /dev/mmcblk0p1 /mnt/chroot/boot/

# mount -t proc none /mnt/chroot/proc
# mount -t sysfs none /mnt/chroot/sys
# mount -o bind /dev /mnt/chroot/dev
# mount -o bind /dev/pts /mnt/chroot/dev/pts

# LANG=C chroot /mnt/chroot/

At first we change the default password for root:

passwd

Install required packages (in the chroot env):

# apt-get update
# apt-get install busybox cryptsetup dropbear

Note, that we have to enter the decryption key on every boot. We do it preferably remotely through SSH, that's why dropbear is needed (in the initramfs).

Now edit /boot/cmdline.txt, change root device to the mapped crypt device (/dev/mapper/crypt_sdcard) and add the appropriate cryptdevice parameter.

So here is an original /boot/cmdline.txt:

dwc_otg.fiq_fix_enable=2 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rootflags=noload net.ifnames=0

And here is the updated one (differences in root and cryptdevice params):

dwc_otg.fiq_fix_enable=2 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mapper/crypt_sdcard cryptdevice=/dev/mmcblk0p2:crypt_sdcard rootfstype=ext4 rootwait rootflags=noload net.ifnames=0

Now create /boot/config.txt with the following content:

initramfs initramfs.gz followkernel

Note that on other hardware a similar steps are needed (the file containing the boot params on ODROID-C2 is /boot/boot.ini).

Let's set up Dropbear SSH key authentication. Create a (passwordless) keypair on the host machine (outside chroot!) and read the public key:

$ ssh-keygen -N "" -f kali-dropbear
$ cat ./kali-dropbear.pub

Add the public key to /etc/dropbear-initramfs/authorized_keys. Restrict Dropbear SSH access for setting up cryptroot only by prepending the key with this:

command="/scripts/local-top/cryptroot && kill -9 `ps | grep -m 1 'cryptroot' | cut -d ' ' -f 3`"

Fix permissions:

chmod 600 /etc/dropbear-initramfs/authorized_keys

Edit /etc/fstab. Original:

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults          0       0
/dev/mmcblk0p1  /boot           vfat    defaults          0       2
/dev/mmcblk0p2  /               ext4    defaults,noatime  0       1

Updated / device changed to /dev/mapper/crypt_sdcard:

# <file system>           <mount point>   <type>  <options>       <dump>  <pass>
proc                      /proc           proc    defaults          0       0
/dev/mmcblk0p1            /boot           vfat    defaults          0       2
/dev/mapper/crypt_sdcard  /               ext4    defaults,noatime  0       1

Add this line to /etc/crypttab:

crypt_sdcard /dev/mmcblk0p2 none luks

Modify /etc/cryptsetup-initramfs/conf-hook by setting

CRYPTSETUP=y

to include cryptsetup files in initramfs image.

Finally, make initramfs for the current kernel version and leave chroot (don't bother with errors/warnings during mkinitramfs):

# ls -l /lib/modules/ |awk -F" " '{print $9}'

4.4.50-v7+
# mkinitramfs -o /boot/initramfs.gz 4.4.50-v7+

Note, that we have to be careful when generating the image (especially on other hardware), because uname -r returns the host kernel version, not the chroot one. So e.g. on ODROID-C2 /boot/mkuinitrd does not work out of the box, we should issue the commands in the script manually (adjusting the kernel version FOR OTHER HARDWARE!):

# rm /boot/initrd.img-3.14.79
# update-initramfs -c -k 3.14.79
# mkimage -A arm64 -O linux -T ramdisk -C none -a 0 -e 0 -n "uInitrd" -d /boot/initrd.img-3.14.79 /boot/uInitrd
#

Exit chroot environment

exit

Unmount filesystems and backup rootfs before creating encrypted volume (and deleting everything) on SD card:

# umount /mnt/chroot/boot
# umount /mnt/chroot/sys
# umount /mnt/chroot/proc
# mkdir -p /mnt/backup
# rsync -avh /mnt/chroot/* /mnt/backup/

Unmount the full chroot:

# umount /mnt/chroot/dev/pts
# umount /mnt/chroot/dev
# umount /mnt/chroot

Delete unencrypted root partition (num. 2) and create an empty one (fulfilling the SD card):

# echo -e "d\n2\nw" | fdisk /dev/mmcblk0
# echo -e "n\np\n2\n\n\nw" | fdisk /dev/mmcblk0

Now (probably) unplugging and inserting the SD card is needed in order to register the new partitions.

Create encrypted volume with a strong passphrase (this step destroys the original rootfs!):

# cryptsetup -v -y --cipher aes-cbc-essiv:sha256 --key-size 256 luksFormat /dev/mmcblk0p2
# cryptsetup -v luksOpen /dev/mmcblk0p2 crypt_sdcard
# mkfs.ext4 /dev/mapper/crypt_sdcard

Restore rootfs to the encrypted volume and close the disk:

# mkdir -p /mnt/encrypted
# mount /dev/mapper/crypt_sdcard /mnt/encrypted/
# rsync -avh /mnt/backup/* /mnt/encrypted/
# umount /mnt/encrypted/
# cryptsetup luksClose /dev/mapper/crypt_sdcard
# sync

Eject SD card and test it on the Raspberry.

Boot the device

The first boot process will fail und fallback into busybox. Enter the following commands:

# cryptsetup luksOpen /dev/mmcblk0p2 crypt_sdcard
## provide password
# exit

Your device should boot now. Login with the password you've chosen earlier and run:

mkinitramfs -o /boot/initramfs.gz
reboot

You will be asked for passphrase now with a nicer prompt, furthermore you should be able to ssh to the busybox and enter the passphrase (you may use a custom known_hosts file if you wish):

$ ssh -o "UserKnownHostsFile=~/.ssh/known_hosts.initramfs" -i ~/.ssh/kali-dropbear root@192.168.88.133

Once it is working, do not forget to clean up backup files on the host:

# rm -fr /mnt/backup
# rm -fr /mnt/chroot

Trouble shooting

To get full debug output during boot you can add debug=1 in cmdline.txt