Backup on LUKS-encrypted external HDD
One of my strategies to prevent important (electronic-form) information from becoming too ephemeral… [Script] [Config] [Update history]
The goal of this text is to share with you a simple strategy to backup your important data to an encrypted external drive. (LUKS, which stands for Linux Unified Key Setup, is the disk encryption framework that we will use to encrypt the external drive.)
Scenario. More specifically, the goal is to create a backup of a set of locations—say, /home/<user>/folder1, /home/<user>/folder_2, etc.—where <user> is your username. The bash script I will provide also gives you the possibility of quickly restoring that external backup onto your computer—on which more later.
Prerequisites. An external hard drive, of course, and in addition, a machine with a Linux installation that isn’t ancient. As already hinted above, the bash shell is required, and furthermore, the cryptsetup package is also a requirement (this is what we’ll actually use to create and manage the LUKS encrypted volume). Finally, the user which will do the backups must have sudo privileges.
To avoid the perilous path of partioning, I shall assume that your entire external drive is to be formatted as a LUKS container. This, of course, needn’t be so: it is perfectly feasible to create multiple partions in said external drive, and use just one of them as a LUKS container for your backups. But I am afraid that you must look elsewhere for instructions on how to do that.1
Hereinafter, I shall assume that your external drive is /dev/sdb—modify as required for your own system. The first step, as I said, is to turn your whole drive into a LUKS container. But before that, there is an
IMPORTANT CAVEAT: if your drive has some previous data in it, setting it up as a LUKS container will render any such data inaccessible. But, just to make matters worse, it might not actually delete said pre-existing data. So, on the one hand, please backup any previous data that you want to keep. On the other hand, do not assume that the format process will wipe the drive of its previous contents.2
Setup. LUKS encryption can be done either using a passphrase, or a key file—here we are going for the latter. The key file it self can be anything really—an image, a PDF file, etc.—but here, we are going for a randomly generated hunk of bits (note that this has to be done as root):
# dd bs=512 count=4 if=/dev/random of=/path/to/keyfile iflag=fullblockNeedless to say, anyone who gets access to this file (keyfile), can trivially decrypt the LUKS container to be created—so be sure to keep it safe!! It is already owned by root, but just for good measure, I also make sure that only root can read it:3
# chmod 400 /path/to/keyfileNow, to format the drive as a LUKS container, do:
# cryptsetup luksFormat /dev/sdb /path/to/keyfileNow we have to open the encrypted volume we just created, and format that with an actual file system. Here I will use ext4, even despite its annoying lost+found folder.4
# cryptsetup --key-file=/path/to/file open /dev/sdb backupdevThis creates a new (“virtual”) device under /dev/mapper, more specifically /dev/mapper/backupdev. To dump an ext4 file system on it:
# mkfs.ext4 /dev/mapper/backupdevAnd now mount it:
# mount /dev/mapper/backupdev /mntBy default, the owner of your new partion will be root—which will mean that any copying of files therein must also be done as root. To avoid this, change its owner to you:5
# chown -R <user> /mntMoving on, you can choose to store the backup data directly into the root of your drive (i.e., its top folder), but I prefer to create a specific folder, and name it something like <USER>-BACKUP. Then, all of your backup data will be inside that folder. This leaves you free to, for example, use any remaining space on your drive to store other things. It is done like so:
# mkdir /mnt/<USER>-BACKUPThe setup of the external drive is done! All that is left is to unmount it, close the encrypted container—and then proceed to setup the bash script. (Note that below, umount is not a typo—the command really is named like that…)
# umount /mnt
# cryptsetup close backupdevOK, onto the script! The code is fairly simple: it consists of the script proper, and its configuration file. Save the former into a file with the .sh extension (below I will use backup_script.sh), and the latter in file $HOME/.config/ext_luks_bck/config.sh.6
On the config file, there are the following variables to set up:
ext_luks_dev: the UUID of the device. This done because the name of a device (/dev/sdb,/dev/sdc, etc.) can vary, depending on, for instance, the USB port onto which it is attached—and hence, it should not be hardcoded. UUIDs, in contrast, are unique. To retrieve it, do (asrootor not, no matter):lsblk -o +UUID.ext_luks_desc: the description of the current configuration file. Unused by the script, this is to help the user not get confused, in the scenario where he uses the current script in several computers, and/or has several backup devices. If you use only one computer and one backup drive, you can safely ignore this.ext_luks_key: the path to thekeyfilecreated above.ext_mount_point: the mount point on which the decrypted LUKS volume (above it was/dev/mapper/backupdev) should be mounted (above we used/mnt, and indeed, this can be a fine choice).ext_root_backup_folder: what I above named<USER>-BACKUP—but give it any name you want, or no name at all. (But if you give it another name, keep in mind you will have to modify accordingly themkdircommand used above.)locations: the locations to back up. They must be absolute paths. Take the examplelocations=+(...)lines, modify them to suit your needs—and if needed, add any extra such lines, as required by the number of locations that you want to include in the backup.excludes: last, but surely not least, the locations to exclude from the back up. It is optional, but if you need it, take the exampleexcludes=+(...)lines, and modify them to suit your needs.
Usage. Just run the script:7
$ sh /path/to/backup_script.sh
Without any arguments, as shown above, it creates/syncs a backup. With -r, it restores the data in the backup to its original location—but without overwriting files that, while present in the backup, have been modified (in the original location) after the backup was done. I.e., restoring a backup preserves files that are newer than the version that exists in the backup.8
In either case, adding the -y option does a --dry-run (i.e., a simulation) of the task that would have been done, if the -y had not been given. (So giving -y alone simulates a backup; giving -y -r—the order does not matter—simulates restoring a backup.)
March 8, 2022. Got feedback? See the contact page.
*   *   *
Update history
March 21, 2024. Script can now be used with a user-specified config file $ sh /path/to/backup_script.sh -f /path/to/custom/config/file.sh. I also added the ability to exclude certain files or folders from being backed up; cf. the new config template.
August 31, 2023. The script now outputs messages to inform the user when the LUKS volume has been successfully mounted/unmounted. Moreover, two new options: -m mounts the LUKS volume, and nothing else; and -u unmounts the LUKS volume, and nothing else. Also, the configuration variables have been placed in a separate file, to ease setting them up.
The Arch Wiki might be a good place to start digging for information on partioning drives.↩︎
This is actually a fairly complex issue, that I cannot hope to cover adequately here. An elucidating read on the some of the problems, might be
cryptsetup’s own FAQ, specifically question 5.19.↩︎There is no need to ever write to this file, so writing permissions make no sense (to give the owner writing permissions, we would use
600instead of400; cf.man chmod). Furtermore,rootpermissions are needed to mount the encrypted volume, so making this file readable byrootonly, should cause no problem.
However, thersynccommand is not run withsudoprivileges—and so, if you backup locations include the folder that stores the (root-only readable) keyfile (which is the case for me), you will get an permissions error fromrsync… The way I solve this problem is to add the keyfile to the exclude list, and transfer it manually. After all, its content is supposed to never change…↩︎The
ext4file system uses alost+foundfolder, that it creates on the root of the device, to store information that might help to repair the file system, if it becomes corrupted, or whatever. Which is all nice and well, except that, for the life of me, I cannot understand why the designers did chose to store said information… in a hidden folder! Anyway, as this is a backup device, you can probably just delete the folder (rm -rf lost+found) and move on… (it will be recreated only when and if you run thefsckfile system repair program on the drive). Or just use any other file system…↩︎If you subsequently mount the drive again, the contents will still be owned by you, i.e., this change is permanent. So this
chownthing needs to be done only once.↩︎You can put the config file in any place you like, but then you need to modify the
$config_filevariable in the main script, to point the correct location. Alternatively, you can specify which config file to use, with the-fhandle—cf. the March 21, 2024 update. This is especially useful if you have more than one config file.↩︎This assumes that the
shcommand points to thebashshell, as is now customary. If this not the case, either run the script replacingshwith/bin/bash, or make the script executable ($ chmod u+x /path/to/backup_script.sh) and then run it like this:
↩︎$ ./path/to/backup_script.shThis is useful in the following kind of scenarios: say you back up your entire home directory, and then one day—some time after you last updated your backup—you accidentally delete a couple of family vacation photos. No sweat: just run this script with
-r, and presto: the deleted photos are restored, but any files on which you have worked—and thus modified—after the last backup are left undisturbed. (Needless to say, the files that exist and are equal on both sides are also left undisturbed.)↩︎