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 ## ## Requirements ##
* **cryptsetup**: Userspace setup tool for transparent encryption of block devices using. * **cryptsetup**
* **util-linux**: Miscellaneous system utilities for Linux, required for **losetup** and **mkfs**. * **util-linux**:
* **losetup**
* **mkfs**
* **coreutils**:
* **dd**
## Usage ## ## Usage ##
* `cryptobox`: displays the list of commands * **create**: `c|-c|--create [filename] [filesystem] [size-in-mb]`
* `cryptobox c filename.img filesystem size-in-mb`: creates an image file with a given filesystem and size in megabytes. * **mount**: `m|-m|--mount [filename] [mountpoint]`
* eg: `cryptobox c myimg.img ext4 128` * **umount**: `u|-u|--unmount [mountpoint]`
* `cryptobox m filename.img /mount/point`: mounts a given image file on a given directory. * **help**: `h|-h|--help`
* eg: `cryptobox m myimg.img /mnt/cryptoimg`
* `cryptobox u /mount/point`: unmounts an image file from a given directory.
* eg: `cryptobox u /mnt/cryptoimg`
## Credits ## ## 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 ## ## License ##

314
cryptobox
View file

@ -1,87 +1,265 @@
#!/usr/bin/env bash #!/usr/bin/env bash
############################################################## #
# # # CryptoBox
# CryptoBox # #
# # # A script that makes it easy to create, mount
# A script that wraps cryptsetup, mkfs and losetup to make # # and unmount encrypted images in Linux using LUKS
# it easy to create, mount and unmount encrypted image # #
# files 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 { # The name of the script
echo -e "Error: $1" scriptname="${0/*\/}"
[[ "$1" = "syntax" ]] && (echo; usage)
# 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 exit 1
} }
function usage { function luks_open {
echo -e "${NAME}: create and mount encrypted images\n" loopdev="$1"
echo "Usage: $NAME option arguments" container="$2"
echo -e "\tc (create) -> $NAME c filename.img filesystem size-in-mb"
echo -e "\tm (mount) -> $NAME m filename.img /mount/point" # Decrypt the image so we can use it
echo -e "\tu (umount) -> $NAME u /mount/point" 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
} }
function luks_close {
loopdev="$1"
container="$2"
# Close the encrypted device
cryptsetup luksClose "$container" || error_exit "Unable to close the decrypted device for ${c_m}$container"
sleep 1
# Close the loop device
losetup -d "$loopdev" || error_exit "Unable to close the loop device ${c_m}$loopdev"
}
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
# 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 # Check for root
[[ "$UID" -ne 0 ]] && errorquit "run with root permission\n" (( UID == 0 )) || error_exit 'Must be run with root permissions'
# Check dependencies # Dependencies
[[ `type -P dd` ]] || errorquit "Error: The 'dd' program is missing" deps=('dd' 'losetup' 'cryptsetup' 'mkfs')
[[ `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"
# Load modules if they aren't present # Check for missing dependencies
[[ `lsmod | grep loop` ]] || echo "loading 'loop' module"; modprobe loop || errorquit "Error: failed to load 'loop' module" declare -a missing_deps=()
[[ `lsmod | grep dm_mod` ]] || echo "loading 'dm_mod' module"; modprobe dm_mod || errorquit "Error: failed to load 'dm_mod' module"
if [ -z "$1" ]; then for dep in "${deps[@]}"; do
usage; exit 1 type -P "$dep" >/dev/null \
elif [ ! "$1" = "c" -a ! "$1" = "m" -a ! "$1" = "u" ]; then || missing_deps=( "${missing_deps[@]}" "$dep" )
errorquit "syntax" done
fi
case "$1" in [[ -n "${missing_deps[*]}" ]] && {
c) error_exit "${c_w}missing dependencies ($(
if [ -z "$2" -o -z "$3" -o -z "$4" ]; then errorquit "syntax"; fi for (( x=0; x < ${#missing_deps[@]}; x++ )); do
[[ -f "$2" ]] && errorquit "$2 already exists" printf '%s' "$c_m${missing_deps[$x]}$c_c"
LOOPDEV=`losetup -f` (( (( x + 1 )) < ${#missing_deps[@]} )) && printf '%s' ', '
CONTAINER=`echo "$2" | sed s/"[^\/]*\/"//g | sed s/"\.".*$//g` done
dd bs=1M count="$4" if=/dev/urandom of="$2" || errorquit "couldn't create create image file" )$c_w)"
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" # Load the required modules if not already available
mkfs -t "$3" "/dev/mapper/${CONTAINER}" || errorquit "mkfs failed for filesystem type: $3" modules=('loop' 'dm_mod')
cryptsetup luksClose "$CONTAINER" || errorquit "couldn't close encryption for $CONTAINER"
sleep 1 for module in "${modules[@]}"; do
losetup -d "$LOOPDEV" || errorquit "couldn't close loop device (${LOOPDEV})" 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 m|-m|--mount)
[[ ! -f "$2" ]] && errorquit "$2 does not exist" mount_image "$@"
[[ -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"
;; ;;
u)
if [ -z "$2" ]; then errorquit "syntax"; fi u|-u|--unmount)
MOUNT=`mount | grep $(echo "$2" | sed s/"\/"$//)` unmount_image "$@"
[[ -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})"
;; ;;
generic)
usage h|-h|--help)
usage 0
;;
*)
usage 1
;; ;;
esac esac