Secure Remote File Storage with SSH and DM-Crypt
published 2009-04-12 · tags: backup, dm-crypt, encryption, linux, ssh
My goal for this tutorial is to provide a simple mechanism of securely transporting and storing files on a remote storage system, such as those provided by the Rsync.net storage service. We will be using SSH for network transport encryption, and transparent volume encryption handled by DM-Crypt. The only visible content on the remote storage system will be a single “container” file which will be mounted and accessed as if it were a local folder. This provides for much more flexible file access without sacrificing security.
For more information on the Rsync.net service, visit their website. Definitely worth the money. The following has been thoroughly tested using their service, but with a little tweaking and imagination you should be able to get this working on any SSH server. Enjoy!
Initial Configuration
First, you’ll want to create an SSH key pair for automatic SSH authentication. Then you need to copy the public key to your remote server and test a connection.
ssh-keygen -t rsa
scp ~/.ssh/id_rsa.pub username@servername:.ssh/authorized_keys
ssh username@servername
Next, we’ll create an empty container file and transfer it to the remote file system via SSH. The file we’ll create is 1G, but since we create it using /dev/zero, it can be compressed during transfer which will speed up the process considerably. If you use /dev/urandom, there’s virtually no compression benefit, so it will take much longer to transfer depending on bandwidth availability. There’s entropy benefits to using the latter, so you can weigh the costs any way you like.
To create the container file, I prefer using the utility dcfldd instead of the typical dd command because dcfldd actually has a progress indicator. Of course, feel free to use dd instead of dcfldd if you wish. The following command dumps /dev/zero to stout, pipes the results through a compressed SSH connection which dumps into a file called vol1.img on the remote server. Cool.
dcfldd if=/dev/zero bs=1k count=1000000 | ssh -C username@servername "dd of=vol1.img"
999936 blocks (976Mb) written.
1000000+0 records in
1000000+0 records out
2000000+0 records in
2000000+0 records out
1024000000 bytes (1.0 GB) copied, 449.426 s, 2.3 MB/s
In order for you to mount the remote container file as a loop device, you need to be able to access it as if it were a local file. For this, you should mount the remote SSH file system using sshfs, which relies on the FUSE library. I added the allow_root option to the sshfs command, because your root account needs to be able to access the container file. The option of user_allow_others in /etc/fuse.conf needs to be uncommented, and you may need to add your user account to the fuse group or modify the permissions of the file /etc/fuse.conf in order to carry out the command.
mkdir ~/sshfs
sshfs -o allow_root username@servername: ~/sshfs
ls -lh ~/sshfs
total 977M
-rw-r--r-- 1 username username 977M 2009-04-12 11:46 vol1.img
Link the container file to a local loop device, and verify that the step has been completed successfully.
sudo losetup /dev/loop0 ~/sshfs/vol1.img
sudo losetup -a
/dev/loop0: [0018]:4 (/home/username/sshfs/vol1.img)
Create a key file that DM-Crypt will use like a password for your encrypted volume.
mkdir ~/keys
dd if=/dev/urandom of=~/keys/vol1.key bs=1 count=256
256+0 records in
256+0 records out
256 bytes (256 B) copied, 0.00357629 s, 71.6 kB/s
Encrypt and map the virtual loop device using the cryptsetup command. The options are fairly self explanatory, but as you can see, we’re using AES encryption with the SHA256 hashing algorithm. We are also specifying which key file to use as the authenticator.
sudo cryptsetup --verbose -c aes-cbc-essiv:sha256 luksFormat /dev/loop0 ~/keys/vol1.key
WARNING!
========
This will overwrite data on /dev/loop0 irrevocably.
Are you sure? (Type uppercase yes): YES
Command successful.
sudo cryptsetup --key-file ~/keys/vol1.key luksOpen /dev/loop0 vol1.map
key slot 0 unlocked.
Command successful.
ls -l /dev/mapper/vol1.map
brw-rw---- 1 root disk 254, 3 2009-04-12 14:54 /dev/mapper/vol1.map
Format, prepare and mount the new file system for use.
sudo mkfs.ext3 -j -m 0 /dev/mapper/vol1.map
mkdir ~/vol1
sudo mount /dev/mapper/vol1.map ~/vol1
Verify your results any way you wish.
mount -t ext3 | column -t
/dev/mapper/vol1.map on /home/username/vol1 type ext3 (rw)
sudo mkdir ~/vol1/{Docs,Pics,Vids}
sudo chown username:username ~/vol1/{Docs,Pics,Vids}
ls -l ~/vol1
total 28
drwxr-xr-x 2 username username 4096 2009-04-12 15:21 Docs
drwx------ 2 root root 16384 2009-04-12 15:07 lost+found
drwxr-xr-x 2 username username 4096 2009-04-12 15:21 Pics
drwxr-xr-x 2 username username 4096 2009-04-12 15:21 Vids
At this point, you can do anything you wish with your volume. Congrats. I like the idea of using rsync or unison to keep local and remote storage consistent. Remember, these utilities will be synchronizing to what appears as a local directory.
For example, the following will move the contents of your local Documents, Pictures and Videos folders to their respective counterparts on the encrypted volume. Check out the documentation for the appropriate syntax.
rsync -avP ~/Documents/ ~/vol1/Docs
rsync -avP ~/Pictures/ ~/vol1/Pics
rsync -avP ~/Videos/ ~/vol1/Vids
Tear Down the Connection
To tear down the connection, reverse the procedures.
sudo umount ~/vol1
sudo cryptsetup remove vol1.map
sudo losetup -d /dev/loop0
fusermount -u ~/sshfs
Set Up the Connection
To set up the connection again, the syntax is much easier.
sshfs -o allow_root username@servername: ~/sshfs
sudo losetup /dev/loop0 ~/sshfs/vol1.img
sudo cryptsetup --key-file ~/keys/vol1.key luksOpen /dev/loop0 vol1.map
sudo mount /dev/mapper/vol1.map ~/vol1
A Sample Script
The following is a concept script that will perform all of the described setup and disconnect functions. I included the sudo commands in all the necessary steps, so you don’t need to use sudo to run the script. Modify as necessary, as this is just to serve as an idea.
Download the script here (the download was lost with the old server; the script is reproduced in this post), or copy it from the text below.
#!/bin/bash
#
# Crypto Mount Script
# By Gilbert Mendoza
# www.savvyadmin.com
SERVER="[email protected]:"
MOUNT="$HOME/vol1"
VOL="$HOME/sshfs/vol1.img"
KEY="$HOME/keys/vol1.key"
MAP="vol1.map"
LOOP="/dev/loop0"
crypto_mount(){
sshfs -o allow_root $SERVER $HOME/sshfs
sudo losetup $LOOP $VOL
sudo cryptsetup --key-file $KEY luksOpen $LOOP $MAP
sudo mount /dev/mapper/$MAP $MOUNT
}
crypto_teardown(){
sudo umount $MOUNT
sudo cryptsetup remove $MAP
sudo losetup -d $LOOP
sleep 2 # Wait a couple seconds to avoid conflict
fusermount -u $HOME/sshfs
}
actions(){
# Examples of possible actions you might take
echo "Waiting 5 seconds..."
sleep 5
# rsync -avP ~/Documents/ ~/vol1/Docs
# rsync -avP ~/Pictures/ ~/vol1/Pics
# rsync -avP ~/Videos/ ~/vol1/Vids
}
case $1 in
start)
crypto_mount
actions
;;
stop)
crypto_teardown
;;
*)
echo "Usage: $0 {start|stop}"
;;
esac
exit 0
To use the script, the syntax is listed below.
cryptomount.sh start
cryptomount.sh stop