Protecting Files at Home Using Encrypted Containers
Chances are you have some type of information on your desktop or laptop computer that someone else wants--for the wrong reasons. For the most part, your information is relatively safe in your home. However, if someone was to break into your house and steal a computer, what would he or she walk away with? Medical information? Bank account information? Tax records? With the loss of any of these to the wrong person, you could be facing years of trying to get your life straightened out due to identity theft.
Concerned about this type of situation, I decided to engineer a way of protecting my family's critical information. But before I started, I had to set a few goals for whatever it was that I built:
It had to be as simple as possible to use.
Data loss was not acceptable.
The data had to be protected even if a computer physically was stolen.
The first goal, simple to use, was absolutely necessary. I wouldn't be the only one using the system, so it had to be geared toward someone non-technical. Setting it up could be difficult, but in the long run it wouldn't be used or useful if the solution itself was difficult. Data loss also was an important criterion. Protected data is worthless if you have no way of getting it back in the event of a hard drive failure. The last criterion was the main point of doing this whole project: if a computer was stolen, how long would it take before I could get a full night's sleep knowing that someone had access to all the information necessary to steal the identity of one of my family members?
Once my goals were set, I went about researching my options and evaluating ideas. I finally settled on using an encrypted "container" that could function like any other storage device, but only when needed. To prevent data loss, it would be backed up to my home server. On the home server, the same technology would be used to protect my backups and where I centrally could burn a CD or DVD for off-site storage.
The first step to protecting my data was to figure out how to create an encrypted container. After doing some research, I discovered there were two ways to do this. I could use either cryptoloop or its successor, dm_crypt. A considerable amount of information is available for using cryptoloop, but I quickly discovered that it has been deprecated and replaced by the more secure dm_crypt kernel module.
To complete the encrypted container configuration presented in this article, you must satisfy the following short list of prerequisites:
Kernel version 2.6.4-rc2 or higher must be installed.
KDE version 3.3 or higher must be installed.
You must have root access to complete the initial setup.
I currently am running this configuration under kernel version 2.6.12 on several Gentoo systems, both x86 and AMD64. I also have used it on kernels starting around version 2.6.6. The scripts I present here for making the system easy to use have been used under KDE versions 3.3.x and 3.4.x. Older versions of KDE also may work, but they have not been tested by me.
To use dm-crypt, a few modules must be compiled or built into the kernel. You first must enable the device-mapper module that lets you create new logical block devices from portions of existing devices. The block devices then are "mapped" to devices that for our use are treated like normal drive partitions. Once device-mapper is enabled, you then can enable dm-crypt itself; it goes by the name Crypt Target Support in the kernel configuration menu. dm-crypt is the kernel module that we actually use to handle the encryption/decryption using the crypto API available in the 2.6 version kernels.
To use an encrypted container for our files instead of an entire drive or partition, loopback device support also needs to be enabled in the kernel. The loopback device kernel module allows us to use ordinary files as if they were real block devices. Finally, you must have the encryption type you want to use compiled into the kernel or set as a module. In the examples given here, I use the AES encryption algorithm, but many other options are available.
Once you have enabled the required kernel modules, compile the kernel and install it. Because I did not build the modules directly into the kernel, I added the necessary modules--dm-mod, dm-crypt and aes-i586--to /etc/modules.autoload/kernel-2.6 on my Gentoo boxes so they would be loaded automatically at boot. Below is a list of kernel modules you need to enable in the kernel:
Device Drivers -> Multi-Device Support (RAID and LVM) -> Device Mapper Support Device Drivers -> Multi-Device Support (RAID and LVM) -> Crypt Target Support Device Drivers -> Block Devices -> Loopback Device Support Cryptographic Options -> <encryption type>
For other systems where there may not be a central location to load kernel modules automatically at boot, you could add the appropriate modprobe commands to a startup script. For Debian-based systems such as Ubuntu, the script to use is the /etc/init.d/bootmisc.sh startup script. For other distributions you can use /etc/init.d/rc.local. Here are suggested commands to add to startup scripts for non-Gentoo distributions:
modprobe dm-mod modprobe dm-crypt modprobe aes
Now that the kernel has been configured, two packages must be installed that will be used to ease the creation and use of the encrypted container as a device. The first package you need to install is the device-mapper utilities. Under Gentoo, simply executing emerge device-mapper automatically downloads, compiles and installs the package. It already may be installed in other Linux distributions. An easy way to tell if you already have the device-mapper package installed is to look to see if /dev/mapper and /dev/mapper/control already exist. If they do, the device mapper is installed. Otherwise, for Debian-based systems such as Ubuntu, you can execute apt-get install libdevmapper. If you're using a different distribution, check your package management system and see if it can be installed easily. If not, refer to the Resources section for a link to where you can download it directly.
The second package you need to install is the cryptsetup utility. Under Gentoo, it can be installed by executing emerge cryptsetup. Again, if you are using a different Linux distribution, cryptsetup already may be installed for you or available through the package management system. You can determine if the package already is installed by checking to see if the cryptsetup command is available, most likely in the /bin directory. Otherwise, refer to the dm-crypt listing in Resources for the link to the download.
After rebooting with your new kernel, you are ready to create the container for your files and mount it. Select a partition with enough space to create the container and make the container large enough to support all the files you want to put into it. Don't forget to make it large enough to hold new files. Keep in mind that at the time this article was written, it was not possible to increase the container size once it's created. Instead, you have to create a new, larger drive container, copy everything from the old one to the new one and then delete the old container.
To create the container file, use the dd command; it typically is used to copy drive partitions. In the example below, I specified a block size of 1MB and that the container should be 2,048 blocks in size. This equates to a container that is 2GB in size--1MB block size X 2,048 blocks.
As a source for the dd command, I used a special device that outputs nothing but zeros when read. Others have suggested that using /dev/random may be better, because it makes it impossible to determine how much of the container actually is being used. The choice is up to you. I also used a partition (/encrypted) that I set aside specifically for storing my containers.
dd if=/dev/zero of=/encrypted/data.crypt bs=1m count=2048
Now, create a loopback device using your container file. If you use multiple containers, you have to use a different /dev/loopX device--where X is a unique number--if you want them all to be mounted at the same time. I chose to put specific containers on specific numbers in my scripts (details below) so I know which one is using which loopback device.
losetup /dev/loop0 /encrypted/data.crypt
Next, create the encrypted device. In the example given below, I chose to use the AES encryption algorithm, which was compiled as a kernel module during kernel configuration. When the device is created, you must specify a password, otherwise known as a key, that is used to encrypt and decrypt everything in the container. This can be a secure password that you specify on the command line. In my case, I used /dev/random to generate a 32-character random string (256 bits) that I stored in a file named /home/pritchey/crypto.key temporarily; more on this later.
cat /dev/random > /home/pritchey/foo (hit control-c after a second goes by to stop it!) cat /home/pritchey/foo | cut -b 0-31 > /home/pritchey/crypto.key rm /home/pritchey/foo cryptsetup -c aes -d /home/pritchey/crypto.key create data.crypt /dev/loop0
You now have something that acts like a normal drive partition. In order to mount and use it, you must create a filesystem on it. I chose to use the ext2 filesystem, but others can be be used. Once the filesystem is created, it can be mounted like a regular drive partition.
mke2fs -j /dev/mapper/data.crypt mkdir /mnt/encrypted mount /dev/mapper/data.crypt /mnt/encrypted
Once mounted, your encrypted device acts the exact same way that a normal drive partition does. You can copy files to it, delete them, edit them and so on. The only difference is you must remember to unmount the device properly and destroy the loopback setup when you are done with it:
umount /mnt/encrypted cryptsetup remove data.crypt losetup -d /dev/loop0
Getting access to your files in the future requires three simple steps:
losetup /dev/loop0 /encrypted/data.crypt cryptsetup -d /home/pritchey/crypto.key create data.crypt /dev/loop0 mount /dev/mapper/data.crypt /mnt/encrypted
This solves the problem of protecting our data. But, for non-technical users, these commands are difficult to remember and scary looking--not to mention they require root access. The next step is to automate the mounting/unmounting of the encrypted containers in a way that is easy to do yet not obvious to someone who might not think to look for them.
To automate the mounting/unmounting of the encrypted container, I turned to my favorite search engine and a coworker. The problem I encountered when trying to create a script for the above commands was root access is required to set up everything. I wanted others in the house to be able to use the encrypted containers, but they're not experienced enough yet to be trusted with root-level access. Carlos, a co-worker, once had to solve a similar problem that required root access to run a script, yet didn't warrant the use of sudo. He found a Perl script that did the trick when the sticky bit was set, essentially acting as a wrapper around the real script. I searched the Web and couldn't find the Perl script, but I did stumble upon exactly what I needed: a tiny program written in C that acts as a wrapper (see Resources) around a script that needs root privileges. All that was required was changing the commands the program was supposed to run, compiling the program and copying it into place with the sticky bit set.
To use the wrapper, you need to change three values before compiling it. First, set the variable myprog to the script that you want to execute. The second variable, named myarg1, contains the name of the loopback device (/dev/loopX) to be used. The last variable, myarg2, is set to the name of the container that you want to mount. For my naming scheme, I add a .crypt to the end of my container file names. I also named my key files in a similar manner, ending them with .key. Here is the C source code for wrapper.c:
#include <stdio.h> #include <unistd.h> const char myprog[] = "/bin/encrypt_mount_gen"; const char myarg1[] = "loop0"; const char myarg2[] = "data"; main() { putenv("PATH=/bin:/usr/bin"); putenv("IFS= \t\n"); setgid(getegid()); setuid(geteuid()); execl(myprog, myprog, myarg1, myarg2, (char*)0); fprintf(stderr,"Could not execute %s\n",myprog); perror("exec"); exit(1); }
Once the appropriate changes are made, compile the program, copy it into place and be sure to set the permissions correctly. I chose to create a special group in /etc/group only for users that should be able to mount the encrypted container (named crypto). The permissions and ownership of the directory the encrypted device is mounted to, created in a previous step as /mnt/encrypted, is also set this way. This prevents other users who have accounts on the computer from mounting or accessing the encrypted devices.
gcc -o mysecretcommand wrapper.c cp mysecretcommand /bin chown root:crypto /bin/mysecretcommand chmod 750 /bin/mysecretcommand chmod u+s /bin/mysecretcommand #To protect mounted encrypted data: chown pritchey:crypto /mnt/encrypted chmod 770 /mnt/encrypted
To handle unmounting the encrypted containers, you need to use the wrapper program again--named encryptoff in the sample scripts. This time, though, it calls a script containing the appropriate commands to undo the encrypted container. Here is a sample script for unmounting a mounted encrypted container:
#!/bin/bash /bin/umount /mnt/encrypted /bin/cryptsetup remove data.crypt /sbin/losetup -d /dev/loop0 #Repeat the above three commands for each encrypted container....
The script called from the above wrapper is written so that when called with two parameters, it would be able to mount any encrypted container. That way I could have as many wrappers as needed that are unique to each specific encrypted container but only have one script that actually performs the mounting and unmounting. The script assumes that the encrypted container ends in .crypt and the key file ends in .key, but both have the same beginning name that's passed as the second parameter.
To help increase security, I also decided to store the key (password) used for the encryption device on a USB thumb drive. It's never a good idea to store passwords in files, but in this case the key is impossible to remember. As long as the thumb drive is kept separate from the computer it belongs to--locked in a safe, for example--it would be an adequate solution. If the computer is stolen, the likelihood of the thief taking the USB key along with the computer and knowing what to do with it is small. And with a 256-bit (32-character) key, the system would be difficult to crack by brute force methods.
So the automation script also is responsible for mounting the USB thumb drive and unmounting it when done. This minimizes the amount of time the thumb drive needs to be connected to the computer. Hopefully, this helps minimize the chances of someone leaving it connected, as the drive can be removed immediately after logging in (detailed later).
Here is the encrypt_mount_gen shell script called from the wrapper:
#!/bin/bash /bin/mount /mnt/usb_stick /sbin/losetup /dev/$1 /encrypted/$2.crypt /bin/cryptsetup -d /mnt/usb_stick/$2.key create $2.crypt /dev/$1 /bin/mount /dev/mapper/$2.crypt /mnt/encrypted_$2 /bin/umount /mnt/usb_stick
This design partially solves the ease of use requirement, but it still requires someone to remember a command, which could be named anything I want in order to hide it. I had an idea, and searching the Web revealed that KDE has two special directories that can contain scripts or links to executables the user wants run when he or she first logs in or out. Every user's home directory contains a hidden directory named .kde. This is where the two directories used for login/logout are located. The directory used on login is called Autostart, note the capital letter A. Simply place a script here that executes as many of your wrappers as needed. Here is the sample script I placed in my /home/pritchey/.kde/Autostart directory:
#!/bin/bash /bin/mysecretcommand /bin/mysecretcommand2 ...
Similarly, we can use the directory .kde/shutdown with another script to unmount and clean up automatically any encrypted containers. This is the final piece that solves the ease of use issue. To use the system, all you need to do is power on the computer, plug in the USB thumb drive with the keys to the encrypted containers and log into KDE. Once logged in, unplug the thumb drive and return it immediately to its hiding spot. If you log in before remembering to plug in the thumb drive, you either can log out and back in again with the thumb drive attached or execute the wrapper command from a shell prompt.
One remaining requirement needs to be addressed: protecting ourselves from data loss. I had solved this problem before, so it simply was a matter of integrating the encrypted containers into my backup system. All of my systems are set to back up users' home directories when they log out of KDE, which is triggered by a script in .kde/shutdown. All I needed to do was modify the script to handle the encrypted containers when they are mounted. This script executes rsync and saves any changed files to a server. I modified the backup script to take parameters specifying the directory to be synced. The default is to sync the user's home directory with the backup location on the server. The script in the user's shutdown directory then was modified to detect if the encrypted containers were mounted and if so, rsync it before unmounting. Syncing the mounted data is much faster than trying to sync the large container file itself, even on a quiet home network. Here is the script I placed in /home/pritchey/.kde/shutdown:
#!/bin/bash # Look to see if our encrypted partitions have been mounted # and if so back them up before unmounting them. is_projects_mounted=`df | grep encrypted_projects` is_encrypted_mounted=`df | grep encrypted | grep -v projects` if [ "$is_projects_mounted" != "" ]; then /home/pritchey/scripts/backup_rsync.sh /mnt/encrypted_projects fi if [ "$is_encrypted_mounted" != "" ]; then /home/pritchey/scripts/backup_rsync.sh /mnt/encrypted fi # Now that they have been properly backed up, umount the # encrypted partitions. Call the wrapper that calls the # script with the commands to 'undo' the mounted containers. /bin/encryptoff # Now backup non-encrypted data so we have an off computer backup. /home/pritchey/scripts/backup_rsync.sh
And here is the general purpose directory backup script referenced in my KDE shutdown script:
#! /bin/bash -x # This script performs a very simple backup of the user's home directory # by default (if no parameters are passed). # # This script is meant to be referenced in the user's ~/.kde/shutdown directory # # If parameters are passed, then they are used as follows: # Parameter 1: Used as the base directory to be synced. # Parameter 2: Used as the 'ignore' list of directories/files echo "------------" >> /tmp/rsync.log echo "starting" `/bin/date` >> /tmp/rsync.log # make sure we're online and have access to our backup server... if [ `ping -c 1 192.168.1.50 | grep '64 bytes from' | wc -l` -eq 1 ] then if [ "$1z" == "z" ]; then BACKUP_DIR="/home/$USER" EXCLUDE_DIR="/home/$USER/OpenOffice*" else BACKUP_DIR="$1" EXCLUDE_DIR="$2" fi /usr/bin/rsync -vauR --delete --stats --exclude="$EXCLUDE_DIR" "$BACKUP_DIR" rsync://192.168.1.50/intrepid-linux echo "Rsync for: $BACKUP_DIR succeeded" >> /tmp/rsync.log else echo "Rsync for: $BACKUP_DIR FAILED" >> /tmp/rsync.log fi echo "completed" `/bin/date` >> /tmp/rsync.log
I have read many articles on encrypting entire partitions and drives, but I chose to use containers instead. By using containers, I have the flexibility to move them around, back them up to CD or DVD and not mount them when I don't need them. If the situation was different, I may have chosen the route of using encrypted partitions or drives. In the beginning I also was concerned about the performance impact of using an encrypted system. In a year's worth of use, though, I have not noticed any performance problems.
The backup script I use obviously can be expanded to be much more robust. For my home use, it's proven to be reliable enough. But in a work environment, I would take the time to add additional safety checks. I also chose not to go into too much detail here about the use of rsync. Many good articles detailing the use of rsync are available, and you might decide to use another method for performing backups.
In order to complete the protection of my systems, I also have included the commands used to unmount the encrypted containers in the system shutdown scripts. This way, if a system is shutdown remotely or the UPS software shuts the system down, the encrypted containers are unmounted properly and cleanly.
I would like to thank Carlos for the pointer to the idea of using an suid wrapper and Darryl for testing the scripts and installation procedure presented here. I have enjoyed working with both of them over the years, and they have proven to be great resources of information for things nobody else can figure out.
Linux: Replacing Cryptoloop With dm-crypt: article stating that dm-crypt is the preferred method to use over cryptoloop.