dm-crypt (or Device Mapper crypt) is the Linux kernel's infrastructure for creating encrypted block devices. It works between the physical (or virtual disk) partition and the filesystem, transparently encrypting data as it's persisted to storage and decrypting it on recollection.

There are two configuration models:

  • LUKS (strongly recommended) allows keys to be placed in up to 16 slots and provides convenient CLI tools for their management. This is what we deploy.
  • Plain mode simply encrypts and decrypts data using the specified passphrase, without support for management of multiple keys.

The cryptsetup command is used to manage encrypted volumes. Note that for all of the below commands that utilise key slots, the key slot will default to the first key (numbered from zero) where not specified.

Loss of the header means loss of the data

You must take care to not damage or, in the case of detached LUKS headers where the header is stored in a separate file outside of the partition, lose the header. Losing the header renders the contents of an encrypted volume unrecoverable.

Opening an encrypted container

To access the data within an encrypted container we need to open it:

$ sudo cryptsetup luksOpen test-disk.img my-test
Enter passphrase for test-disk.img:

This will expose the device in /dev, which we can then mount as usual:

$ sudo mkdir /mnt/test
$ sudo mount /dev/mapper/my-test /mnt/test
$ sudo ls /mnt/test

Closing an encrypted container

First ensure that any mounted volumes on it are unmounted. You can identify these with lsblk to list the devices and mount to list the active volumes:

$ lsblk
loop0                     7:0    0  89.1M  1 loop  /snap/core/8039
loop1                     7:1    0  89.1M  1 loop  /snap/core/8268
loop2                     7:2    0  56.4M  1 loop  /snap/powershell/39
loop3                     7:3    0  56.4M  1 loop  /snap/powershell/77
loop4                     7:4    0   100M  0 loop
└─my-test               253:3    0    84M  0 crypt /mnt/test
nvme0n1                 259:0    0 931.5G  0 disk
├─nvme0n1p1             259:1    0   512M  0 part  /boot/efi
├─nvme0n1p2             259:2    0   732M  0 part  /boot
└─nvme0n1p3             259:3    0 930.3G  0 part
  └─nvme0n1p3_crypt     253:0    0 930.3G  0 crypt
    ├─ubuntu--vg-root   253:1    0 929.3G  0 lvm   /
    └─ubuntu--vg-swap_1 253:2    0   976M  0 lvm   [SWAP]
$ mount | grep my-test
/dev/mapper/my-test on /mnt/test type ext4 (rw,relatime)
$ umount /dev/mapper/my-test
$ rm -rf /mnt/test

Then close it:

sudo cryptsetup luksClose my-test

Listing enabled encrypted volumes

You can find a complete list of device mapper targets known to the system using dmsetup:

$ sudo dmsetup ls --target crypt
my-encrypted-data-disk    (253, 2)
my-encrypted-os-disk      (253, 1)

Note that the device names shown here will be the names of the decrypted devices -- the ones within the encrypted container. Most cryptsetup commands operate on the container itself, so we'll need to know the names of the backing devices. The easiest way to visualise this is lsblk -- just look at the line above the volume you're interested in:

$ sudo lsblk
NAME                                       MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINT
sda                                          8:0    0   30G  0 disk
├─sda1                                       8:1    0 29.7G  0 part
│ └─my-encrypted-os-disk                    253:1    0 29.7G  0 crypt /
├─sda2                                       8:2    0  256M  0 part  /boot
├─sda14                                      8:14   0    4M  0 part
└─sda15                                      8:15   0  106M  0 part  /boot/efi
sdb                                          8:16   0   40G  0 disk
└─sdb1                                       8:17   0   40G  0 part  /mnt
sdc                                          8:32   0   48M  0 disk
└─sdc1                                       8:49   0   30G  0 part
  └─my-lvm-lv                              253:0    0   60G  0 lvm
    └─my-encrypted-data-disk               253:2    0   60G  0 crypt /mnt/data
sde                                          8:64   0   30G  0 disk
└─sde1                                       8:65   0   30G  0 part
  └─my-lvm-lv                              253:0    0   60G  0 lvm
    └─my-encrypted-data-disk               253:2    0   60G  0 crypt /mnt/data

From the above we can deduce that our dm-crypt backing devices are as follows:

  • /dev/sda1 for my-encrypted-os-disk
  • /dev/my-lvm-lv for my-encrypted-os-disk

You can drill down into the properties of an individual volume with cryptsetup:

Decrypting filesystems at boot

Filesystems to be decrypted during boot will be listed in /etc/crypttab:

# <target name> <source device>         <key file>      <options>
my-encrypted-data-disk /dev/mapper/storage-data /mnt/azure_bek_disk/LinuxPassPhraseFileName_1_1 luks,nofail

The target names define the name of the mapped volume in /dev, which can then be added to /etc/fstab or similar for automatic mounting.

Using an encrypted root filesystem

Decrypting the root (/root) filesystem requires some steps in addition to ordinary volumes. Because the root filesystem is not yet available, all tools required in the decryption chain must be available in the initrd, achieved in Ubuntu by installing cryptsetup-initramfs.

Formatting a new container

Don't run these commands on a system you don't have complete backups for -- there's a pretty good chance you'll lose data.

sudo cryptsetup luksFormat test-disk.img

This will overwrite data on test-disk.img irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase for test-disk.img:
Verify passphrase:

Backing up the LUKS header

When making changes (e.g. changing keys) it's advisable to take a backup of the volume's LUKS header data. If this becomes corrupted and no backup is available any data on the disk will not be recoverable.

sudo cryptsetup luksHeaderBackup --header-backup-file test-disk-header test-disk.img

Restoring the LUKS header

Use the same arguments as with luksHeaderBackup:

$ sudo cryptsetup luksHeaderRestore --header-backup-file test-disk-header test-disk.img

Device test-disk.img already contains LUKS2 header. Replacing header will destroy existing keyslots.

Are you sure? (Type uppercase yes): YES

Dumping configuration

To print the information contained in the header of the disk along with the status of the key slots for a given volume:

$ sudo cryptsetup luksDump test-disk.img
LUKS header information
Version:        2
Epoch:          3
Metadata area:  16384 [bytes]
Keyslots area:  16744448 [bytes]
UUID:           00000000-0000-0000-0000-000000000000
Label:          (no label)
Subsystem:      (no subsystem)
Flags:          (no flags)

Data segments:
  0: crypt
    offset: 16777216 [bytes]
    length: (whole device)
    cipher: aes-xts-plain64
    sector: 512 [bytes]

  0: luks2
    Key:        512 bits
    Priority:   normal
    Cipher:     aes-xts-plain64
    Cipher key: 512 bits
    PBKDF:      argon2i
    Time cost:  4
    Memory:     1048576
    Threads:    4
    Salt:       00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    AF stripes: 4000
    AF hash:    sha256
    Area offset:32768 [bytes]
    Area length:258048 [bytes]
    Digest ID:  0
  0: pbkdf2
    Hash:       sha256
    Iterations: 147603
    Salt:       00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    Digest:     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
                00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Adding a new key

In corporate environments it's often necessary for the organisation to be able to decrypt the contents of the disk as well as the user of the machine. LUKS allows us to do this by providing up to 16 key slots for us to fill. To add a new key:

$ sudo cryptsetup luksAddKey --key-slot 1 test-disk.img
Enter any existing passphrase:
Enter new passphrase for key slot:
Verify passphrase:

Changing an existing key

To change a key in one of the slots you need to know any one of the existing keys:

$ sudo cryptsetup luksChangeKey test-disk.img
Enter passphrase to be changed:
Enter new passphrase:
Verify passphrase:

Deleting a key

To delete a key, specify the volume path and the key slot number. Note that you don't use the --key-slot option as the key slot number is required.

$ sudo cryptsetup luksRemoveKey test-disk.img
Enter passphrase to be deleted: