Skip to content

Instantly share code, notes, and snippets.

@mikkorantalainen
Created March 10, 2023 08:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikkorantalainen/0be0a4682369772694f8acd755481b0d to your computer and use it in GitHub Desktop.
Save mikkorantalainen/0be0a4682369772694f8acd755481b0d to your computer and use it in GitHub Desktop.
Bash script to create full Bitwarden vault backup on Ubuntu host, backup file will be saved as AES-256 encrypted ZIP file
#!/bin/bash
#
# Backup Bitwarden vault to AES-256 encrypted ZIP file
# Copyright 2023 Mikko Rantalainen <mikko.rantalainen@iki.fi>
# License: MIT
#
set -o errexit
DATE="$(date +%Y-%m-%d)"
ZIPFILENAME="$(pwd)/bitwarden-backup-$DATE.zip"
echo
echo "Backup Bitwarden vault to AES-256 encrypted ZIP file:"
echo
echo "Using following binaries to create the Bitwarden backup:"
which expect # apt install expect
which 7za # apt install p7zip-full
which bw # snap install bw
which cat # apt install coreutils
echo "If you don't trust these to handle your password, use Ctrl+C to abort."
echo
test -e "$ZIPFILENAME" && printf "Notice: %s will be overwritten!\n\n" "$ZIPFILENAME" || true
cd "/var/run/user/$UID"
printf "Changing to directory %s to create the sensitive files...\n" "$(pwd)"
FSTYPE="$(findmnt --target . -no FSTYPE)"
# verify that we're using storage in RAM only
test "$FSTYPE" = "tmpfs" || { echo "Error: expected filesystem to be tmpfs, got '$FSTYPE' instead, aborting." 2>&1; exit 1; }
mkdir "bitwarden-backup.$$"
chmod 700 "bitwarden-backup.$$"
cd "bitwarden-backup.$$"
echo
read -e -i "$EMAIL" -p "Enter Bitwarden login (email) > " EMAIL
read -p "Enter Bitwarden password for login $EMAIL > " -s BITWARDENPW
echo
echo
cat > password <<EOF
$BITWARDENPW
EOF
cat > expect.encrypt-backup <<EOF
set timeout 5
spawn 7za a -tzip -p -mem=AES256 bitwarden-backup.zip bitwarden-backup.json
expect "Enter password (will not be echoed):"
send -- "$BITWARDENPW\\r"
expect "Verify password (will not be echoed) :"
send -- "$BITWARDENPW\\r"
expect eof
EOF
BITWARDENPW="overwrite the existing password in the RAM before running any additional programs"
echo "Logging to Bitwarden..."
(bw status | grep -q userEmail) || (cat password | bw login "$EMAIL") >> output
echo "Unlocking and synchronizing the vault..."
cat password | bw unlock >> output
bw sync >> output
echo "Exporting the vault to JSON..."
cat password | bw export --format json --raw > bitwarden-backup.json
echo "Logging out from the Bitwarden..."
bw logout >> output
echo "Encrypting the JSON copy of the vault..."
# Compress and encrypt the file with AES-256 encryption.
# However, 7za doesn't want to read input from stdin so we have to use
# expect script to emulate end user entering the password twice.
# The only other alternative is to include the plaintext password
# on the command line but that could be captured by any other
# process running in the system!
#7za a -tzip -p"$BITWARDENPW" -mem=AES256 bitwarden-backup.zip bitwarden-backup.json
expect -f expect.encrypt-backup >> output
echo
echo "Removing all sensitive files..."
rm password expect.encrypt-backup bitwarden-backup.json output
echo "Moving generated backup file..."
mv bitwarden-backup.zip "$ZIPFILENAME"
echo "Cleaning up..."
cd ..
rmdir "bitwarden-backup.$$"
printf "\nThe copy of your Bitwarden vault in plaintext JSON format
is now in following AES256 encrypted zip file:
%s
Use your Bitwarden vault password to decrypt the zip file.\n" "$ZIPFILENAME"
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment