1
0
.dotfiles/home/bin/luksvault

191 lines
5.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""luksvault: Encrypted Disk Images via Luks.
This script makes it easy to set up, mount and unmount encrypted disk images.
It uses an ext4 filesystem by default; this can be overridden with --mkfs,
for example `--mkfs mkfs.vfat`
Usage: luksvault /path/to/disk.img /mnt/point
If the image doesn't exist, you'll be prompted to set it up. If it does exist,
it will be mounted. The setup phase takes the longest and asks for passwords the
most often, but once set up you'll only need to enter the disk image password
(and potentially your sudo password).
To unmount: luksvault -u /path/to/disk.img /mnt/point
See `luksvault --help` for additional options.
--Kirsle
http://sh.kirsle.net/
"""
import sys
import os
import argparse
import logging
import subprocess
logging.basicConfig(format="[%(levelname)s] %(message)s")
console = logging.getLogger("luksvault")
console.setLevel(logging.INFO)
def main(args):
if args.debug:
console.setLevel(logging.DEBUG)
# Test for cryptsetup.
if subprocess.call("which cryptsetup >/dev/null 2>&1", shell=True) != 0:
print("You require cryptsetup to use this script.")
sys.exit(1)
# Get the base name of the file. We remove any file extensions from it,
# and strip dots from it (so if they point to a dotfile like ~/.vault.img
# the basename becomes "vault")
console.debug("File path given via CLI: {}".format(args.image))
basename = os.path.basename(args.image).strip(".").split(".")[0]
console.debug("Base name: {}".format(basename))
if len(basename) == 0:
print("Bad file name for disk image.")
# Mount point exists?
if not os.path.isdir(args.mount):
console.error("Mount point does not exist: {}".format(args.mount))
sys.exit(1)
# Mapper name?
mapper = "luksvault-{}".format(basename)
if args.name:
mapper = args.name
# Does the image exist?
if not os.path.isfile(args.image):
init_image(args, mapper)
sys.exit(0)
# Unmounting it?
if args.unmount:
unmount(args, mapper)
sys.exit(0)
# Mount it.
mount(args, mapper)
sys.exit(0)
def init_image(args, mapper):
"""Initialize the disk image."""
print("The disk image {} does not yet exist.".format(args.image))
answer = input("Create it? [yN] ")
if answer != "y":
sys.exit(1)
# Do we have a size?
size = args.size
if size is None:
try:
size = int(
input("How big do you want the image to be, in MB? ")
)
except:
print("Invalid size; a number was expected.")
sys.exit(1)
# Create it quickly?
if args.fast:
print("Creating fast disk image via fallocate...")
subprocess.call(["fallocate", "-l", "{}M".format(size), args.image])
else:
print("Creating disk image from /dev/urandom...")
subprocess.call(["dd", "if=/dev/urandom",
"of={}".format(args.image),
"bs=1M",
"count={}".format(size)])
# Set up Luks
print("Setting up the Luks format on this disk image...")
subprocess.call(["cryptsetup", "-y", "luksFormat", args.image])
print("REMEMBER: If you lose your password, you won't be able to recover "
"it!")
print("Opening the disk image with Luks and formatting it...")
subprocess.call(["sudo", "cryptsetup", "luksOpen", args.image, mapper])
if not os.path.exists("/dev/mapper/{}".format(mapper)):
console.error("The file /dev/mapper/{} isn't there like I "
"expected!".format(mapper))
sys.exit(1)
subprocess.call("sudo {} /dev/mapper/{}".format(args.mkfs, mapper),
shell=True)
# Mount it.
mount(args, mapper, skip_luks=True)
def mount(args, mapper, skip_luks=False):
"""Mount it."""
# Luks setup.
if not skip_luks:
subprocess.call(["sudo", "cryptsetup", "luksOpen", args.image, mapper])
# Mount it.
subprocess.call(["sudo", "mount",
"/dev/mapper/{}".format(mapper), args.mount])
def unmount(args, mapper):
"""Unmount and tear down Luks."""
print("Unmounting encrypted volume...")
subprocess.call(["sudo", "umount", args.mount])
print("Closing the LUKS context...")
subprocess.call(["sudo", "cryptsetup", "luksClose", mapper])
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="luksvault")
parser.add_argument(
"--unmount", "-u",
action="store_true",
help="Unmount the volume.",
)
parser.add_argument(
"--name", "-n",
type=str,
help="The mapper name to use for Luks. Default name is 'luksvault-"
"<basename-of-file>'",
)
parser.add_argument(
"--size", "-s",
type=int,
help="Size of the disk image, in megabytes (e.g. 1024)."
)
parser.add_argument(
"--fast",
action="store_true",
help="Create the disk image quickly (fallocate), but insecurely. "
"It's recommended NOT to use this option (the default is to fully "
"allocate the disk image from /dev/urandom).",
)
parser.add_argument(
"--mkfs",
type=str,
default="mkfs.ext4 -j",
help="The mkfs command prefix you want to use with the encrypted "
"disk image (default is: mkfs.ext4 -j)",
)
parser.add_argument(
"--debug",
action="store_true",
help="Enable debug mode (verbose logging).",
)
parser.add_argument(
"image",
type=str,
help="Path to disk image file.",
)
parser.add_argument(
"mount",
type=str,
help="Path to mount the disk image to.",
)
args = parser.parse_args()
main(args)