Monday, May 21, 2018

Setting up an encrypted /home partition with LVM and LUKS

My shiny new Slimbook Katana II laptop recently arrived. And I am very pleased with its looks. I got the black model.
Mine is fitted with an Intel i7 8th gen processor, 16 GB of RAM, a 120 GB SSD and a secondary HD of 1 TB.
Lots of room to play around with VM's, LXD containers and Docker containers. I also particularly like that it comes with a penguin on the SUPER key, instead of a Windows logo. It also has a badge that says: "Powered by GNU/Linux".

This being a laptop and us living in this day and age, I decided I wanted two things. I wanted the OS to have one logical pool of storage to draw from and I wanted the /home partition to be encrypted. The first requirement can be met with LVM, the second with LUKS. But I had never used the two combined.

Setting up LVM at install time for Ubuntu is quite straight forward. It is one of the options in the setup wizard. Unfortunately the wizard does not provide any obvious way to customize the LVM setup in
a similar way that the Anaconda installer does on Red Hat based distros. Ubiquity only lets you choose which hard disk you want to install the LVM on. I chose the SSD.

At the end of the installation the LVM situation looks as follows:
root@ubuntu:~# pvs
  PV         VG        Fmt  Attr PSize    PFree 
  /dev/sdb1  ubuntu-vg lvm2 a--  <120,00g 44,00m
root@ubuntu:~# lvs
  LV     VG        Attr       LSize   ...
  root   ubuntu-vg -wi-ao---- 119,00g                                                    
  swap_1 ubuntu-vg -wi-ao---- 976,00m
root@ubuntu:~#
The installer allocated one physical volume, /dev/sdb1, to LVM, and assigned it to the ubuntu-vg volume group. Within that volume group it created two logical volumes, named root and swap_1. The root file system / is mounted under root and swap space is mounted under swap_1.

But what about my 1TB disk? Well, I will need to make LVM aware of it myself. Here I will go into some more detail as to how I want the final system to be configured. I want to dedicate  500 GB of disk space to the var logical volume as that is where all the data for the virtual machines and containers will reside. I also thought I would start out with 100GB of space for my home logical volume. Notice that this would not use all of the 1 TB of space I have available. I am using LVM, so I figure I can always resize the logical volume if I need more space. (That should be possible to do, even if the file system on the home logical volume will be encrypted. But I will cross that bridge when I reach it. If it gives me trouble, that will be the subject of another blog post.) I will mostly just jot down the commands that I used without explaining them in depth. Your preferred search engine and the man pages for the fine grained usage details of each command.

Let's get to it then. My SSD was assigned to /dev/sda. I had assigned some space at the beginning for a recovery partition of sorts.
I still need to decide how I want that to work exactly. But the rest of the 1 TB of space I want to add to the ubuntu-vg volume group. So first I booted the 18.04 installation media and elected to enter the live session. There I fired up gparted (because I like me a graphical user interface if I can use one) and created a partition filling the rest of the disk and assigned it the file system lvm2 pv.

Now the physical volume situation looks as follows:
root@ubuntu:~# pvs
  PV         VG        Fmt  Attr PSize    PFree 
  /dev/sda2            lvm2 a--  <916,61g <315,61g
  /dev/sdb1  ubuntu-vg lvm2 a--  <120,00g 44,00m
root@ubuntu:~#
Notice that, while LVM is aware of the new physical volume, it has not been assigned to any volume group (VG) as yet. Let's remedy that:
root@ubuntu:~# vgextend ubuntu-vg /dev/sda2
  Volume group "ubuntu-vg" successfully extended
root@ubuntu:~# pvs
  PV         VG        Fmt  Attr PSize    PFree 
  /dev/sda2  ubuntu-vg lvm2 a--  <916,61g <315,61g
  /dev/sdb1  ubuntu-vg lvm2 a--  <120,00g 44,00m
root@ubuntu:~#
That's better. Now we create our two logical volumes. First we'll tackle var since that just needs to be created and formatted:
root@ubuntu:~# lvcreate -L 500G -n var ubuntu-vg
  Logical volume "var" created
root@ubuntu:~# mkfs -t ext4 /dev/ubuntu-vg/var
  # Output of this command omitted for brevity
root@ubuntu:~#
The making of the home volume group is just slightly more involved, since I want to have encrypted additionally:
root@ubuntu:~# lvcreate -L 100G -n home ubuntu-vg
  Logical volume "home" created
root@ubuntu:~#
Now we will encrypt the volume. First we scramble the contents:
root@ubuntu:~# shred --verbose --random-source=/dev/urandom \
 --iterations=3 /dev/ubuntu-vg/home
   ...
By performing this operation we fill the whole partition with random bytes, making it impossible for a snooper to see what parts of the volume contain data and what parts dont. But be patient. This might take a while to complete. Next we setup the encryption:
root@ubuntu:~# cryptsetup --verbose --cipher aes-xts-plain64 \
 --key-size 512 --hash sha512 --iter-time 5000 \
 --use-random luksFormat /dev/ubuntu-vg/home
WARNING!
========
This will overwrite data on /dev/ubunut-vg/home irrevocably.
 
Are you sure? (Type uppercase yes): YES
Enter LUKS passphrase: 
Verify passphrase: 
Command successful.
root@ubuntu:~# 
Be sure to remember the passphrase you entered here. Write it down on a slip of paper and store that in a safe. Or use a password manager. But do not loose it!! You will be in a world of pain if you do.

Now that the encryption is in place, we have to unlock the volume to be able to format it and copy over the contents of /home to it.
root@ubuntu:~# cryptsetup open --type luks \
 /dev/ubuntu-vg/home encrypted_home
Enter passphrase for /dev/ubuntu-vg/home:
root@ubuntu:~# mkfs -t ext4 /dev/mapper/encrypted_home
...
Right... Almost there. Now we make temporary mount points for the root, var and home volume groups to populate the latter two with the contents from the former. Remember that we are doing all of this from a live session:
root@ubuntu:~# mkdir /mnt/{root,var,home}
root@ubuntu:~# mount /dev/ubuntu-vg/root /mnt/root
root@ubuntu:~# mount /dev/ubuntu-vg/var  /mnt/var
root@ubuntu:~# mount /dev/mapper/encrypted_home /mnt/home
root@ubuntu:~# rsync -avz /mnt/root/var/ /mnt/var
...
root@ubuntu:~# rsync -avz /mnt/root/home/ /mnt/home
...
root@ubuntu:~# umount /mnt/{root,var,home}
root@ubuntu:~# cryptsetup close encrypted_home
Notice that I did not clean up the old home and var directories under /mnt/root. As this was a clean install they don't take up so much space. And leaving them put guarantees I have a bootable system if for whatever reason the logical volumes cannot be mounted.

What have we achieved thus far?
  • We've extended the volume group to encompass both disks.
  • We've added var and home logical volumes to house our /var and /home partitions. 
  • We've setup LUKS encryption for the home logical volume.
  • We've copied the contents from the old /home and /var directories to the designated logical volumes.
Looks like we're about ready for a reboot, right? Wrong. We still need to have our logical volumes mounted at boot time. We also need to let the kernel know that one of the volumes is encrypted, so that we will be prompted for the password at boot time to unlock it.

First we will notify the kernel of the encrypted volume. We do this by editing /etc/crypttab and adding the following encry:
root@ubuntu:~# nano /etc/crypttab
encrypted_home  /dev/mapper/ubuntu--vg-home none luks,discard
And finally we edit /etc/fstab and add our new mount points:
root@ubuntu:~# nano /etc/fstab
/dev/mapper/ubuntu--vg-var  /var    ext4  errors=remount-ro  0  1
/dev/mapper/encrypted_home  /home   ext4  errors=remount-ro  0  1
The complete file looks like this on my system:
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name
# devices that works even if disks are added and removed. See fstab(5).
#
#                
/dev/mapper/ubuntu--vg-root /       ext4  errors=remount-ro  0  1
/dev/mapper/ubuntu--vg-swap_1 none  swap  sw                 0  0

/dev/mapper/ubuntu--vg-var  /var    ext4  errors=remount-ro  0  1
/dev/mapper/encrypted_home  /home   ext4  errors=remount-ro  0  1
And we're done! Reboot your machine and see if it all works as it should. Mine didn't but that was because I misspelt the name of the /etc/crypttab file. After I corrected the typo all went as it should.