Mastodon

New Year, New Password Manager, Part Deux

Back during the Before Times™, I set out on a quest to free my passwords and various other credentials from the walled garden of the Apple ecosystem, and was fairly satisfied with my first attempt at a solution. Six months later, and, well, you can probably guess by the fact that you’re reading this that things were not so peachy, and you’d be right. So what went wrong? A few minor quibbles here and there, with one annoyance substantial enough to drive me back to the drawing board: That pandora’s box we all unwittingly unleash upon ourselves the moment we find ourselves with more than one device: synchronization.

In my first post about KeePass, I naïvely hand-waved away the problem of synchronization onto the clients, blissfully unaware of how ill-equipped many were to the task. The principle problem, I believe, lies in the fact that KeePass stores everything in a single binary file. A change to one password affects every password, from the perspective from a client. While backing up one’s database is as simple as copying a file, there was nonetheless much apprehension whenever a sync was required. So, back at the drawing board, I’ve added a requirement of easy conflict resolution. Reviewing my list of available options, I eased my requirement of widespread availability, and settled on pass, "the standard unix password manager".

pass is made with the Unix philosophy in mind; it does one thing, and it does it well, namely retrieving passwords and orchestrating other tools to save (any editor you’d like), encrypt (gpg) passwords in a versioned (git) repository. Oh, and passwords are saved as individual files, because everything is a file. With version control being handled by git, I think it’s safe to say that the “easy conflict resolution” box can be checked.

Synchronization across devices works in a similar fashion to multiple persons collaborating on a project with Git; each device holds a local copy of the password store, pushing and pulling updates to and from one another. For simplicities sake, I’ve opted to establish a central repository hosted on a private server using NGINX.

Providing Password Protection

Although our passwords are individually encrypted, it’s probably still a good idea to password-protect the repository endpoint on the server. There are a variety of ways this can be accomplished; one of the simplest being with htpasswd. Add a user like so (you may omit the -c flag if a credential file already exists):

$
htpasswd -c /etc/nginx/.htpasswd

Serving the Password Store

Serving a Git repository with NGINX is relatively straight-forward. We can simply pass any requests to git-http-backend via FastCGI. Like password protection, it’s probably a good idea to secure the endpoint with SSL as well. Thankfully, this can be achieved with virtually no effort thanks to Let’s Encrypt. I’ll leave that part of the process as an exercise for the reader.

server {
listen 443 ssl;
server_name my.domain.com;
root /var/www/pass;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
access_log /var/log/nginx/pass.access.log;
error_log /var/log/nginx/pass.error.log;
auth_basic "Authentication required";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
client_max_body_size 0; # git pushes can be massive, make sure nginx doesn't abruptly cut the connection
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/libexec/git-core/git-http-backend;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param GIT_PROJECT_ROOT /var/www/pass; # directory containing repositories
fastcgi_param PATH_INFO $uri;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_pass unix:/var/run/fcgiwrap.socket; # pass the request to fastcgi
}
}

Tug of War

Git relies on the concepts of “pushing” and “pulling” to update others and receive updates from others, respectively. Unfortunately, these are manual processes. We can automate pushing though, through the use of commit hooks, as pass automatically commits changes. Hooks are simply executables, such as a bash script, that are run by git at various stages of the commit process. For our purposes of automatically pushing updates, we can use a post-commit hook. To do so, add an executable file to /path/to/your/.password-store/.git/hooks/post-commit containing the following:

#!/bin/sh
git push origin master

Automating pulling updates is a little more tricky. Perhaps a hook could be written to regularly run git pull, or some solution using push notifications from the server could be architected. I’ll leave this as an exercise to the reader or my future self.

Déjà-vu All Over Again

Because I like the idea of symmetry with the first post, here’s a list of clients I’m currently using:

Conclusion

I’m excited to be continuing taking steps towards a more private, personal infrastructure for my data. In particular, pass, with its philosophy of being a focused, composable application, represents freedom not only for my data, but for my workflow and the tools supporting it.