Emacs + GPG + pinentry.el for authentication

Viewed 1750

I'm having a hard time setting up pinentry for Emacs to work with authentication (lets say for github). Using GPG and pinentry for signing commits already works. I use magit inside emacs for version control, and everytime I commit something pinentry pops up in the minibuffer, I enter my password and the commit is signed. All good.

However I also use GPG for authentication. I setup and authentication subkey with GPG and setup SSH to use it. If I do this from the tty it works as well. Just not from inside Emacs. The problem has something to do with the $GPG_TTY env var. If I try to push something to GH, pinentry in the minibuffer doesn't pop up. Instead it waits and eventually says I have no permission.

I should mention that I use emacs, more specifially EXWM as my window manager. If I then exit out of emacs I see the pinentry-encurses dialog in my TTY. If I have another terminal open in emacs while pushing it will open the pinentry-ncurses dialog in that terminal instead of popping up pinentry.el in the minibuffer.

I've been googling and trying out different approaches for days now with no luck.

~/.gnupg/gpg-agent.conf:

allow-emacs-pinentry
allow-loopback-pinentry
default-cache-ttl 600
max-cache-ttl 7200
enable-ssh-support

~/.gnupg/gpg.conf:

use-agent

~/.bash_profile:

# GPG related
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
export GPG_TTY=$(tty)
#gpgconf --launch gpg-agent

My pinentry config in emacs:

;; Enable loopback so that pinentry will pop up in emacs
(setq epa-pinentry-mode 'loopback)
(pinentry-start)

;; Update the TTY for gpg-agent
;(setenv "GPG_TTY" "/dev/pts/0")
(shell-command "gpgconf --launch-agent")
(shell-command "gpg-connect-agent updatestartuptty /bye >/dev/null")

I tried setting the $GPG_TTY manually inside emacs as you can see with no luck. It's baffling to me that it will just work for signing commits but not authentication.

2 Answers

It's been a while since I asked this question. I wasn't able to find an answer until i stopped playing with this for a while and came back a couple of weeks later. Unfortunately I'm not able to tell why it didn't work in the first place. But I have found a configuration that just happens to work for me, ever since I found that I haven't touched any of the files, there it is:

Requirements:

  • Emacs 27+ (I haven't testes with older versions)
  • pinentry-emacs. If not available in your package manager, build it from source:
# Install deps (these also include emacs deps - sorry)
sudo apt update && sudo apt install -y git libgpg-error-dev libassuan-dev lbzip2 autoconf automake autotools-dev bsd-mailx build-essential diffstat gnutls-dev imagemagick libasound2-dev libc6-dev libdatrie-dev libdbus-1-dev libgconf2-dev libgif-dev libgnutls28-dev libgpm-dev libgtk2.0-dev libgtk-3-dev libice-dev libjpeg-dev liblockfile-dev liblqr-1-0 libm17n-dev libmagickwand-dev libncurses5-dev libncurses-dev libotf-dev libpng-dev librsvg2-dev libsm-dev libthai-dev libtiff5-dev libtiff-dev libtinfo-dev libtool  libx11-dev libxext-dev libxi-dev libxml2-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxt-dev libxtst-dev libxv-dev quilt sharutils texinfo xaw3dg xaw3dg-dev xorg-dev xutils-dev zlib1g-dev libjansson-dev libxaw7-dev libselinux1-dev libmagick++-dev libacl1-dev

# build pinentry-emacs
cd ~
wget https://gnupg.org/ftp/gcrypt/pinentry/pinentry-1.1.0.tar.bz2
tar -xf pinentry-1.1.0.tar.bz2
cd pinentry-1.1.0
./configure --enable-pinentry-emacs --enable-inside-emacs
make
sudo make install
cd ~
rm -rf pinentry-1.1.0 pinentry-1.1.0.tar.bz2

Now the configuration files:

# set this somwhere in your .bashrc / .zshrc / ...
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)

~/.gnupg/gpg-agent.conf:

pinentry-program /usr/local/bin/pinentry-emacs # or we the path is
enable-ssh-support

~/.gnupg/sshcontrol:

<keygrip of your auth key>

Get the keygrip with gpg2 --list-secret-keys --keyid-format LONG --with-keygrip.

Now for emacs itself. Install pinentry for emacs. I use doom so I just have to do (package! pinentry).

Apply the following configuration:

;; Enable loopback so that pinentry will pop up in emacs
(pinentry-start)

;; Start GPG agent with SSH support
(shell-command "gpg-connect-agent /bye")

At least for me, after doing all this, pinentry inside emacs works for both signing and authentication.

I had the exact same symptoms and resolved it today. GPG signing worked from magit, but attempts to push failed. However, I noticed that magit push works after the emacs server is restarted. The problem was that the environment variable SSH_AUTH_SOCK was not defined when emacs server started.

I believe that the problem is that emacs starts before SSH_AUTH_SOCK is defined. In my case, the emacs server is started and SSH socket variable is defined from systemd user services. The issue was resolved by forcing emacs server to start only after the variable has been set.

In your case, it's most likely because emacs starts very early as part of EXWM. You can confirm this with M-x getenv and looking for variable SSH_AUTH_SOCK. If the variable is missing, then a possible solution is to define the environment variable within emacs:

(setenv "SSH_AUTH_SOCK" (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket"))

However, this step has to be delayed till after EXWM has started fully, when you can reasonably expect the variable to be set. Perhaps it could be executed with some hook at the end of EXWM loading. Also, this will work only if gpg agent is used as ssh agent. This must be true in your case since you are using gpg authentication subkey.

Related