How To Resize A LUKS Partition

How To Resize A LUKS Partition

Foreword

I wrote this in 2011. The general structure should still be fairly similar as of 2018, but some small things might have changed, so check the relevant manpages.

I would probably also change some of the advice. Eg I would not worry about filling an existing partition with zeroes from /dev/zero before also filling it with noise from /dev/urandom. That might be a sane precaution when preparing a used hard drive for disposal, but it really is overkill prior to using the disk.

Worse than that, the suggestion that one might wish to use /dev/random instead of /dev/urandom is just a bad idea and unnecessary:

Finally, the tutorial assumes you have a fairly complicated setup. These days I’m more inclined to use LVM-over-LUKS or just plain Ext4-over-LUKS. The LVM-over-LUKS-over-LVM sandwich addressed here has come to seem a little baroque.

Introduction

Disclaimer

This how-to is provided for educational purposes only. If you follow the instructions you could break your system. I am not responsibile for what happens on your system.

Background

Logical Volume Management (LVM) abstracts the notion of hard disk partitioning. It allows you to treat multiple partitions as though they were a single volume, and it allows you to treat a single partition as though it were multiple volumes.

Linux Unified Key Setup (LUKS) can be used to encrypt partitions and logical volumes.

It’s common to combine LVM and LUKS in various ways. If you create the logical volumes first and then put LUKS on them, it’s called LUKS over LVM. Conversely, if you put a logical volume inside a partition that’s already encrypted with LUKS, it’s called LUKS over LVM.

You can go to an arbitrary number of layers. LUKS over LVM over LUKS over LVM over … over and over.

Description of problem

You’ve set up a LUKS partition sandwiched between two LVM layers. The bottom layer helps you treat several partitions scattered across several hard-drives as actually all contiguous with each other. The upper LVM, on the other hand, contains the logical layout of your Linux system, with internally distinct regions for /home, /var, /tmp, etc.

Now you decide that, out of your several hard-drives, you want your encryption schema to cover more partitions than before. How can you extend the LUKS layer to include those other partitions?

Sketch of solution

This how-to will walk you through the following steps:

  1. Prepare the partitions you want brought into the LVM/LUKS/LVM schema.
  2. Extend the lower logical volume over the partitions.
  3. Extend the LUKS partition over the now expanded lower logical volume.
  4. Extend the upper logical volumes over the expanded LUKS partition.
  5. Resize the filesystems on the upper logical volumes.

Materials required

You should have a LiveCD. At a minimum, the LiveCD should be able to give you a command line and it should provide the commands I use. You’ve satisfied these requirements if you use an Archlinux iso from the second half of 2011. It would also help if you have ssh access to some other machine, and the ability to encrypt files using GnuPG.

Specifically, let’s assume you have two hard-drives, which appear in /dev as sda and sdb. The /dev/sda drive has three partitions, sda1, sda2, and sda3. The other drive has two partitions, sdb1 and sdb2.

The partition scheme looks like this:

The first partition on /dev/sda, /dev/sda1, is a small unenrypted partition formatted as ext2. It is the boot partition for the LVM/LUKS/LVM complex, which sits across /dev/sda2 and /dev/sdb1. The lower LVM consists of a volume group called vg, containing a logical volume called crypt. The latter contains the LUKS partition.

Without loss of generality, you open the LUKS partition on crypt with the name cryptroot. Once decrypted, it is treated much like a physical volume, modulo one or two subtleties. This physical volume in turn contains a logical volume called cryptvg, containing the logical volumes root, home, and swap.

The first two of these are ext4. At boot time, your initrd mounts the root logical volume onto / and your home logical volume onto /home.

To summarise, the LVM/LUKS schema looks something like this:

Finally, /dev/sda3 and /dev/sdb2 are normal ext3 partitions. These are partitions we’re about to destroy and subsume into the LVM/LUKS/LVM schema, so that when we’re finished, the partition layout will look like this:

If you’re reading this, you’ve probably used LVMs before. If you haven’t, some of the naming conventions only seem obvious with hindsight. In particular, the system will map crypt to a device called vg-crypt, since crypt sits inside the volume group called vg. Thus below, there may be points where it looks like I’m doing things like using crypt and vg-crypt interchangeably; that’s because of this correspondence between them.

1. Prepare the new partitions.

This step is identical to steps you would have taken when first preparing the LVM/LUKS complex. You need to destroy all data on /dev/sda3 and /dev/sdb2. First ensure they are not mounted anywhere, and make really sure you’ve backed up any data you’re even thinking about preserving from them. Not even a forensics team will be able to recover information after this. Then run:

for d in /dev/sda3 /dev/sdb2; do
    for f in /dev/zero /dev/urandom; do
        echo Writing to $d from $f.
        sudo dd if=$f of=$d bs=1M
    done
done

This can take a while. It might be best to leave it running overnight. Some people would argue that doing two passes per partition is overkill. For example, the cryptsetup FAQ specifies that you should only need one write from /dev/zero. On the other hand, I don’t see any reason not to play it extra safe when luxury permits. You may even wish to increase the number of passes, or write from /dev/random rather than /dev/urandom.

While waiting for the secure erasure to complete, take the opportunity to back up your LUKS header. This should (hopefully) allow you to recover your data in the event that something goes wrong in the subsequent steps. You’ll want to encrypt this and store it somewhere safe. This is where I assume you have an encryption key with id 0x12345678 and can scp the encrypted backup somewhere offsite.

sudo cryptsetup luksHeaderBackup vg-crypt --header-backup-file cryptheader
sudo chown $(whoami) cryptheader
gpg --encrypt --recipient 0x12345678 cryptheader > cryptheader.gpg
scp cryptheader.gpg ssh_server:
shred -fuzn20 cryptheader cryptheader.gpg

2. Extend the lower LVM.

One of the nice things about LVMs is they can be resized on the fly.

for d in /dev/sda3 /dev/sdb2; do
    sudo pvcreate $d
    sudo vgextend vg $d
    sudo lvextend /dev/mapper/vg-crypt $d -l +100%FREE
done

Optimisation note: of course, it would be trivial to merge this for-loop with the one in Step 1.

It has been pointed out (Bellman 2014) that it is clearer to do the above like so:

for d in /dev/sda3 /dev/sdb2; do
    sudo pvcreate $d
    sudo vgextend vg $d
done
sudo lvextend /dev/mapper/vg-crypt -l +100%FREE

3. Extend the LUKS partition.

Although easy to describe, this step is the most complicated to carry out. It’s also the step where you have the greatest chance of destroying your LUKS setup.

Boot into your LiveCD. Get yourself a command line. The live environment doesn’t know about the volume groups on your hard drives yet. Tell it about the lower LVM for starters:

vgchange -a y

Now open the LUKS partition:

cryptsetup luksOpen /dev/mapper/vg-crypt cryptroot

and resize it:

cryptsetup --verbose resize cryptroot

Note that this only expands the LUKS encryption to again cover the crypt logical volume. This is one of the subtleties I mentioned above, when I asserted that cryptroot is treated almost like a physical volume. We haven’t yet had any effect on the upper LVM.

4. Extend the upper LVM.

Back to the easy stuff: resizing LVMs. First tell the system about the volume groups that LUKS had hidden away in vg-crypt.

vgchange -a y

The following steps will then:

  1. resize the physical volume corresponding to the LUKS partition,
  2. extend the cryptvg volume group to fill the new space on the cryptroot physical volume, and
  3. resize home to fill the cryptvg volume group.
pvresize /dev/mapper/cryptroot
vgextend cryptvg /dev/mapper/cryptroot
lvresize /dev/mapper/cryptvg-home -l +100%FREE

Note: I thought the above worked for me in the past, but apparently vgextend now yields an error (Bellman 2014). If you have trouble, try replacing the above with:

pvresize /dev/mapper/cryptroot
lvresize /dev/mapper/cryptvg-home -l +100%FREE

5. Resize the filesystem.

Just because you expanded the cryptvg-home logical volume, doesn’t mean you expanded the filesystem sitting on that volume. To do so, proceed as follows:

e2fsck -f /dev/cryptvg/home
resize2fs /dev/cryptvg/home
e2fsck -f /dev/cryptvg/home

You should now be able to reboot the system, and find that your /home directory now has more room, by an amount previously taken up by /dev/sda3 and /dev/sdb2.

References