paulgorman.org/technical

Linux Disk Encryption

(August 2017)

These notes cover full disk (block device) encryption. The complexity of disk encryption depends on whether whether we wish to encrypt the root filesystem or a non-root data partition (e.g. a removable backup drive). Disk encryption has two parts: dm-crypt and LUKS.

LUKS does key/passphrase management for dm-crypt volumes (dm-crypt is part of the kernel device mapper). LUKS can accept multiple (up to eight?) keys, giving different users (or backup keys) access to the device.

Plain dm-crypt can be used, but LUKS protects against many common mistakes.

The cryptsetup utility manages both plain dm-crypt volumes and LUKS volumes. See CRYPTSETUP(8).

# apt-get install cryptsetup

The cryptsetup FAQ is information and engagingly written. https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions

Creating a New Encrypted Device

Before we repartition the disk, if it formerly contained sensitive data, wipe it. Create a temporary encrypted device or partition:

# cryptsetup open --type plain /dev/sdXY testdev --key-file /dev/random
# fdisk -l
# dd if=/dev/zero of=/dev/mapper/testdev bs=1M status=progress
# cryptsetup close testdev

Or just use shred:

# shred -v --iterations=1 /dev/sdXY

If we don’t want to overwrite the whole disk, minimally do:

# wipefs -a /dev/sdX

Initialize the partition or device:

# cryptsetup --verbose --verify-passphrase luksFormat /dev/vg0/lvdata

“Open” the LUKS container, test it, and create a new filesystem on it:

# cryptsetup open /dev/vg0/lvdata data
# ls -l /dev/mapper | grep data
# mkfs.ext4 /dev/mapper/data

Mount the new filesystem:

# mount /dev/mapper/data /data

Unmount the filesystem like:

# umount /data
# cryptsetup close data

If desired, set the filesystem to mount on boot. The entry in /etc/crypttab causes LUKS to prompt for a passphrase when mounting the filesystem. See CRYPTTAB(5).

In /etc/crypttab:

data /dev/vg0/lvdata none

In /etc/fstab:

/dev/mapper/data /data ext4 defaults 0 2

LUKS Passphrases

Add a passphrase to an existing device:

# cryptsetup luksAddKey /dev/vg0/lvdata

Remove a passphrase from a device:

# cryptsetup luksRemoveKey /dev/vg0/lvdata

Just for the sake of curiosity, show which LUKS key slots are in use:

# cryptsetup luksDump /dev/sdX

Note: LUKS provides no plausible deniability about the presence of encrypted data, because the LUKS header is easily identifiable.

Back Up the LUKS Header

If the LUKS header gets damaged, data will be unrecoverable. Back up the LUKS header!

See https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions#6-backup-and-data-recovery.

Back up:

# cryptsetup luksHeaderBackup --header-backup-file <file> <device>
# cryptsetup luksHeaderBackup --header-backup-file /root/luks-header-backup /dev/sdX

Restore:

# cryptsetup luksHeaderRestore --header-backup-file <file> <device>
# cryptsetup luksHeaderRestore --header-backup-file /root/luks-header-backup /dev/sdX

Example: USB hard disk as encrypted backup target

A LUKS container can be created either in a partition or on a raw block device. See “2.2 LUKS on partitions or raw disks?” in the cryptsetup FAQ.

For the scenario of a removable backup disk, it makes sense to create a LUKS device on the raw block device. Having done so, do NOT partition the container (e.g. with gdisk); instead, directly format the LUKS container with a filesystem.

Prepare a new USB disk.

# apt-get install cryptsetup
# dmesg | tail | grep 'sd[[:alpha:]][[:digit:]]'
[265197.180467]  sdc: sdc1
# shred -v --iterations=1 /dev/sdX
shred: /dev/sdX: pass 1/1 (random)...
# cryptsetup --verbose --verify-passphrase luksFormat /dev/sdX
# cryptsetup open /dev/sdX backup
# mkfs.ext4 /dev/mapper/backup
# mkdir -p /backup/usb
# mount /dev/mapper/backup /backup/usb/
# df -h
# umount /backup/usb
# cryptsetup close backup
# cryptsetup status backup

Normal backup operation:

# cryptsetup open /dev/sdX backup
# mount /dev/mapper/backup /backup/usb/

(Copy files to /backup/usb.)

# umount /backup/usb
# cryptsetup close backup
# cryptsetup status backup

In some backup scenarios, backups must happen non-interactively. If we’re not worried about storing the secret locally, use a keyfile.

# dd if=/dev/urandom of=/root/luks-keyfile-for-usb-backups bs=1024 count=4
# chmod 0400 /root/luks-keyfile-for-usb-backups
# cryptsetup luksAddKey /dev/sdX /root/luks-keyfile-for-usb-backups
# cryptsetup open /dev/sdX backup --key-file /root/luks-keyfile-for-usb-backups

If we’re adding brand-new media to our rotation, and want all disks to share the same LUKS header, prep the new disk like:

# wipefs -a /dev/sdX
# cryptsetup luksHeaderRestore --header-backup-file /root/luks-header-backup /dev/sdX
# cryptsetup open /dev/sdX backup
# mkfs.ext4 /dev/mapper/backup

Here’s an example shell script for running backups:

#!/bin/sh
set -euf
# Supply the device name of an external USB drive as the sole argument:
#     $ sudo ~/bin/backup-offsite /dev/sdc
d="$1"
mnt=/media/usb
h=/home/paulgorman
cryptsetup open "$d" backup --key-file /root/luks-keyfile-for-usb-backups
mkdir -p "$mnt"
mount /dev/mapper/backup "$mnt"
mkdir -p "$mnt"/Trash
rsync -aqh --delete --backup --backup-dir="$mnt"/Trash \
		--exclude "$h"/.cache \
		--exclude "$h"/Downloads \
		--exclude "$h"/tmp \
		"$h" \
		/data \
		/etc \
		/root \
		/var/spool/anacron \
		/var/spool/cron \
		"$mnt"
umount "$mnt"
cryptsetup close backup
cryptsetup luksHeaderBackup --header-backup-file /root/luks-header-backup "$d"

Using a loop device for testing

$ head -c 100M /dev/zero > luksfile
# losetup /dev/loop0 luksfile
# cryptsetup luksFormat /dev/loop0

Open/close/mount/unmount the loop device just like a real device.

To discard the loop device:

# losetup -d /dev/loop0