Last active
November 23, 2025 13:55
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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