Articles in the code category

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!

To the comments

All posts

  1. Editing a CV in markdown with pandoc
  2. Releasing Michel, a flat-text-file-to-google-tasks uploader
  3. plowbot, a jabber bot that downloads links from 1-click hosters
  4. Vim: complete C++ accurately, pulling informations from the compiler, with gccsense and clang_complete
  5. Google releasing a constraint programming library
  6. TalkMyPhone, une appli android pour recevoir des notifications de son téléphone
  7. renaming files and variables from vim
  8. Continuous background compilation within vim
  9. autotools, doxygen, et génération conditionnelle
  10. vim+gdb=vimgdb
  11. l'UML automatisé et le libre : c'est pas gagné!

Atom feed