Building a Secure Portable Ubuntu Environment (Part 2)

May 7, 2023 - Reading time: 9 minutes

"If you encrypt your hard drive, you don't have to worry about data breaches" - John McAfee

In Part 1 we discussed the importance of choosing the right hardware for this project. We also noted the issues that can come up with mixing high-performance drives with moderate-performance CPUs. With all of that out of the way, we move on to the installation process.

To start you will need a bootable CD, DVD, or USB stick (other than the USB drive you’re going to install to) containing your desired Ubuntu image. Booting it up you will need to access the live system so we can perform all of the partitioning and disk preparation manually. This is accessed by clicking “Try Ubuntu”

Initial Drive Setup

We first need to find the device file corresponding to our target USB drive that we want to install Ubuntu onto. There are a few different ways to do this, but the easiest might be to just launch the gparted application and find the correct device file. Once we have the correct device file, let’s launch a terminal window and get started.

The first command will be to create a variable with the name of our target USB drive, so we can reference it later. Be sure to replace “/dev/nvme0n1” with whatever device file corresponds to your USB drive:

export DEV="/dev/nvme0n1"

Then we’ll perform the basic partitioning of the target USB drive:

sudo sgdisk --zap-all $DEV
sudo sgdisk --new=1:0:+768M $DEV
sudo sgdisk --new=2:0:+2M $DEV
sudo sgdisk --new=3:0:+128M $DEV
sudo sgdisk --new=5:0:0 $DEV
sudo sgdisk --typecode=1:8301 --typecode=2:ef02 --typecode=3:ef00 --typecode=5:8e00 $DEV
sudo sgdisk --change-name=1:/boot --change-name=2:GRUB --change-name=3:EFI-SP --change-name=5:LVM $DEV
sudo sgdisk --hybrid 1:2:3 $DEV

What are we doing here? First we are clearing any previous partition table. Then we're adding three primary and one logical partition. The primary partitions consist of a partition for the kernel, another for GRUB, and another for EFI-SP. The logical partition is an LVM partition which will make up most of our installation's filesystem.

Our next step is to apply encryption to the primary /boot partition where the kernel will be stored, and to our logical LVM partition. You will need to set a passphrase, and I recommend using the same passphrase for each partition.

sudo cryptsetup luksFormat --type=luks1 ${DEV}1
sudo cryptsetup luksFormat --type=luks1 ${DEV}5

Now lets open those encrypted partitions using the passphrase you just set:

sudo cryptsetup open ${DEV}1 LUKS_BOOT
sudo cryptsetup open ${DEV}5 LUKS_LVM

Our next step is to setup our logical volumes. These volumes may look a little strange, but are aligned with the CIS Benchmark for Ubuntu Linux:

sudo pvcreate /dev/mapper/LUKS_LVM
sudo vgcreate root /dev/mapper/LUKS_LVM
sudo lvcreate -L 4G -n swap root
sudo lvcreate -L 30G -n root root
sudo lvcreate -L 5G -n var_log root
sudo lvcreate -L 5G -n var_log_audit root
sudo lvcreate -L 512M -n var_tmp root
sudo lvcreate -l 80%FREE -n home root

And let's format our /boot and EFI-SP partitions:

sudo mkfs.ext4 -L boot /dev/mapper/LUKS_BOOT
sudo mkfs.vfat -F 16 -n EFI-SP ${DEV}p3

Ubuntu Installation

Now we are ready to launch the Ubuntu Installer. When you arrive at "Installation type", you will want to select "Something else", and then you will need to set mount points for each partition and logical volume you created earlier.

IMPORTANT: As soon as the installation actually starts, you need to run the following command in the terminal:

sudo echo "GRUB_ENABLE_CRYPTODISK=y" >> /target/etc/default/grub

This command needs to run successfully before the installation gets around to setting up GRUB, which happens toward the end. If you run this command and get an error that the file doesn't exist, wait 10 seconds and try again.

IMPORTANT: Do not reboot after the installation completes.

Post-Installation Setup

Following the completion of the Ubuntu installer, but before you reboot into the new installation for the first time, you will need to perform some steps to ensure encryption works properly when the system boots up:

sudo mount /dev/mapper/root-root /target
sudo for n in proc sys dev etc/resolv.conf; do mount --rbind /$n /target/$n; done
sudo chroot /target
sudo mount -a
sudo echo "KEYFILE_PATTERN=/etc/luks/*.keyfile" >> /etc/cryptsetup-initramfs/conf-hook
sudo echo "UMASK=0077" >> /etc/initramfs-tools/initramfs.conf
sudo mkdir /etc/luks
sudo dd if=/dev/urandom of=/etc/luks/boot_os.keyfile bs=4096 count=1
sudo chmod u=rx,go-rwx /etc/luks
sudo chmod u=r,go-rwx /etc/luks/boot_os.keyfile
sudo cryptsetup luksAddKey ${DEV}1 /etc/luks/boot_os.keyfile
sudo cryptsetup luksAddKey ${DEV}5 /etc/luks/boot_os.keyfile
sudo echo "LUKS_BOOT UUID=$(blkid -s UUID -o value ${DEV}1) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
sudo echo "LUKS_LVM UUID=$(blkid -s UUID -o value ${DEV}5) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
sudo update-initramfs -u -k all

You can now reboot the system.

Conclusion

At this point, you will have a usable encrypted live Ubuntu installation. In the next post we will discuss some further hardening to improve security inside the system.