Scripting sudo with pass

Like a lot of folks, I use zx2c4's pass. Like even more people, I use sudo for administrative tasks on my machines.

Seamless integration

A lot of tools (offlineimap, docker, ansible-vault...) offer a way to provide the password through an external command. This is where pass excels. By allowing you to extract any password with a simple command line, it lets you have all of your passwords in one place, with no overhead in your workflow.

What about sudo?

If you want to run a sudo command without typing the password interactively, you would normally use the NOPASSWD option in /etc/sudoers. I do not find it desirable: should you leave a console unattended, this exposes you to somebody running administrative commands too easily.

So can you instead tell sudo to use pass for the password input? Sort of. The -A command line option is designed to provide the password through an helper program. To quote the manpage:

If the -A (askpass) option is specified, a (possibly graphical) helper program is executed to read the user's password and output the password to the standard output. If the SUDO_ASKPASS environment variable is set, it specifies the path to the helper program.

Let's take advantage of this!

Implementation

I store in pass my different "user@host" passwords under the path host/<user>@<host>. If you adopt the same convention, this is what you can do:

Save the following file as executable under ~/.local/bin/sudo-askpass

#!/bin/bash
pass show hosts/$(whoami)@$(hostname) | head -n1

Insert your password in pass accordingly

pass edit hosts/$(whoami)@$(hostname)

And now if you run

export SUDO_ASKPASS=$HOME/.local/bin/sudo-askpass
sudo -A whoami

The answer should be root. Neat, but not seamless, since you need the -A switch. You could solve the problem by adding these 2 lines in your shell startup file

export SUDO_ASKPASS=$HOME/.local/bin/sudo-askpass
alias sudo='sudo -A'

However, programs which invoke sudo (such as yay) will not know about this alias, and you will still be required to type your password interactively. What I do instead is to create my own sudo executable with higher precedence in the path whicht insert the proper options before invoking the real binary:

#!/bin/bash

SCRIPTDIR=$(dirname $(which $0))
PATH_WITHOUT_SCRIPTDIR=$(echo $PATH | tr ":" "\n" | grep -v "$SCRIPTDIR" | tr "\n" ":")
REAL_SUDO=$(env PATH=$PATH_WITHOUT_SCRIPTDIR which sudo)
exec env SUDO_ASKPASS=$HOME/.local/bin/sudo-askpass $REAL_SUDO --askpass "$@"

If your distribution follows the systemd file hierarchy, you can save this file under ~/.local/bin/sudo and it will happily take precedence over the real sudo.

Keychain

I employ a similar trick for unlocking my ssh keys with keychain. I store in pass the password of the private ssh key of <user>@<host> as ssh/<user>@<host>. The following file is saved as executable under ~/.local/bin/ssh-askpass

#!/bin/bash
pass show ssh/$(whoami)@$(hostname) | head -n1

And this goes in my shell startup file

{type keychain > /dev/null} && {type ssh-askpass > /dev/null} && \
    source <(SSH_ASKPASS=ssh-askpass keychain --quiet --eval id_rsa </dev/null)

More to come

If you have a look at my dotfiles, you will realize that there is more to this setup. The main additional thing to know is that I use a smartcard to handle the gpg decryption, so that no machine contains the gpg key that I use with pass. This will likely be the purpose of a different article. Until then and as always, I am gladly taking any comment.

Happy sudo-ing!

Comments !