Encrypt backups at an untrusted remote location

This blog post was published 7 years ago and may or may not have aged well. While reading please keep in mind that it may no longer be accurate or even relevant.

In a previous blog post I argued that a good backup solution includes backups at different geographical locations to compensate for local disasters. If you don’t fully trust the location, the only solution is to keep an encrypted backup.

In this tutorial we’re going to set up an encrypted, mountable backup image which allows us to use regular file system operations like rsync.

First, on any kind of permanent medium available, create a large enough file which will hold the encrypted file system. You can later grow the file system (with dd and resize2fs) if needed. We will use dd to create this file and fill this file with zeros. This may take a couple of minutes, depending on the write speed of the hard drive. Here, we create a 500GB file:

dd if=/dev/zero of=/path/to/backup.img bs=100M count=5000

A quicker method to do the same (file will not be filled with zeroes) is:

fallocate -l 500G /path/to/backup.img

Now we will use LUKS to set up a virtual mapping device node for us:

apt-get install cryptsetup

First, we generate a key/secret which will be used to generate the longer symmetric encryption key which in turn protects the actual data. We tap into the entropy pool of the Linux kernel and convert 32 bytes of random data into base64 format (this may take a long time; consider installing haveged as an additional entropy source):

dd if=/dev/random bs=1 count=32 | base64

Store the Base64-encoded key in a secure location and create backups! If this key/secret is lost, you will lose the backup.

Next, we will write the LUKS header into the backup image:

echo "Base64-encoded key" | base64 --decode | cryptsetup luksFormat --key-file=- /path/to/backup.img

Next, we “open” the encrypted drive with the label “backup_crypt”:

echo "Base64-encoded key" | base64 --decode | cryptsetup luksOpen --key-file=- /path/to/backup.img backup_crypt

This will create a device node /dev/mapper/backup_crypt which can be mounted like any other hard drive. Next, create an Ext4 file system on this raw device (“formatting”):

mkfs.ext4 /dev/mapper/backup_crypt

Now, the formatted device can be mounted like any other file system:

mkdir -p /mnt/backupspace_loop
mount -o loop /dev/mapper/backup_crypt /mnt/backupspace_loop

You can inspect the mount status by typing mount. If data is written to this mount point, it will be transparently encrypted to the underlying physical device.

If you are done writing data to it, you can unmount it as follows:

umount /mnt/backupspace_loop
cryptsetup luksClose /dev/mapper/backup_crypt

To re-mount it:

echo "Base64-encoded key" | base64 --decode | cryptsetup luksOpen --key-file=- /path/to/backup.img backup_crypt
mount -o loop /dev/mapper/backup_crypt /mnt/backupspace_loop

Note that we always specify the Base64-encoded key on the command line and pipe it into cryptsetup. This is better than creating a file somewhere on the disk. Now, if the machine is powered off, the decrypted mount point is lost and only the encrypted image remains.

If you found a mistake in this blog post, or would like to suggest an improvement to this blog post, please me an e-mail to michael@franzl.name; as subject please use the prefix "Comment to blog post" and append the post title.
 
Copyright © 2023 Michael Franzl