The goal of this tutorial is to show you how to create an encrypted LVM on Linux. This will help you keep your data safe in the event of, for example, your laptop computer being stolen.

Why an encrypted LVM ?

Why an encrypted LVM ? Well, we could have stopped at an encrypted partition but, once we get there, it’s not much harder to reap the benefits (in term of fexibility) of the added LVM layer.

So basically :

  • Encryption for security : making sure the data will be accessible only by you
  • LVM for flexibility : being able to allocate only portions of the encrypted volume at a time, growing them when needed, and so on.

We’ll follow a step by step procedure to get you through it, hopefully painlessly. In the end, you’ll have :

  • a GPG password protected keyfile
  • which will be required to access the encrypted disk partition
  • on top of which will sit one or more LVM volumes

Setting up the encryption

We are going to use the cryptsetup tool to setup the encryption.

Generate a GPG password protected key

You could stick with a regular password keyfile, but it would mean that this file would be the only requirement to accessing your protected data. Adding GPG into the mix makes sure that an additional password (hopefully one only you know) will be required.

1
2
3
4
kattoo@roadrunner ~ % dd if=/dev/urandom count=1 | gpg --symmetric -a > key.gpg
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000102417 s, 5.0 MB/s

You’ll then be prompted to enter a password. Choose it wisely because the safety of your data depends on it.

Keep that file preciously. If you lose it, there won’t be any way to access your data anymore.

Don’t let anyone access it.

Nuking previous content of the disk / partition

It is generally advised to randomize the disk data before setting up encryption. This serves a double purpose :

  1. it ensures that no previous data will be available (better safe than sorry)
  2. once the encryption is setup, it will make the actual data less easy to spot in the middle of the random noise (encrypted data will look like random bits in the middle of random bits)

There are many ways to randomize the harddisk content : we’ll create a temporary key, encrypt the volume with it, then fill it with encrypted zeroes.

I’ll just show the procedure below, if it doesn’t quite make sense right now just keep reading, I’ll detail the steps when we’ll do the permanent encryption.

1
root@roadrunner ~ # cd /tmp

Creating a temporary random key :

1
2
3
4
root@roadrunner tmp # dd if=/dev/urandom count=1 of=tmp_key
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000112387 s, 4.6 MB/s

Using it to create / open the encrypted LUKS device from the partition. We’ll use the /dev/sdb1 device as an example. Pick yours carefully because all data stored on it will be nuked.

1
2
root@roadrunner tmp # cat /tmp/tmp_key | cryptsetup luksFormat /dev/sdb1
root@roadrunner tmp # cat /tmp/tmp_key | cryptsetup luksOpen /dev/sdb1 tmpVault

Filling that encrypted partition with encrypted zeroes :

1
2
3
4
5
root@roadrunner tmp # dd if=/dev/zero bs=1M of=/dev/mapper/tmpVault 
dd: writing `/dev/mapper/tmpVault': No space left on device
246+0 records in
245+0 records out
257393664 bytes (257 MB) copied, 22.5508 s, 11.4 MB/s

Closing the encrypted zero filled device :

1
root@roadrunner tmp # cryptsetup luksClose tmpVault

And finally, deleting the temporary key :

1
root@roadrunner tmp # rm tmp_key

The device is now left filled with random data.

Generate and encrypted LUKS device

We’ll use cryptsetup with LUKS extensions to deal with the encryption part. It’s pretty easy to use with the default options, but make sure to read the cryptsetup manpage for the fancy options and extra security.

From there on, you’ll need to be root.

We’ll use the /dev/sdb1 device as an example. Pick yours carefully because all data stored on it will be nuked.

1
2
3
4
5
root@roadrunner ~ # gpg -d ~kattoo/key.gpg | cryptsetup luksFormat /dev/sdb1
gpg: CAST5 encrypted data
gpg: encrypted with 1 passphrase
gpg: WARNING: message was not integrity protected
root@roadrunner ~ #

You’ll be prompted for the key password. This will then be fed to cryptsetup which will use it to create the encrypted partition.

Open the encrypted LUKS device

To make the encrypted LUKS device available to the system, you need to open it :

1
2
3
4
5
root@roadrunner ~ # gpg -d ~kattoo/key.gpg | cryptsetup luksOpen /dev/sdb1 vault
gpg: CAST5 encrypted data
gpg: encrypted with 1 passphrase
gpg: WARNING: message was not integrity protected
root@roadrunner ~ #

vault is a symbolic name, you can pick up what you like and it will help you identify the devices if you have more than one of them.

The encrypted block device will now show up into /dev/mapper :

1
2
3
4
5
6
7
root@roadrunner ~ # ls -l /dev/mapper/
total 0
crw------- 1 root root 10, 236 Apr 17 05:31 control
[...]
lrwxrwxrwx 1 root root       7 Apr 17 17:59 vault -> ../dm-5
[...]
root@roadrunner ~ #

This device is suitable for being used in LVM.

Setting up the volumes

It is really beyond this tutorial to give a full explanation of the Linux LVM, so if you are not familiar with the concepts, make sure you pick up one the the many tutorials you’ll find on the internet.

For this tutorial, we’ll create 2 volumes on that encrypted disk.

Let’s create the physical volume encapsulating the encrypted device :

1
2
3
4
root@roadrunner tmp # pvcreate /dev/mapper/vault
  Writing physical volume data to disk "/dev/mapper/vault"
  Physical volume "/dev/mapper/vault" successfully created
root@roadrunner tmp #

Let’s create the volume group containing that physical volume :

1
2
3
root@roadrunner tmp # vgcreate vg_vault /dev/mapper/vault 
  Volume group "vg_vault" successfully created
root@roadrunner tmp #

Now let’s create the logical volumes on top of that volume group. We’ll create 2 of them for the example :

1
2
3
4
5
root@roadrunner tmp # lvcreate -n lv_vault_01 -L 80M vg_vault
  Logical volume "lv_vault_01" created
root@roadrunner tmp # lvcreate -n lv_vault_02 -L 80M vg_vault
  Logical volume "lv_vault_02" created
root@roadrunner tmp #

Those 2 volumes will now show up in /dev/mapper as well as in /dev/vg_vault :

1
2
3
4
5
6
7
8
9
10
11
12
13
root@roadrunner tmp # ls -l /dev/mapper/
total 0
crw------- 1 root root 10, 236 Apr 17 05:31 control
[...]
lrwxrwxrwx 1 root root       7 Apr 17 21:40 vault -> ../dm-5
[...]
lrwxrwxrwx 1 root root       7 Apr 17 21:40 vg_vault-lv_vault_01 -> ../dm-6
lrwxrwxrwx 1 root root       7 Apr 17 21:40 vg_vault-lv_vault_02 -> ../dm-7
root@roadrunner tmp # ls -l /dev/vg_vault/
total 0
lrwxrwxrwx 1 root root 7 Apr 17 21:40 lv_vault_01 -> ../dm-6
lrwxrwxrwx 1 root root 7 Apr 17 21:40 lv_vault_02 -> ../dm-7
root@roadrunner tmp #

You can now proceed to create the filesystems on those logical volumes and mount them as usual.

Create the mount points :

1
2
root@roadrunner tmp # mkdir /mnt/vault01
root@roadrunner tmp # mkdir /mnt/vault02

Create the filesystems :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
root@roadrunner tmp # mkfs /dev/vg_vault/lv_vault_01 
mke2fs 1.42 (29-Nov-2011)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
20480 inodes, 81920 blocks
4096 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=67371008
10 block groups
8192 blocks per group, 8192 fragments per group
2048 inodes per group
Superblock backups stored on blocks: 
        8193, 24577, 40961, 57345, 73729
 
Allocating group tables: done                            
Writing inode tables: done                            
Writing superblocks and filesystem accounting information: done 
 
root@roadrunner tmp # mkfs /dev/vg_vault/lv_vault_02
mke2fs 1.42 (29-Nov-2011)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
20480 inodes, 81920 blocks
4096 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=67371008
10 block groups
8192 blocks per group, 8192 fragments per group
2048 inodes per group
Superblock backups stored on blocks: 
        8193, 24577, 40961, 57345, 73729
 
Allocating group tables: done                            
Writing inode tables: done                            
Writing superblocks and filesystem accounting information: done

Mount the filesystems into their mount points :

1
2
root@roadrunner tmp # mount /dev/vg_vault/lv_vault_01 /mnt/vault01
root@roadrunner tmp # mount /dev/vg_vault/lv_vault_02 /mnt/vault02

Let’s control that everything went fine :

1
2
3
4
root@roadrunner tmp # df -h /mnt/vault0*
Filesystem                        Size  Used Avail Use% Mounted on
/dev/mapper/vg_vault-lv_vault_01   78M  1.6M   72M   3% /mnt/vault01
/dev/mapper/vg_vault-lv_vault_02   78M  1.6M   72M   3% /mnt/vault02

Alright, everything you would put into /mnt/vault01 and /mnt/vault02 would be stored on the encrypted device.

A word of caution

This is nothing but a step in securing your data. For example, if you store your precious data onto such encrypted volume only to open it later with a software which will store temporary files on an un-encrypted /tmp, the whole setup doesn’t make much sense.

Common practice is to encrypt the swap spaces, as well as /tmp. Unfortunately, this gets really distro-specific and you’ll need to dig into the manual of your favorite Linux distribution.

I hope you’ll have fount this tutorial useful. Don’t hesitate to hit the comments if you see any omission or correction !

April 17, 2012 at 10:17 pm by Stephane Kattoor
Category: Systems
Tags: , , ,