Configuring automount for LUKS encrypted disk image on SMB share
In my previous post, I detailed how I set up an LUKS-encrypted filesystem on a loop device (a.k.a. sparse disk image file). To make automated backups easier and not have to add commands in my backup script to handle the mounting and unmounting of the disk image, I set up automount to:
- Mount the SMB share when accessing the
/cifsdirectory - Attach a loop device and set up LUKS filesystem access when accessing the
/encrypteddirectory
Following along with the examples in my previous posts, I have a SMB share available at //lacie-2big/backup with the encrypted disk image linuxbackup.sparseimage sitting on it. My goal was to be able to do this:
$ ls /encrypted/linuxbackup
and for automount to auto-magically mount the SMB share and then mount the encrypted disk image. Unfortunately I could not find a way for automount to handle this "cascading" or "recursive" mount operation. Instead I have to execute two commands:
$ ls /cifs/lacie-2big/backup linuxbackup.sparseimage $ ls /encrypted/linuxbackup hda1 hda2 hda3 hda6 hda7 lost+found
Still, two simple ls commands are better than the 4 hard-to-remember mount, losetup, cryptsetup, and mount commands needed to do this manually. And with the help of a small script along with what automount provides, I don't have to worry about the 4 hard-to-remember umount, cryptsetup, losetup, umount commands needed to unmount everything when I'm finished.
Automount CIFS mount script
I found online an example automount script based from the auto.smb one that comes in many distributions called auto.cifs that will work with SMB mounts requiring authentication. That script looks like:
#!/bin/bash
# $Id$
# This file must be executable to work! chmod 755!
key="$1"
# Note: create a cred file for each windows/Samba-Server in your network
# which requires password authentification. The file should contain
# exactly two lines:
# username=user
# password=*****
# Please don't use blank spaces to separate the equal sign from the
# user account name or password.
credfile="/etc/auto.smb.$key"
# Note: Use cifs instead of smbfs:
mountopts="-fstype=cifs,file_mode=0644,dir_mode=0755,uid=root,gid=wheel"
smbclientopts=""
for P in /bin /sbin /usr/bin /usr/sbin
do
if [ -x $P/smbclient ]
then
SMBCLIENT=$P/smbclient
break
fi
done
#echo $SMBCLIENT >&2
[ -x $SMBCLIENT ] || exit 1
if [ -e "$credfile" ]
then
mountopts=$mountopts",credentials=$credfile"
smbclientopts="-A "$credfile
else
smbclientopts="-N"
fi
#echo $smbclientopts -gL $key >&2
$SMBCLIENT $smbclientopts -gL $key 2>/dev/null \
| awk -v key="$key" -v opts="$mountopts" -F'|' -- '
BEGIN { ORS=""; first=1 }
/Disk/ { if (first) { print opts; first=0 };
gsub(/ /, "\\ ", $2);
sub(/\$/, "\\$", $2);
print " \\\n\t /" $2, "://" key "/" $2 }
END { if (!first) print "\n"; else exit 1 }
'
I created that as /etc/auto.cifs and made it executable. Then in /etc/auto.master I added this line:
/cifs /etc/auto.cifs --timeout=60
The credentials for mounting the SMB share are then stored in a /etc/auto.smb.lacie-2big file, which is how I set things up originally in my last post. Now, after reloading the autofs service, I am able to:
$ ls /cifs/lacie-2big/backup
linuxbackup.sparseimage
Automount LUKS script
Then I created another automount script /etc/auto.luks.loop.0:
#!/bin/bash # This file must be executable to work! chmod 755! # Make links to this file with the last digit replaced # with other numbers for corresponding loop devices, # e.g. if this script is executed as auto.luks.loop.1 the # /dev/loop1 device will be used. # # The LUKS key must exist as a file at /etc/.key key="$1" los="" cry="" name=`basename $0` l=${name##*.} img="/cifs/lacie-2big/backup/$key.sparseimage" mountopts="-fstype=ext3,defaults,noatime,nodiratime" if [ ! -e "/etc/$key.key" ]; then exit 1 fi # search for losetup and cryptsetup for P in /bin /sbin /usr/bin /usr/sbin do if [ -z "$los" -a -x $P/losetup ]; then los=$P/losetup fi if [ -z "$cry" -a -x $P/cryptsetup ]; then cry=$P/cryptsetup fi if [ -n "$los" -a -n "$cry" ]; then break fi done # check if loop device already attached, if not then attach it chk=`$los -a |grep /dev/loop$l` if [ -z "$chk" ]; then if [ ! -e $img ]; then echo "Image file $img not found." >&2 exit 1 fi $los /dev/loop$l $img $cry --key-file /etc/$key.key luksOpen /dev/loop$l \ luks-`$cry luksUUID /dev/loop$l` >/dev/null 2>&1 fi echo $mountopts / :/dev/mapper/luks-`$cry luksUUID /dev/loop$l`
This script is hard-coded to look for disk image files in /cifs/lacie-2big/backup named imagefilename.sparseimage where filename will be the automount directory name. The script uses the last number of the script name to determine which loop device to use, although it could be easily adapted to use any freely-available device (by way of losetup -f).
Then it uses cryptsetup to open the LUKS device, using a key file located at /etc/imagefilename.key. Then it configures automount to mount the LUKS filesystem. In short, the script basically does:
$ losetup /dev/loop0 /cifs/lacie-2big/backup/linuxbackup.sparseimage $ cryptsetup --key-file /etc/linuxbackup.key luksOpen /dev/loop0 \ luks-`cryptsetup luksUUID /dev/loop0` $ echo -fstype=ext3,defaults,noatime,nodiratime \ / :/dev/mapper/luks-`cryptsetup luksUUID /dev/loop0`
That last line is what is returned to automount and causes it to mount the image file as a directory named filename.
Finally in /etc/auto.master I added this line:
/encrypted /etc/auto.luks.loop.0 --timeout=600
Afer having the autofs reload this configuration, I am then able to do this:
ls /encrypted/linuxbackup
hda1 hda2 hda3 hda6 hda7 lost+found
With the caveat that the /cifs/lacie-2big/linuxbackup directory is already mounted. This is where I couldn't find a way for automount to mount both file systems in one call.
Auto-umount
One final piece remains to be automated, however: completely unmounting both the encrypted and SMB file systems. automount will take care unmounting the LUKS encrypted filesystem after the configured period of inactivity. However, it won't be able to unmount the SMB filesystem because the auto.luks.loop.0 script has attached the /dev/loop0 device to the disk image file on that share. In effect, the SMB share is still in use.
It would be very nice if automount provided a way to execute scripts when it unmounted a filesystem. But it does not. My solution was to write a small script that runs every so often (via cron) that looks to see if any loop devices are attached to filesystems that are "not in use" and if found, detach them. Here's the script, which I have stored at /etc/auto.luks.loop.umount:
#!/bin/bash
los=""
cry=""
# search for losetup and cryptsetup
for P in /bin /sbin /usr/bin /usr/sbin
do
if [ -z "$los" -a -x $P/losetup ]; then
los=$P/losetup
fi
if [ -z "$cry" -a -x $P/cryptsetup ]; then
cry=$P/cryptsetup
fi
if [ -n "$los" -a -n "$cry" ]; then
break
fi
done
for dev in `$los -a|cut -d: -f1`; do
file=`$los -a|grep /dev/loop |sed 's/.*(\(.*\)).*/\1/'`
dir=${file%/*}
# check if the only open file on loopback file's filesystem
# is from automount, if so close loopback device so automount
# can un-mount filesystem for us later
match=`lsof |grep $dir|cut -d" " -f1 |grep -v automount`
if [ -n "$match" ]; then
echo "Loop device $dev attched to in-use filesystem $dir" >&2
else
echo "Loop device $dev on unused filesystem $dir"
$cry isLuks $dev 2>/dev/null
if [ "$?" -eq "0" ]; then
echo "Closing LUKS on $dev"
$cry luksClose luks-`$cry luksUUID $dev`
fi
echo "Unattaching loop device $dev"
$los -d $dev
fi
done
It's not particularly clever. I'm sure it would not work on systems using loop devices for other things that what I'm using them for on my system and it's making assumptions on the output of losetup. But it does the job nicely for what I need. I have this run every so often via cron. After it runs, and if it found any loop devices it could detach, then automount will eventually unmount the SMB share for us and we're done.
These scripts can be obtained via anonymous CVS:
cvs -d :pserver:anonymous@msqr.us:/data/cvs co twobig