In this post we will cover a method we developed years ago for a specific use case of dynamically mounting external USB hard drives that are encrypted via LUKS. The need seemed simple enough at first. A client was looking for an offsite backup solution. After reviewing some options it was determined rather than the expense and complexity of a new tape system, we would leverage hard drives via an external USB enclosure to store offsite backups. These drives would then be rotated offsite on a weekly basis just like tapes would be. Oh, and some of the data will need to be encrypted for legal reasons. After running through a few scenarios I quickly discovered that one of the biggest challenges was going to be reliably, and dynamically, un-mounting and re-mounting the external drives that were encrypted. Below I will share the solution I came up with that has been working wonders ever since, and also share our script with you.
The following requirements were gathered when starting the project, some of which were requirements that I set for the solution since it needed to not be a an extremely "techie" solution that required me or another resource babysitting the mounts every week.
The biggest challenge to this turned out to be: How do you dynamically determine what encrypted partitions existed on what devices since they are...ENCRYPTED? It turns out that the desire to have the configuration of the backup server itself backed up unencrypted helped solve this problem. I soon realized that by having a small, unencrypted partition as the first partition with a labeled file system, I could use that device name to determine the physical device and then derive the encrypted device path.
For the purposes of this post, I'm only going to cover configuring a single hard drive for use with our script. However, the same process applies to however many disks you may have. Furthermore, this example assumes your physical disk device is /dev/sdx.
First, we will partition the disk using parted. We need two partitions, first a tiny one then the remainder of the disk for the encrypted partition.
# parted -a optimal /dev/sdx (parted) mklabel gpt Warning: The existing disk label on /dev/sdx will be destroyed and all data on this disk will be lost. Do you want to continue? Yes/No? Yes (parted) unit MB (parted) mkpart primary 0% 1GB (parted) mkpart primary 1GB 100% (parted) p Model: ST2000DM 001-9YN164 (scsi) Disk /dev/sdi: 2000399MB Sector size (logical/physical): 512B/512B Partition Table: gpt Number Start End Size File system Name Flags 1 1.05MB 1000MB 999MB primary 2 1000MB 2000399MB 1999399MB primary
Next, we format /dev/sdx1 as a standard EXT4 partition with a label, in this case test1.
# mkfs -t ext4 -L test1 /dev/sdx1
Now, if we reload our partitions, we can see that this partition shows up in /dev/disk/by-label as a symlink to our physical device/partition. This is the key to using our script.
# partprobe # ls -l /dev/disk/by-label/ total 0 lrwxrwxrwx 1 root root 10 Jan 18 20:06 test1 -> ../../sdx1
Next, we setup /dev/sdx2 as our LUKS encrypted partition. I enter a dummy password when prompted.
# cryptsetup --cipher aes-cbc-essiv:sha256 --verbose --verify-passphrase luksFormat /dev/sdx2
Next, we add our encryption key.
# cryptsetup luksAddKey /dev/sdx2 /path/to/our/key
Next, we open the device created in the previous step in device mapper.
# cryptsetup luksOpen --key-file /path/to/our/key /dev/sdx2 test1
Now you can create a file system on your encrypted device, for example:
# mkfs -t ext4 /dev/mapper/test1
Once thats finished, close the encrypted device
# cryptsetup luksClose /dev/mapper/test1
Our script can be downloaded from Github using the following links:
Script: https://raw.githubusercontent.com/kissit/kiss-ops/master/disk/luks_mounter.pl
Sample Config File: https://raw.githubusercontent.com/kissit/kiss-ops/master/disk/luks_mounter.ini
In terms of setup, simply download them to a common directory, say /usr/local/bin. Make sure the perl script is executable. The script requires some extra CPAN modules that are not typically included in a minimal Perl installation. To install them on a CentOS 6.x system you can do the following:
yum install perl-MIME-Lite perl-Config-Simple
You now must update the configuration file to reflect your setup. The configuration file is already well commented so I'm not going to cover it in detail here. This sample is already configured for our example above.
The script has the following options:
# ./luks_mounter.pl --help ./luks_mounter.pl: Mount or unmount the devices as configured in the config file Usage: luks_mounter.pl --mount | --unmount [ --clean ] [ --config ] --mount - Mount the devices in the config file --unmount - Unmount the devices in the config file --clean - Run the cleanups as configured (Default: no cleanups) --config - Alternate config file (Default: ./luks_mounter.ini) --help - This help page
To mount the configured disks, run the script with the --mount option. If you included directories to be cleaned in the configuration, and want to do so, also include the --clean option. This will delete the contents of the directories you have configured. This is useful for completely removing the data on the disk when being mounted.
To unmount the configured disks, run the script with the --unmount option.
By default, the script will load the configuration file luks_mounter.ini from the same directory as the script is located. If you wish to use an alternate location simply move the file as desired and pass the --config=/path/to/config/file.ini option when running the command.
In conclusion, I hope someone finds this post helpful. I'm sure many will say why all the complexity just use XYZ but nothing I could find at the time provided a solution that met all of our requirements and worked so well. This solution has been used for years without a hiccup. Please reach out to us here with any questions or comments.