]> Linux encrypted disk image 🌐:aligrant.com

Linux encrypted disk image

Alastair Grant | Tuesday 4 January 2022

As we rely on hosted (sorry, it's "cloud" now) servers more, we need to pay attention to the security of our data-at-rest.  How confident are you that the data being stored somewhere by somebody is secure?  There are various ways we can deal with disk encryption, this article is going to cover setting up an encrypted file stored on an existing partition, which can be mounted as a drive in Linux.

For this, we need a dm-crypt enabled Linux distribution, and cryptsetup.  As always, this guide is done for openSUSE (currently using Leap 15.3), so things may vary slightly with your distribution.

Image creation

Before we get onto any encryption, we need to create a file that will store our data.  There is no real "format" for this sort of thing, we just need some space to write some binary data.  To do this, we'll use "truncate", which is a fast way of creating a file of a certain size, without having to faff about with zero'ing the disk.  For instance:

truncate -s 100G /home/mydata.img

For reasons I'm not entirely clear on, this image needs to be formatted with a filesystem both before and after the encryption process.  If you do it without, you can mess about with creating a partition table on the final image, but I had problems getting that to map and mount correctly after the first attempt.  I'm using EXT4 here, because why not?

mkfs.ext4 /home/mydata.img

Encryption

Next we encrypt the image.

cryptsetup luksFormat /home/mydata.img

Following the prompts on screen.

We now need to open the encrypted image as a device.  This example will create the device /dev/mapper/mydevice which we can then mount and use as a normal file system.

cryptsetup luksOpen /home/mydata.img mydevice

Format and mount the device as you would any other Linux partition.

mkfs.ext4 /dev/mapper/mydevice

mount /dev/mapper/mydevice /mnt

Usage

At this point, you should have an encrypted image mounted as a drive and you can use this as normal.

When you're done, you may want to unmount the file system, and then use cryptsetup luksSuspend mydevice to stop access to the image.  The image is now secure again, which you can then re-open with cryptsetup luksResume mydevice and then mount again wherever you like.  You will be prompted for a password each time you resume.

You can specify a password through a file, so it's possible to have the device automatically resume and mount - which is probably a good idea in a server environment.  The catch is, if that password is being stored on the server somewhere, doesn't that defeat the point of having encryption?

A potential answer to this is to use secure key storage, but you have to be sure that anybody else with access to this machine can't just access the same storage.

Systemd

To simplify the mounting and unmounting, and also allow for dependent services to wait until the drive is mounted correctly, I wanted to manage the mounting with systemd.  I've not set these services to run at boot, so there is no hanging on bootup, and instead I have to logon and kick it off (and keep my password secure), but as above, there is no reason why you can't pull in a password from a remote location or something.

First I created a service to handle the opening of the encrypted drive:

[Unit]
Description=Unlocks encrypted image
Wants=multi-user.target

[Service]
Type=oneshot
ExecStart=/root/unlockdrive.sh
RemainAfterExit=true
ExecStop=/sbin/cryptsetup luksClose mydevice

This file needs to be saved in /etc/systemd/system with an appropriate name (decrypt-drives.service, maybe).  It simply chains onto a script /root/unlockdrive.sh:

#!/bin/bash
PWD=$(systemd-ask-password "Unlock key: ")
echo -n $PWD | cryptsetup open /home/mydata.img mydevice

This script will allow systemd to prompt for a password when starting.

And then to manage the mount point, I created a systemd mount file (/etc/systemd/system/mnt.mount)

[Unit]
Description=Mounts decrypted image
Requires=decrypt-drives.service

[Mount]
Type=ext4
What=/dev/mapper/mydevice
Where=/mnt

Running systemctl daemon-reload will load these into systemd, and then we can mount the drive through the command: systemctl start mnt.mount.  We will be prompted for a password.

If there are any services that require the drive to be mounted first, then you can add a Requires=mnt.mount entry into their service file (although again, be careful not to have them auto start on boot if you're expecting an interactive password prompt).

Breaking from the voyeuristic norms of the Internet, any comments can be made in private by contacting me.