Rewrite the script using best practices, way more error checks, nice colour output and an updated encryption algorithm for new images

This commit is contained in:
Kevin MacMartin 2020-11-11 00:56:37 -05:00
parent f32decc0da
commit 4376e91b94
2 changed files with 262 additions and 80 deletions

View file

@ -1,25 +1,29 @@
# Cryptobox #
# Cryptobox
A wrapper script for **cryptsetup** that makes it easy to create, mount and unmount encrypted image files using LUKS.
A script that makes it easy to create, mount and unmount encrypted images in Linux using LUKS
## Requirements ##
* **cryptsetup**: Userspace setup tool for transparent encryption of block devices using.
* **util-linux**: Miscellaneous system utilities for Linux, required for **losetup** and **mkfs**.
* **cryptsetup**
* **util-linux**:
* **losetup**
* **mkfs**
* **coreutils**:
* **dd**
## Usage ##
* `cryptobox`: displays the list of commands
* `cryptobox c filename.img filesystem size-in-mb`: creates an image file with a given filesystem and size in megabytes.
* eg: `cryptobox c myimg.img ext4 128`
* `cryptobox m filename.img /mount/point`: mounts a given image file on a given directory.
* eg: `cryptobox m myimg.img /mnt/cryptoimg`
* `cryptobox u /mount/point`: unmounts an image file from a given directory.
* eg: `cryptobox u /mnt/cryptoimg`
* **create**: `c|-c|--create [filename] [filesystem] [size-in-mb]`
* **mount**: `m|-m|--mount [filename] [mountpoint]`
* **umount**: `u|-u|--unmount [mountpoint]`
* **help**: `h|-h|--help`
## Credits ##
Written by Kevin MacMartin: [GitHub Projects](https://github.com/prurigro?tab=repositories) | [Arch Linux AUR Packages](https://aur.archlinux.org/packages/?SeB=m&K=prurigro)
Written by Kevin MacMartin
* [GitHub Projects](https://github.com/prurigro?tab=repositories)
* [Arch Linux AUR Packages](https://aur.archlinux.org/packages/?SeB=m&K=prurigro)
## License ##

314
cryptobox
View file

@ -1,87 +1,265 @@
#!/usr/bin/env bash
##############################################################
# #
# CryptoBox #
# #
# A script that wraps cryptsetup, mkfs and losetup to make #
# it easy to create, mount and unmount encrypted image #
# files using LUKS. #
# #
##############################################################
#
# CryptoBox
#
# A script that makes it easy to create, mount
# and unmount encrypted images in Linux using LUKS
#
# Written by Kevin MacMartin
#
# Licensed under the GPLv3
#
NAME=`echo "$0" | grep -o -e "[^\/]*$"`
[[ -t 1 ]] && {
c_d=$'\e[1;30m' # DARK GREY
c_r=$'\e[1;31m' # RED
c_g=$'\e[1;32m' # GREEN
c_y=$'\e[1;33m' # YELLOW
c_b=$'\e[1;34m' # BLUE
c_m=$'\e[1;35m' # VIOLET
c_t=$'\e[1;36m' # TEAL
c_w=$'\e[1;37m' # WHITE
c_u=$'\e[1;4;37m' # UNDERLINE WHITE
c_h=$'\e[1;41m' # HIGHLIGHT RED
c_c=$'\e[0m' # DISABLES COLOUR
}
function errorquit {
echo -e "Error: $1"
[[ "$1" = "syntax" ]] && (echo; usage)
# The name of the script
scriptname="${0/*\/}"
# Display uage information
function usage {
printf '%s\n\n' "${c_w}Create, mount and unmount encrypted images${c_c}"
printf '%s\n' "${c_w}Usage:${c_c}"
printf ' %s\n' "${c_y}create${c_w}: ${c_m}c${c_w}|${c_m}-c${c_w}|${c_m}--create${c_c} ${c_w}$scriptname${c_c} ${c_m}c${c_c} [${c_b}filename${c_c}] [${c_g}filesystem${c_c}] [${c_t}size-in-mb${c_c}]"
printf ' %s\n' "${c_y}mount${c_w}: ${c_m}m${c_w}|${c_m}-m${c_w}|${c_m}--mount${c_c} ${c_w}$scriptname${c_c} ${c_m}m${c_c} [${c_b}filename${c_c}] [${c_r}mountpoint${c_c}]"
printf ' %s\n' "${c_y}umount${c_w}: ${c_m}u${c_w}|${c_m}-u${c_w}|${c_m}--unmount${c_c} ${c_w}$scriptname${c_c} ${c_m}u${c_c} [${c_r}mountpoint${c_c}]"
printf ' %s\n' "${c_y}help${c_w}: ${c_m}h${c_w}|${c_m}-h${c_w}|${c_m}--help${c_c} ${c_w}$scriptname${c_c} ${c_m}h${c_c}"
exit "$1"
}
# Exit with a nicely formatted error
function error_exit {
printf '%s\n' "${c_r}Error${c_c}: ${c_w}$1${c_c}" >&2
exit 1
}
function usage {
echo -e "${NAME}: create and mount encrypted images\n"
echo "Usage: $NAME option arguments"
echo -e "\tc (create) -> $NAME c filename.img filesystem size-in-mb"
echo -e "\tm (mount) -> $NAME m filename.img /mount/point"
echo -e "\tu (umount) -> $NAME u /mount/point"
function luks_open {
loopdev="$1"
container="$2"
# Decrypt the image so we can use it
while (( 1 )); do
if cryptsetup luksOpen "$loopdev" "$container"; then
break
else
printf '%s' "${c_w}Failed to decrypt ${c_m}$container${c_w}, press return to try again or ctrl+c to exit$c_c"
read -r
fi
done
}
# Check for root
[[ "$UID" -ne 0 ]] && errorquit "run with root permission\n"
function luks_close {
loopdev="$1"
container="$2"
# Check dependencies
[[ `type -P dd` ]] || errorquit "Error: The 'dd' program is missing"
[[ `type -P losetup` ]] || errorquit "Error: The 'losetup' program is missing"
[[ `type -P cryptsetup` ]] || errorquit "Error: The 'cryptsetup' program is missing"
[[ `type -P mkfs` ]] || errorquit "Error: The 'mkfs' program is missing"
# Close the encrypted device
cryptsetup luksClose "$container" || error_exit "Unable to close the decrypted device for ${c_m}$container"
sleep 1
# Load modules if they aren't present
[[ `lsmod | grep loop` ]] || echo "loading 'loop' module"; modprobe loop || errorquit "Error: failed to load 'loop' module"
[[ `lsmod | grep dm_mod` ]] || echo "loading 'dm_mod' module"; modprobe dm_mod || errorquit "Error: failed to load 'dm_mod' module"
# Close the loop device
losetup -d "$loopdev" || error_exit "Unable to close the loop device ${c_m}$loopdev"
}
if [ -z "$1" ]; then
usage; exit 1
elif [ ! "$1" = "c" -a ! "$1" = "m" -a ! "$1" = "u" ]; then
errorquit "syntax"
function create_image {
# Exit with an error if exactly 3 arguments haven't been given
[[ -n "$3" && -z "$4" ]] || error_exit 'Incorrect number of arguments for the create command'
# Store the provided arguments as named variables so they're easier to keep track of
filename="$1"
filesystem="$2"
size="$3"
# Exit with an error if the image file already exists
[[ ! -e "$filename" ]] || error_exit "$filename already exists"
# Exit with an error if the filesystem isn't available
mkfs_binaries=("$(type -P mkfs)".*)
grep -q mkfs."$filesystem" <<< "${mkfs_binaries[*]}" || error_exit "The filesystem ${c_m}$filesystem${c_w} is not available"
# Exit with an error if size isn't a number
[[ "$size" =~ ^[0-9]*$ ]] || error_exit 'The argument for the size is not a number'
# Exit with an error if size is less than 18
(( size >= 18 )) || error_exit 'The size of the image must be 18 megabytes or larger'
# Retrieve the first unused loop device name
loopdev=$(losetup -f)
# Retrieve an appropriate name for the container
container="${loopdev/*\/}"
# Exit with an error if a container already exists with this name
[[ ! -e "/dev/mapper/$container" ]] || error_exit "A container already exists at ${c_m}/dev/mapper/$container"
# Create the image file at the requested size and filled with random data
dd bs=1M count="$size" if=/dev/urandom of="$filename" || error_exit "Unable to create ${c_m}$filename"
# Setup the loop device
losetup "$loopdev" "$filename" || error_exit "Unable to connect ${c_m}$filename${c_w} to the loop device ${c_m}$loopdev"
# Initialize encryption on the image
while (( 1 )); do
if cryptsetup -c aes-xts-plain64 -y -s 512 luksFormat "$loopdev"; then
break
else
printf '%s' "${c_w}Failed to encrypt the image, press return to try again or ctrl+c to exit$c_c"
read -r
fi
done
case "$1" in
c)
if [ -z "$2" -o -z "$3" -o -z "$4" ]; then errorquit "syntax"; fi
[[ -f "$2" ]] && errorquit "$2 already exists"
LOOPDEV=`losetup -f`
CONTAINER=`echo "$2" | sed s/"[^\/]*\/"//g | sed s/"\.".*$//g`
dd bs=1M count="$4" if=/dev/urandom of="$2" || errorquit "couldn't create create image file"
losetup "$LOOPDEV" "$2" || errorquit "couldn't setup loop device (${LOOPDEV})"
cryptsetup -c aes-xts-plain -y -s 512 luksFormat "$LOOPDEV" || errorquit "couldn't encrypt image file"
cryptsetup luksOpen "$LOOPDEV" "$CONTAINER" || errorquit "couldn't decrypt $CONTAINER"
mkfs -t "$3" "/dev/mapper/${CONTAINER}" || errorquit "mkfs failed for filesystem type: $3"
cryptsetup luksClose "$CONTAINER" || errorquit "couldn't close encryption for $CONTAINER"
sleep 1
losetup -d "$LOOPDEV" || errorquit "couldn't close loop device (${LOOPDEV})"
# Decrypt the image so we can use it
luks_open "$loopdev" "$container"
# Initialize the target filesystem on the device
mkfs -t "$filesystem" "/dev/mapper/${container}" || error_exit "Unable to create the ${c_m}$filesystem${c_w} filesystem"
sync
# Close the decrypted image
luks_close "$loopdev" "$container"
}
function mount_image {
# Exit with an error if exactly 3 arguments haven't been given
[[ -n "$2" && -z "$3" ]] || error_exit 'Incorrect number of arguments for the create command'
# Store the provided arguments as named variables so they're easier to keep track of
filename="$1"
mountpoint="$2"
# Exit with an error if either the image file or mount point do not exist
[[ ! -f "$filename" ]] && error_exit "${c_m}$filename${c_w} does not exist"
[[ -e "$mountpoint" ]] || error_exit "${c_m}$mountpoint${c_w} does not exist"
[[ -d "$mountpoint" ]] || error_exit "${c_m}$mountpoint${c_w} is not a directory"
# Retrieve the first unused loop device name
loopdev=$(losetup -f)
# Retrieve an appropriate name for the container
container="${loopdev/*\/}"
# Exit with an error if a container already exists with this name
[[ ! -e "/dev/mapper/$container" ]] || error_exit "A container already exists at ${c_m}/dev/mapper/$container"
# Setup the loop device
losetup "$loopdev" "$filename" || error_exit "Unable to connect ${c_m}$filename${c_w} to the loop device ${c_m}$loopdev${c_w}"
# Decrypt the image so we can use it
luks_open "$loopdev" "$container"
# Mount the mapped decrypted image
mount "/dev/mapper/$container" "$mountpoint" || error_exit "Unable to mount ${c_m}/dev/mapper/${container}${c_w} on ${c_m}$mountpoint"
}
function unmount_image {
# Exit with an error if exactly 3 arguments haven't been given
[[ -n "$1" && -z "$2" ]] || error_exit 'Incorrect number of arguments for the create command'
# Store the the absolute path of the mount point
mountpoint="$(readlink -f "$1")"
# Exit with an error if the mount point does not exist or isn't a directory
[[ -e "$mountpoint" ]] || error_exit "${c_m}$mountpoint${c_w} does not exist"
[[ -d "$mountpoint" ]] || error_exit "${c_m}$mountpoint${c_w} is not a directory"
# Check the list of mounts for the mount point
mount=$(grep "$mountpoint" /proc/mounts)
# Exit with an error if the mount point isn't in the list of mounts
[[ -n "$mount" ]] || error_exit "${c_m}$mountpoint${c_w} is not mounted"
# Retrieve the name of the container
container_path="${mount/ *}"
# Retrieve the loop device associated with the mount
loopdev="${container_path/*\//\/dev\/}"
# Exit with an error if the container_path doesn't exist
[[ -e "$container_path" ]] || error_exit "The mount does not appear to be a decrypted container"
# Unmount the mount point
umount "$mountpoint" || error_exit "Unable to unmount ${c_m}$mountpoint"
# Close the decrypted image
luks_close "$loopdev" "${container_path/*\/}"
}
# Exit with an error on ctrl-c
trap 'error_exit "$scriptname has been killed"' SIGINT SIGQUIT
# Check for root
(( UID == 0 )) || error_exit 'Must be run with root permissions'
# Dependencies
deps=('dd' 'losetup' 'cryptsetup' 'mkfs')
# Check for missing dependencies
declare -a missing_deps=()
for dep in "${deps[@]}"; do
type -P "$dep" >/dev/null \
|| missing_deps=( "${missing_deps[@]}" "$dep" )
done
[[ -n "${missing_deps[*]}" ]] && {
error_exit "${c_w}missing dependencies ($(
for (( x=0; x < ${#missing_deps[@]}; x++ )); do
printf '%s' "$c_m${missing_deps[$x]}$c_c"
(( (( x + 1 )) < ${#missing_deps[@]} )) && printf '%s' ', '
done
)$c_w)"
}
# Load the required modules if not already available
modules=('loop' 'dm_mod')
for module in "${modules[@]}"; do
modinfo "$module" >/dev/null 2>&1 || {
printf '%s ' "${c_w}Loading module: ${c_m}$module${c_w}..."
if modprobe "$module" >/dev/null 2>&1; then
printf '%s\n' "${c_g}done!${c_c}"
else
printf '%s\n' "${c_r}failed!${c_c}"
error_exit "Unable to load module ${c_m}$module"
fi
}
done
# Store the option and then drop it from the list of arguments
option="$1"
shift
# Run the appropriate function based on the provided option
case "$option" in
c|-c|--create)
create_image "$@"
;;
m)
if [ -z "$2" -o -z "$3" ]; then errorquit "syntax"; fi
[[ ! -f "$2" ]] && errorquit "$2 does not exist"
[[ -d "$3" ]] || errorquit "$3 does not exist"
LOOPDEV=$(losetup -f)
CONTAINER=$(echo "$LOOPDEV" | sed s/"[^\/]*\/"//g | sed s/"\.".*$//g)
losetup "$LOOPDEV" "$2" || errorquit "couldn't setup loop device (${LOOPDEV})"
cryptsetup luksOpen "$LOOPDEV" "$CONTAINER" || errorquit "couldn't decrypt $CONTAINER"
mount "/dev/mapper/${CONTAINER}" "$3" || errorquit "couldn't mount /dev/mapper/${CONTAINER} on $3"
m|-m|--mount)
mount_image "$@"
;;
u)
if [ -z "$2" ]; then errorquit "syntax"; fi
MOUNT=`mount | grep $(echo "$2" | sed s/"\/"$//)`
[[ -z "$MOUNT" ]] && errorquit "$2 is not mounted"
LOOPDEV=`echo "$MOUNT" | sed s/\ .*//g | sed s/"\/mapper"//`
CONTAINER=`echo "$LOOPDEV" | sed s/"[^\/]*\/"//g | sed s/"\.".*$//g`
umount "$2" || errorquit "Couldn't unmount $2"
cryptsetup luksClose "$CONTAINER" || errorquit "couldn't close encryption for $CONTAINER"
sleep 1
losetup -d "$LOOPDEV" || errorquit "couldn't close loop device (${LOOPDEV})"
u|-u|--unmount)
unmount_image "$@"
;;
generic)
usage
h|-h|--help)
usage 0
;;
*)
usage 1
;;
esac