Mounting an Encrypted External Partition

Now that you have a suitably set up encrypted partition on an external drive for offsite backups, mounting and unmounting it each require several steps. It's much simpler to have a shell script to do all that. I call these two offsite.mount, and offsite.umount.

They have lots of debug statements from the development period, now commented out. I comment them out rather than remove them so I can easily bring them back if (Muprhy forbid!) it is necessary.

offsite.mount

This script scans devices looking for possible external drive partitions (/dev/sd?* where ? is not a), lines 30 through 40. Note that one host, hawk, has a second drive, which also has an encrypted partition on it. On hawk we start scanning with sdc. On other hosts, we start scanning on sdb.

The reason for scanning for encrypted partitions is to avoid having to make an assumption as to which partition on which external drive is the one we want, which could cause problems in the future. Each partition is tested as to whether it is a LUKS partition, line 44. If not, we try again. If so (line 49), we open it, line 52. That requires the password. (You did remember the password, didn't you?) If we never find an encrypted pertition, we skip to line 119, and exit with a complaint.

Once the encrypted parition is opened, we can access it, lines 59 through 114.

First we check the drive's check interval, and if it is not 3 months (7776000 seconds), we set it to that value, lines 61 through 68.

Similarly for the maximum mount count, which should be 15, lines 70 through 77. You should have been done both when you formatted the partition, but a bit of redundancy doesn't hurt.

Then we fsck the partition, lines 81 through 87, and mount it, lines 89 through 98. Most of the time, since we are not forcing fsck (option -f), nothing happens. But every three months or 15 mounts, whichever comes first, we do check the drive for errors.

Then some housekeeping. In case the partition has never been used before, we create the directory where, by custom, all our offsite backups will go, lines 100 through 106.

Next we write a file with the data and host on which we are mounting the partition, lines 108 through 111, and print out some listings to give the user warm fuzzies. age (line 108) is a utility I wrote to keep backups of things around. It isn't really necessary here, so I won't detail it.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#! /bin/bash

# Time-stamp: <2020-01-01 16:14 root offsite.umount>

# A script to mount an offsite external drive.

# Copyright 2014 through the last date of modification, Charles
# Curley. This replaces the 2008 version, which is too unweildy. This
# version now (2019-10-04) uses an encrypted file system rather than
# ecryptfs, due to Debian 10 dropping the ecryptfs-utils package.

# Partition encryption is done per notes at
# https://charlescurley.com/blog/posts/2019/Nov/29/encrypting-an-external-partition/

devDir="/dev/"
offsiteDir="/media/offsite"

mkdir -p ${offsiteDir}

# Identify the offsite partition. It will not be on sda, but may be on
# any other partition. Warning: hawk has an encrypted partition on
# sdb, but that's not what we want. So on hawk we also ignore sdb.

# We can't use * alone here because that would return the drive
# file (e.g. /dev/sdc), which we can't use. Instead, we use the ?,
# which insists on one character. Then *, which insists on 0 or
# more characters, on the off chance we see a drive with more than
# 9 partitions.

if [ "${HOSTNAME}" == 'hawk' ] ; then
    # echo Host is hawk.
    dev=$(ls ${devDir}sd[c-z]?* )
else
    # echo host is $HOSTNAME.
    dev=$(ls ${devDir}sd[b-z]?* )
fi

# echo 'Device(s): '$dev

for offsite in ${dev} ; do
    # echo Partition is ${offsite}

    # be sure it's a LUKS partition...
    cryptsetup isLuks ${offsite}
    ret=$?

    # echo Return from isLuks is ${ret}

    if [ "${ret}" = '0'  ] ; then
        # echo "${offsite} is a LUKS partition."

        cryptsetup luksOpen ${offsite} offsite
        ret=$?

        if [ "$ret" != '0' ] ; then
            echo "luksOpen error $ret. Stopping."
            exit $ret
        fi
        offsiteDev=${devDir}mapper/offsite

        interval=$(dumpe2fs -h "${offsiteDev}" 2> /dev/null | grep 'Check interval' | cut -b27-33)
        # echo Check interval is "$interval"
        if [ "$interval" != "7776000" ] ; then
            echo setting interval. Interval was "${interval}"
            tune2fs -i 3m "${offsiteDev}"     # Set check interval to 3 months
        # else
        #     echo not changing check interval.
        fi

        maxCount=$(dumpe2fs -h "${offsiteDev}" 2> /dev/null | grep 'Maximum mount count:' | cut -b27-28)
        # echo maxCount = "$maxCount"
        if [ "$maxCount" != "15" ] ; then
            echo setting maximum mount count. Max mount was "${maxCount}"
            tune2fs -c 15 "${offsiteDev}" # Set max mount count
        # else
        #     echo not changing max count.
        fi

        dumpe2fs -h "${offsiteDev}" 2> /dev/null | egrep -i 'check(ed| )+|nt count'

        fsck "${offsiteDev}"
        ret=$?

        if [ "$ret" != '0' ] ; then
            echo "fsck error $ret. Stopping."
            exit $ret
        fi

        mount -v ${offsiteDev} ${offsiteDir}

        ret=$?

        # echo return from mount is ${ret}.

        if [ "$ret" != '0' ] ; then
            echo "Mount error $ret mounting ${offsiteDev} on ${offsiteDir}. Stopping."
            exit $ret
        fi

        mkdir -p ${offsiteDir}/myob # for initial mounts
        ret=$?

        if [ "$ret" != '0' ] ; then
            echo "mkdir error $ret. Stopping."
            exit $ret
        fi

        /usr/local/bin/age "${offsiteDir}/myob/last.mounted"

        date +%F.%T > "${offsiteDir}/myob/last.mounted"
        hostname >> "${offsiteDir}/myob/last.mounted"
        ls -ltr ${offsiteDir}/myob/last* # /dev/mapper/ ${offsiteDir}

        exit 0;                 # Unstructured! Unstructured!
    # else
    #     echo "Oops: ${offsite} is NOT a LUKS partition."
    fi

done

echo "Oops! No offsite drive found!"
exit 1

offsite.umount

This script is for unmounting the encrypted partition. It also checks for other mounted partitions on the same drive, and unmounts them as well.

First, we determine the offsite partition's device (line 15). If we don't detect it, we stop (lines 17-20). Then we detect the mount point, line 22, and the underlying device (line 28).

We actually do the deed on lines 34 through 40. We close the LUKSs device in lines 43 through 49, and remove the mount point, lines 51 through 57.

On the off chance that other (not encrypted) partitions are also mounted, we scan for and unmount those, lines 60 through 70. This is followed by two commands to shut down the drive, in preparation for unplugging it. Finally, we print out the date, for entering the date in our records.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#! /bin/sh

# Time-stamp: <2020-01-01 15:05 root offsite.umount>

# A script to umount an offsite external drive.

# Copyright 2008 through the last date of modification, Charles
# Curley. This (2019-10-04) version now uses an encrypted file system
# rather than ecryptfs, due to Debian 10 dropping the ecryptfs-utils
# package.

# Partition encryption is done per notes at
# https://charlescurley.com/blog/posts/2019/Nov/29/encrypting-an-external-partition/

device=$(grep -i offsite /etc/mtab | cut -f1 -d' ')

if [ -z ${device} ] || [ ! -L ${device} ] ; then
   echo Offsite device ${device} does not exist. Stopping.
   exit $?
fi

mounted=$(grep -i offsite /etc/mtab | grep '^/dev/' | cut -d ' ' -f 2)
# echo Mounted is ${mounted}. Device is ${device}.

# exit

# Get the underlying partition.
uDevice=$(cryptsetup -v status offsite | grep device | cut -b12-19)
# echo Underlying device is ${uDevice}

# exit

# Do the deed.
umount ${mounted}
ret=$?
# echo Return from umount is $ret
if [ "$ret" != '0' ] ; then
    echo "umount error $ret. Stopping."
    exit $ret
fi


cryptsetup luksClose offsite
ret=$?
# echo Return from luksClose is $ret
if [ "$ret" != '0' ] ; then
    echo "luksClose error $ret. Stopping."
    exit $ret
fi

rmdir ${mounted}
ret=$?
# echo Return from rmdir is $ret
if [ "$ret" != '0' ] ; then
    echo "rmdir error $ret removing ${mounted}. Stopping."
    exit $ret
fi


# Just in case any other partitions on the device are mounted
# elsewhere....
for i in $(ls ${uDevice}? ) ; do
    # echo ${i}
    device=$(grep -i ${uDevice} /etc/mtab | cut -f1 -d' ')
    # echo device is ${device}
    if [ ! -z ${device} ] ; then
        # echo ${device} is mounted.
        umount ${device}
    fi
done

# flush the caches and spin down. Once DeviceKit is available, use
# "devkit-disks --detach $uDevice"
sdparm --command=sync ${uDevice} > /dev/null
sdparm --command=stop ${uDevice} > /dev/null

# Show the date so we can note it on the external drive.
date +%Y/%m/%d

blogroll

social