Build Your Own Secure Shell-Based Password Manager

devopsshellterminalscriptsbash

Friday, June 28, 2024

Hey there!

Are you tired of juggling a bunch of passwords and looking for a simple, hands-on solution? Why not craft your own shell-based password manager? It’s less daunting than it sounds, and it gives you full control over your password management, ensuring your credentials stay snug and secure, right where you need them.

Why Go DIY with Your Password Manager?

Opting for a shell-based password manager means embracing simplicity and security. Shell scripts are lean, mean, and run just about anywhere without fuss. They let you tailor your password vault without depending on anyone else’s software. Plus, you get the satisfaction of saying, “I built that!”

Getting Started: Set Up the Stage

  1. Create Your Plain Text File: Start by creating a plain text file where your passwords will temporarily live before encryption.
touch passwords.txt

2. Encrypt Your Password File: Use OpenSSL to encrypt your passwords.txt into passwords.enc using a secure PIN.

openssl enc -aes-256-cbc -pbkdf2 -in passwords.txt -out passwords.enc -pass pass:YOUR_PIN_HERE
rm -f passwords.txt  # Make sure to remove the plain text file after encryption

3. Write the pwd_mgr.sh Script: Now, let’s create the script that will manage this encrypted file. Open a new script file:

touch pwd_mgr.sh
chmod +x pwd_mgr.sh  # Make the script executable
nano pwd_mgr.sh  # Use any editor you prefer

Inside pwd_mgr.sh: Add the Logic

Paste the following into your pwd_mgr.sh script. This is your password manager:

#!/bin/bash

if [[ "$SESSION_UNLOCKED" != "true" ]]; then
    read -sp "Enter your PIN to unlock the session: " PIN
    echo
fi

# File variables
encrypted_file="passwords.enc"
plain_file="passwords.txt"  

# Decrypt the password file
decrypt_file() {
    if [ -e "$encrypted_file" ]; then
        decryption_output=$(openssl enc -aes-256-cbc -d -pbkdf2 -in "$encrypted_file" -out "$plain_file" -pass pass:$PIN 2>&1)
        if echo "$decryption_output" | grep -q "bad decrypt"; then
            echo "Wrong PIN. Exiting."
            exit 1
        else
            export SESSION_UNLOCKED="true"
            export PIN=$PIN  
        fi
    else
        touch "$plain_file"
    fi
}

# Encrypt the plain password file
encrypt_file() {
    openssl enc -aes-256-cbc -pbkdf2 -in "$plain_file" -out "$encrypted_file" -pass pass:$PIN
    rm "$plain_file"
}

# Decrypt the encrypted password file
decrypt_file

# Function to add a password
add_password() {
    read -p "Enter the account name: " account
    read -sp "Enter the password: " password
    echo "$account: $password" >> "$plain_file"
    clear
    echo "Password added for $account."
    echo
}

# Function to determine the clipboard command based on the OS
get_clipboard_command() {
    case "$(uname)" in
        "Linux")
            echo "xclip -selection clipboard"  # Debian-based systems like Ubuntu
            ;;
        "Darwin")
            echo "pbcopy"  # macOS uses pbcopy
            ;;
        *)
            echo "Unsupported OS for clipboard copying"
            ;;
    esac
}

# Function to retrieve a password and copy it to the clipboard
get_password() {
    read -p "Enter the account name: " account
    password=$(grep "^$account:" "$plain_file" | cut -d ":" -f2)
    if [ -n "$password" ]; then
        clear
        echo "Password for $account: $password"
        # Determine the correct clipboard command and use it
        clipboard_cmd=$(get_clipboard_command)
        if [ "$clipboard_cmd" = "Unsupported OS for clipboard copying" ]; then
            echo "Clipboard copying is not supported on this OS."
        else
            echo -n "$password" | $clipboard_cmd
            echo "Password copied to clipboard."
        fi
    else
        echo "Password not found for $account."
    fi
    echo
}

# Function to list all users
list_users() {
    clear
    echo "Current users:"
    cut -d ":" -f1 "$plain_file"
    echo
}

# Function to quit the script
quit() {
    encrypt_file
    # check if script is source or not if not reload bash to save changes like "export SESSION_UNLOCKED" for session managing
    if [ "$0" = "$BASH_SOURCE" ]; then
        $SHELL
    fi
    exit 0
}

# Handle script exit, interruptions and Ctrl+C
trap quit SIGINT

# Main menu
while true; do
    echo
    echo "ShellGuard Menu:"
    echo "1. Add a new password"
    echo "2. Retrieve a password"
    echo "3. List all users"
    echo "4. Quit"

    read -p "Choose an option (1-4): " choice
    case $choice in
        1) add_password;;
        2) get_password;;
        3) list_users;;
        4) quit;;
        *) echo "Invalid option";;
    esac
done

How Your New Tool Works

Once you kick off your script, it’ll ask for a PIN — think of it as the key to your digital diary. Punch it in, and you’re presented with a simple menu to navigate your options like adding a new password or fetching an old one. Everything’s tucked away in an encrypted file that only your script can open.

Jump In and Make It Yours

Got an idea to improve it? Found a bug? Dive into the code, tweak it, and make it even better. The shell script community is full of folks who love to share, learn, and help. Don’t hesitate to reach out and contribute your improvements.