How to resize LVM over LUKS over LVM
Introduction
Disclaimer
If you follow the material in this how-to you can break your system. Back up your data, take it slowly, maybe do a practice run first on a system you don’t care about, and don’t come crying to me if you lose everything.
However, I’ll happily update the material here if you can demonstrate that it’s outdated or mistaken. So there’s that.
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 either standard partitions or LVM 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.
These are just the two simplest examples; LVM over LUKS over LVM is a popular choice and will be the focus of this tutorial. The only thing stopping you from making your system arbitrarily baroque with any number of layers of LUKS and LVM in any desired order is your own good taste.
Scenario
You’ve set up a LUKS partition sandwiched between two LVM layers. The bottom layer helps you treat several partitions scattered across multiple hard-drives as though they were 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, the system root directory, /var, etc.
However, not all the partitions on the underlying hard drives were added into the original LVM, and now you decide that you want your Linux system to include the space from these partitions after all. How can you extend the LVM-LUKS-LVM complex to include those other partitions?
Essentially, you need to take the same sort of steps as if you want to extend the the upper floor of a house: first you need to clear the ground and add supporting structures at ground level, then work your way up.
This how-to will walk you through the following steps:
- Prepare the partitions you want brought into the LVM/LUKS/LVM schema.
- Extend the lower logical volume over the partitions.
- Extend the LUKS partition over the now expanded lower logical volume.
- Extend the upper logical volumes over the expanded LUKS partition.
- Resize the filesystems on the upper logical volumes.
If you’re doing this for your main system, you’ll need some variety of LiveCD, since you can’t resize LUKS on a system that’s already booted. The LiveCD should offer a well-equipped command line; tend to be quite good if you’re not sure.
To make the scenario as specific as possible, 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.
Graphically:
Physical partitions
The first partition on /dev/sda, /dev/sda1, is a small unencrypted 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, which in turn contains a logical volume called crypt. The latter contains the LUKS partition.
Keep in mind that crypt contains the LUKS partition, but that doesn’t mean it is a LUKS partition. All logical volumes must have names — we could call any of them “crypt” if we felt like it but this wouldn’t turn them into encrypted devices! Likewise we could call the logical volume containing the LUKS partition “banana” and this wouldn’t affect its suitability for cryptographic functions. The reason we call the logical volume “crypt” is to remind ourselves of its contents.
When we use the cryptsetup command to unlock the LUKS partition, it needs to be registered with a name of its own. To distinguish it from the crypt logical volume, let’s say that we register it as cryptroot.
The cryptroot device is now treated almost the same as a physical volume. (In one of the later sections we’ll see how it is treated a little differently.) It is within this physical volume that our upper LVM lives. As with other physical volumes containing a LVM, cryptroot has a volume group inside, which in turn holds various logical volumes. We called the lower volume group “vg”; now assume the upper volume group was given the name of “cryptvg”.
Getting complicated? Let’s go over it step by step:
- We have two physical devices each with multiple partitions.
- We have a lower volume group called “vg” which aggregates several partitions from the physical drives.
vgcontains one logical volume with the name of “crypt”.cryptcontains a LUKS partition.- When we unlock this LUKS partition, we register it with the name
cryptroot. cryptrootis treated like a physical volume.cryptroothas been set up to contain a volume group with the name “cryptvg”.
Now, this volume group cryptvg in turn contains the logical volumes for our actual system. Assume the logical volumes in cryptvg are root, home, and swap.
The first two of these have ext4 filesystems on them. 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:
LVM layout
Finally, /dev/sda3 and /dev/sdb2 are partitions without any important data on them. Maybe they have a pre-existing filesystem, maybe they don’t. Whatever’s on them will soon be destroyed.
The goal is that when we’re finished, the new partition layout will look like this:
Physical paritions part 2
Step 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 on them. Once we’re done not even a forensics team will be able to recover information from them. When you’re satisfied with the idea of completely wiping them, run the following code:
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 only suggests one write from /dev/zero.
That’s kind of silly though because anyone investigating the hard drive will see how much encrypted information you’ve got just be looking at which parts of your drive still contain zeros.
To conceal this information you need to ensure that at least your final erasure is from /dev/urandom. Furthermore, hard drives kind of remember their past states, so writing over them more than once is recommended to really scramble their memory. In other words, the above commands are the minimum you should consider necessary. On the other hand, government regulations for wiping their own hard drives only require three passes, so if you’re being much more paranoid than that then you’re probably overdoing it.
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 with gpg and store it somewhere safe.
Assuming you have an encryption key with id 0x12345678 and have access to an offsite ssh server:
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
Step 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
Step 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. When I asserted above that cryptroot is treated almost like a physical volume, this is what I meant. We haven’t yet had any effect on the upper LVM.
Step 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:
- Resize the physical volume corresponding to the LUKS partition;
- Extend the
cryptvgvolume group to fill the new space on thecryptrootphysical volume; - Resize
hometo fill thecryptvgvolume 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 vgextend now seems to yield 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
Step 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.
