Migrating from pass to Bitwarden

pass, sometimes called "password-store", is a great password manager. It's nothing more than a shell script that uses tried and tested unix tools like gpg in order to store passwords in a safe and secure manner.

Nevertheless, pass lacks some features that I find important: namely, sync. Sure, you can put your entire pass directory into a git repository and sync it like that, but, especially with the death of Android-Password-Store, actually using pass on my phone is a bit of a pain.

A good alternative is Bitwarden: a free and self-hostable password manager with sync features and a well-maintained Android app. In this blogpost I'll take you through the steps of migrating from pass to Bitwarden.

Migrating passwords with rbw

I'll assume that you already have Vaultwarden set up and running on your homeserver.

On the client, we can install rbw, a third-party command line Bitwarden client.

We can then write a simple shell script (let's call it pass2rbw.sh) to read out all pass entries and add them to the bitwarden vault using rbw.

A note here on formats: rbw uses a fixed format where the username is passed as a command-line option, while the password is stored in the first line of the note, with the rest of the note being freeform text.

pass, on the other hand, does not have a fixed format. The entirety of the note is freeform text. Personally, I put the username on the first line and the password on the second, which is the format that my script uses. Adjust it to fit your own setup.

#!/bin/sh

# Change into the password-store directory
cd ~/.password-store

# Get a list of all the password name
list=$(find . -name '*.gpg' | cut -c 3- | sed 's/\.gpg$//')

# go back to the directory the script was launched from
cd -

for i in $list
do
	# Get the text of the pass entry
	text=$(pass show $i)

	# Get username (first line)
	username=$(echo "$text" | head -n 1)

	# Get password (second line)
	password=$(echo "$text" | tail -n +2 | head -n 1)

	# Get the rest of the entry
	note=$(echo "$text" | tail -n +3)

	# Put the password and note into a file
	echo "$password" > ./PASSWORD
	echo "$note" >> ./PASSWORD

	# Add it to RBW
	EDITOR=$(realpath ./not-an-editor.sh) rbw add "$i" "$username"

done

There doesn't seem to be a non-interactive way to add entries to rbw; rbw add always spawns a text editor to enter the note text. We get around this by overriding the $EDITOR variable and pointing it to a special ./not-an-editor.sh script that just copies the contents of the PASSWORD file created earlier by the script into the target file created by rbw:

#!/bin/sh

cat PASSWORD > "$1"

Make sure that not-an-editor.sh and pass2rbw.sh are executable and run them. Once you've verified that the entries have been transferred correctly, don't forget to shred the temporary PASSWORD file.

Autotype

I used to have a nice autotype script that would let me choose a pass entry using rofi, and autotype it using xdotool.

Although rofi-rbw already exists, I would like to write my own script that works with just BASH and doesn't depend on Python. Here it is:

#!/bin/bash

# Enable using custom keys in rofi
echo -en "\0use-hot-keys\x1ftrue\n"

if [ -z "$1" ]; then
	# First call (no selection provided)...


	rbw unlocked > /dev/null 2>&1
	if [ "$?" -ne 0 ]
	then
		# If `rbw` is not unlocked, spawn a terminal
		# and prompt the user to unlock.
		coproc (
			alacritty -e rbw unlock
			> /dev/null 2>&1
		)
		# Close rofi after spawning terminal
		killall rofi
	else
		# If `rbw` is unlocked,
		# list all entries
		rbw ls
	fi
else
	# Second call (selection provided)...

	# Get name, username, and password
	NAME=$1
	FIRSTLINE=$(rbw get -f user "$NAME")
	SECONDLINE=$(rbw get "$NAME")

	# Unpress all modifiers
	xdotool keyup Control Alt Super

	case $ROFI_RETV in
		1 )
			# User hit `enter`:
			# autotype username <tab> password <enter>
			coproc (
				sleep 0.2
				test "$FIRSTLINE" && {
					echo "$FIRSTLINE" |
						tr -d '\n' |
						xdotool type --file - --delay 20
					xdotool key Tab
				}

				test "$SECONDLINE" && {
					echo "$SECONDLINE" |
						tr -d '\n' |
						xdotool type --file - --delay 20
				}
				xdotool key Enter
			)
			;;
		10 )
			# User hit custom key 1:
			# autotype just the username
			coproc (
				sleep 0.2
				echo "$FIRSTLINE" |
					tr -d '\n' |
					xdotool type --file - --delay 20
			)
			;;
		11 )
			# User hit custom key 2:
			# autotype just the password
			coproc (
				sleep 0.2
				echo "$SECONDLINE" |
					tr -d '\n' |
					xdotool type --file - --delay 20
			)
			;;
	esac

fi

Once you've changed alacritty to reflect the terminal emulator that you use, you can run the script with rofi like this:

rofi \
	-theme solarized \
	-show RBW:~/.config/rofi/rofi_rbw.sh \
	-kb-row-up 'Up' \
	-kb-remove-to-sol '' `# Unset existing Ctrl+p shortcut` \
	-kb-custom-1 "Ctrl+u" \
	-kb-custom-2 "Ctrl+p"