It’s now becoming more widely known that using guessable passwords or using the same password for more than one account is a serious security risk, because an attacker able to control one account (such as an email account) can do a lot of damage. If an attacker gets the hash of your password from some web service, you want to be assured that the hash will be very difficult to reverse, and even if it can be reversed, that it’s unique and won’t give them access to any of your other accounts.
This growing awareness has contributed to the popularity of password managers, tools designed to securely generate, store, and retrieve passwords, encrypted with a master password or passphrase. In some cases these are locally stored, such as KeePass, and in others they are stored on a web service, such as LastPass. Both are good tools, and work well with GNU/Linux. I personally have some reservations about LastPass as I don’t want my passwords stored on a third party service, and I don’t trust JavaScript encryption.
Interestingly, because we now have a tidy GnuPG setup to handle the encryption
ourselves, another option is the pass(1)
tool, billing itself as “the
standard UNIX password manager”. It’s little more than a shell script and some
bash(1)
completions wrapped around existing tools like git(1)
,
gpg2(1)
, pwgen(1)
, tree(1)
, and xclip(1)
,
and your choice of $EDITOR
. If you’re not already invested in an existing
password management method, you might find this a good first application of
your new cryptography setup, and a great minimal approach to secure password
storage accessible from the command line (and therefore SSH).
On Debian-derived systems, it’s available as part of the pass
package:
# apt-get install pass
This includes a manual:
$ man pass
Instructions for installing on other operating systems are also available on
the site. Releases are also available for download, and a link to the
development repository. If you use this, make sure you have the required
tools outlined above installed as well, although xclip(1)
is only needed if
you run the X Windows system.
Setup
We can get an overview of what pass(1)
can do by invoking it with no
arguments:
$ pass
To start, we’ll initialize our password store. For your own passwords, you will
want to do this as your own user rather than root
. Because pass(1)
uses
GnuPG for its encryption, we also need to tell it the ID of the appropriate key
to use. Remember, you can find this eight-digit hex code by typing gpg
--list-secret-keys
. A unique string identifying your private key such as your
name or email address may also work.
$ pass init 0x77BB8872
mkdir: created directory ‘/home/tom/.password-store’
Password store initialized for 0x77BB8872.
Indeed, we note the directory ~/.password-store
has been created, although
it’s presently empty except for the .gpg-id
file recording our key ID:
$ find .password-store
.password-store
.password-store/.gpg-id
Inserting
We’ll insert an existing password of ours with pass insert
, giving it a
descriptive hierarchical name:
$ pass insert google.com/gmail/example@gmail.com
mkdir: created directory ‘/home/tom/.password-store/google.com’
mkdir: created directory ‘/home/tom/.password-store/google.com/gmail’
Enter password for google.com/gmail/example@gmail.com:
Retype password for google.com/gmail/example@gmail.com:
The password is read from the command line, encrypted, and placed in
~/.password-store
:
$ find .password-store
.password-store
.password-store/google.com
.password-store/google.com/gmail
.password-store/google.com/gmail/example@gmail.com.gpg
.password-store/.gpg-id
Notice that pass(1)
creates a directory structure for us automatically. We
can get a nice view of the password store with pass
with no arguments:
$ pass
Password Store
└── google.com
└── gmail
└── example@gmail.com
Generating
If you’d like it to generate a new secure random password for you, you can use
generate
instead, including a password length as the last argument:
$ pass generate google.com/gmail/example@gmail.com 16
The generated password to google.com/gmail/example@gmail.com is:
!Q%i$$&q1+JJi-|X
If you have some service that doesn’t cooperate with symbols in passwords, you
can add the -n
option to this call:
$ pass generate -n google.com/gmail/example@gmail.com 16
The generated password to google.com/gmail/example@gmail.com is:
pJeF18CrZEZzI59D
pass(1)
uses pwgen(1)
for this password generation. In each case, the
password is automatically inserted into the password store for you.
If we need to change an existing password, we can either overwrite it with
insert
again, or use the edit
operation to invoke our choice of $EDITOR
:
$ pass edit google.com/gmail/example@gmail.com
If you do this, you may like to be careful that your editor is not configured to keep backups or swap files in plain text of documents it edits in temporary directories or memory filesystems. If you’re using Vim, I wrote a plugin in an attempt to solve this problem.
Note that adding or overwriting passwords does not require your passphrase; only retrieval and editing does, consistent with how GnuPG normally works.
Retrieval
This password can now be retrieved and echoed onto the command line given the appropriate passphrase:
$ pass google.com/gmail/example@gmail.com
(...gpg-agent pinentry prompt...)
Tr0ub4dor&3
If you’re using X windows and have xclip(1)
installed, you can put the
password on the clipboard temporarily to paste into web forms:
$ pass -c google.com/gmail/example@gmail.com
Copied google.com/gmail/example@gmail.com to clipboard. Will clear in 45 seconds.
In each case, note that if you have the bash completion installed and working,
you should be able to complete the full path to the passwords with Tab
, just
as if you were directly browsing a directory hierarchy.
Deletion
If we no longer need the password, we can remove it with pass rm
:
$ pass rm google.com/gmail/example@gmail.com
Are you sure you would like to delete google.com/gmail/example@gmail.com? [y/N] y
removed ‘/home/tom/.password-store/google.com/gmail/example@gmail.com.gpg’
We can delete whole directories of passwords with pass rm -r
:
$ pass rm -r google.com
Are you sure you would like to delete google.com? [y/N] y
removed ‘/home/tom/.password-store/google.com/gmail/example@gmail.com.gpg’
removed directory: ‘/home/tom/.password-store/google.com/gmail’
removed directory: ‘/home/tom/.password-store/google.com’
Version control
To keep historical passwords, including deleted ones if we find we do need them
again one day, we can set up some automatic version control on the
directory with pass git init
:
$ pass git init
Initialized empty Git repository in /home/tom/.password-store/.git/
[master (root-commit) 0ebb933] Added current contents of password store.
1 file changed, 1 insertion(+)
create mode 100644 .gpg-id
This will update the repository every time the password store is changed, meaning we can be confident we’ll be able to retrieve old passwords we’ve replaced or deleted:
$ pass insert google.com/gmail/newexample@gmail.com
mkdir: created directory ‘/home/tom/.password-store/google.com’
mkdir: created directory ‘/home/tom/.password-store/google.com/gmail’
Enter password for google.com/gmail/newexample@gmail.com:
Retype password for google.com/gmail/newexample@gmail.com:
[master 00971b6] Added given password for google.com/gmail/newexample@gmail.com to store.
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 google.com/gmail/newexample@gmail.com.gpg
Backups
Because the password files are all encrypted only to your GnuPG key, you can
relatively safely back up the store on remote and third-party sites simply by
copying the ~/.password-store
directory. If the filenames themselves contain
sensitive information, such as private usernames or sites, you might like to
back up an encrypted tarball of the store instead:
$ tar -cz .password-store \
| gpg --sign --encrypt -r 0x77BB8872 \
> password-store-backup.tar.gz.gpg
This directory can be restored in a similar way:
$ gpg --decrypt \
< password-store-backup.tar.gz.gpg \
| tar -xz