Skip to content

Instantly share code, notes, and snippets.

@nerun
Last active November 23, 2025 13:55
Show Gist options
  • Select an option

  • Save nerun/cc053edec6715421c2ddd2aec77c6580 to your computer and use it in GitHub Desktop.

Select an option

Save nerun/cc053edec6715421c2ddd2aec77c6580 to your computer and use it in GitHub Desktop.
Keeper of Mythos, a graphical frontend for the basic commands of cryptsetup / dm-crypt / LUKS for creating encrypted containers in files.
#!/bin/bash
#
# keeper.sh - version 2 - November 23, 2025
# Copyright (c) 2025 Daniel Dias Rodrigues. No rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the Creative Commons Zero 1.0 Universal (CC0 1.0) Public
# Domain Dedication (https://creativecommons.org/publicdomain/zero/1.0/).
#
readme="# Keeper of Mythos
# Copyright (c) 2025 Daniel Dias Rodrigues. No rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the Creative Commons Zero 1.0 Universal (CC0 1.0) Public
# Domain Dedication (https://creativecommons.org/publicdomain/zero/1.0/).
Welcome to your Tome of Arcane Lore!
This secure storage space was created using Keeper of Mythos, a graphical
frontend for managing encrypted containers on Linux.
## What is this?
This is an encrypted, rewritable disk image file that functions as a secure
digital vault. You can store confidential files, documents, or any data you
wish to keep secret.
## Technical Details
- **Encryption**: LUKS2 (Linux Unified Key Setup)
- **File System**: ext4
- **Access Method**: Password-based encryption
## How to Use
1. Use Keeper of Mythos or cryptsetup tools to open this container
2. Remember to properly close/seal the container after use
## Security Notes
- Keep your password safe and secure. The security of your data depends on
keeping your password confidential.
- Always close/seal the container when not in use.
- Consider backing up important files stored here.
- The container file itself should be stored in a safe location.
## About
I created this frontend purely as a fun exercise to simplify the basics of
creating and managing encrypted containers.
It covers the essential creation, opening, and closing commands present in
cryptsetup. If you need a more capable frontend, I recommend \"Tomb, the
Crypto Undertaker\" <https://www.dyne.org/software/tomb>.
Since this is a frontend for cryptsetup, you can use all cryptsetup commands
to manage this container, including those not covered in this tool, such as
\"luksAddKey\" and others.
The \".tome\" extension is arbitrary and could be replaced with \".img\" or any
other extension.
## The Joke Explained
The names follow a Cthulhu/Lovecraftian theme:
- \"Keeper of Mythos\" alludes both to the Game Master and to Cthulhu Mythos in
Chaosium's Call of Cthulhu RPG
- A \"tome\" is an ancient book of spells and arcane knowledge
- The password is a \"secret spell\"
- Entering the password is \"casting the spell\" to open the magically sealed
tome
- \"Sealing\" the tome means unmounting and closing (re-encrypting) it
May your secrets remain forever hidden from prying eyes!"
# ---------------------------------------------------------------------------
# Superuser Privileges Required
authenticate_sudo() {
local password
local attempts=0
local max_attempts=3
while [ $attempts -lt $max_attempts ]; do
# Show the zenity window to request a password.
password=$(zenity --password --title="Superuser Privileges Required")
# If the user canceled
if [ $? -ne 0 ] || [ -z "$password" ]; then
zenity --error --text="Authentication canceled. The script will be terminated."
exit 1
fi
# Try authenticating with sudo
if echo "$password" | sudo -S -v 2>/dev/null; then
# Success - keep sudo cache updated in the background
(
while true; do
sleep 60
sudo -n true 2>/dev/null || break
done
) &
zenity --info --text="Authentication successful!" --timeout=2
return 0
else
attempts=$((attempts + 1))
remaining=$((max_attempts - attempts))
if [ $remaining -gt 0 ]; then
zenity --error --text="Incorrect password. Remaining attempts: $remaining"
else
zenity --error --text="Maximum number of attempts exceeded. Script terminated."
exit 1
fi
fi
done
}
# ---------------------------------------------------------------------------
show_progress() {
zenity --progress \
--title="Keeper of Mythos" \
--text="Inscribing arcane seals... Please wait." \
--pulsate \
--auto-close \
--no-cancel
}
# ---------------------------------------------------------------------------
generate_mapper_name() {
local base mapper n
base=$(basename "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g')
mapper="$base"
# If exists, add -1, -2, ...
n=1
while [ -e "/dev/mapper/$mapper" ]; do
mapper="${base}-$n"
n=$((n + 1))
done
echo "$mapper"
}
# ---------------------------------------------------------------------------
scribe_tome() {
local file size pass
file=$(zenity --file-selection --save --title="Scribe Tome")
[ -z "$file" ] && return
size=$(zenity --entry --title="Tome Size" --text="Enter tome size (e.g. 50M, 2G):")
[ -z "$size" ] && return
pass=$(zenity --password --title="Inscribe a Secret Spell")
[ -z "$pass" ] && return
(
truncate -s "$size" "$file"
echo -n "$pass" | sudo cryptsetup --batch-mode --key-file=- luksFormat --type luks2 "$file"
echo -n "$pass" | sudo cryptsetup --batch-mode --key-file=- open "$file" tome_tmp
sudo mkfs.ext4 /dev/mapper/tome_tmp
sudo mkdir -p /mnt/tome_tmp
sudo mount /dev/mapper/tome_tmp /mnt/tome_tmp
sudo chown "$USER": /mnt/tome_tmp
echo "$readme" > /mnt/tome_tmp/README.txt
sudo umount /mnt/tome_tmp
sudo rm -rf /mnt/tome_tmp
sudo cryptsetup close tome_tmp
) | show_progress
zenity --info --title="Keeper" --text="Tome created successfully."
}
# ---------------------------------------------------------------------------
open_tome() {
local file mapper pass
file=$(zenity --file-selection --title="Open Tome")
[ -z "$file" ] && return
mapper=$(generate_mapper_name "$file")
pass=$(zenity --password --title="Cast Secret Spell")
[ -z "$pass" ] && return
echo -n "$pass" | sudo cryptsetup --batch-mode --key-file=- open "$file" "$mapper"
success=$?
sudo mkdir -p /mnt/"$mapper"
sudo mount /dev/mapper/"$mapper" /mnt/"$mapper"
if [ $success -eq 0 ]; then
zenity --info --title="Keeper" --text="Tome opened as:\n /dev/mapper/$mapper\nand mounted as:\n /mnt/$mapper"
else
zenity --error --title="Keeper" --text="Failed to open tome. Wrong spell?"
fi
}
# ---------------------------------------------------------------------------
seal_tome() {
local list choice
# List all open tomes (those ending in -tome*)
list=$(ls /dev/mapper | grep -E 'tome($|-)' || true)
if [ -z "$list" ]; then
zenity --info --title="Keeper" --text="There are no open tomes."
return
fi
choice=$(zenity --list \
--title="Seal Tome" \
--text="Select a tome to close:" \
--column="Open Tomes" \
$list)
[ -z "$choice" ] && return
sudo umount /mnt/"$choice"
sudo rm -rf /mnt/"$choice"
sudo cryptsetup close "$choice"
zenity --info --title="Keeper" --text="Tome sealed."
}
# ---------------------------------------------------------------------------
main_menu() {
local choice
while true; do
choice=$(zenity --list \
--title="Keeper of Mythos" \
--text="Choose an action:" \
--column="Action" \
"Scribe Tome" \
"Open Tome" \
"Seal Tome")
# If Cancel or window closed
if [ -z "$choice" ]; then
exit 0
fi
case "$choice" in
"Scribe Tome") scribe_tome ;;
"Open Tome") open_tome ;;
"Seal Tome") seal_tome ;;
esac
done
}
# ---------------------------------------------------------------------------
authenticate_sudo
main_menu
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment