Pinentry with flatpak applications

jay.kayes at posteo.com jay.kayes at posteo.com
Sat Feb 8 17:51:29 CET 2025


Hi all,

I've been debugging using gpg in flatpak apps.  Particularly, I use 
Evolution as a flatpak for email.  The issue I am having is that gpg 
private key operations only work if the card pin or key passphrase is 
already in the agent cache.  Pinentry does not work when triggered by a 
gpg operation from a flatpak sandboxed app.  I can only decrypt email 
when I've done a gpg operation outside the sandbox to get the pin 
entered.

The following debugging/demonstration notes are with Kleopatra for ease 
of testing gpg operations, but I observe the same behavior and results 
with Evolution.  The following are notes as I verified the behavior in a 
fresh VM (Fedora 41 Workstation) environment.


System GPG version:
   gpg (GnuPG) 2.4.5
   libgcrypt 1.11.0-unknown

Flatpak sandbox GPG version:
   gpg (GnuPG) 2.5.1
   libgcrypt 1.11.0

Agent configuration:
   $ cat ~/.gnupg/gpg-agent.conf
   pinentry-program /usr/bin/pinentry-gnome3
   debug-level basic

With a freshly started agent process, I open Kleopatra and attempt to 
sign some text in the Notepad view.  This fails.

The error message in Kleopatra's diagnostics:
   gpg: WARNING: server 'gpg-agent' is older than us (2.4.5 < 2.5.1)
   gpg: Note: Outdated servers may lack important security fixes.
   gpg: Note: Use the command "gpgconf --kill all" to restart them.
   gpg: signing failed: pinentry error
   gpg: -&48: sign+encrypt failed: pinentry error

Looking at the output of gpg-agent, I see this line:
   DBG: chan_6 <- OPTION 
putenv=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/flatpak/bus

I think this is the smoking gun.  If I understand correctly, Kleopatra 
is telling the agent where it thinks the DBUS socket is and the agent 
attempts to use this value when launching pinentry.  This of course 
fails, as there is no dbus socket at that location from gpg-agent's 
perspective.  This value is what Flatpak sets up when it starts the 
sandbox, from Kleopatra's perspective this the correct value.  For 
gpg-agent, the correct value is of course 
`DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus`.

I now do a GPG operation outside the sandbox so that the passphrase gets 
prompted and cached.  Once the passphrase is cached, I can switch back 
to Kleopatra, and the signing operation (or any other using the private 
key) succeeds.  If I restart the agent, Kleopatra will fail to 
sign/decrypt again until I get the passphrase cached.

Another interesting demonstration is to modify DBUS_SESSION_BUS_ADDRESS 
before starting Kleopatra from a shell inside the sandbox.

If I first open a shell in the sandbox and launch Kleopatra, I do not 
get a pinentry prompt and operation fails.  Same as above.
   $ flatpak run --command=bash org.kde.kleopatra
   [📦 org.kde.kleopatra ~]$ echo $DBUS_SESSION_BUS_ADDRESS
   unix:path=/run/flatpak/bus
   [📦 org.kde.kleopatra ~]$ kleopatra

If I modify DBUS_SESSION_BUS_ADDRESS in the sandbox shell before 
launching Kleopatra as per below, everything works as expected. I get a 
pinentry prompt and private key operations are successful.  
Interestingly `$XDG_RUNTIME_DIR/bus` is symlinked to `/run/flatpak/bus`, 
but flatpak must have it's reasons for using 
`DBUS_SESSION_BUS_ADDRESS=/run/flatpak/bus`.

   [📦 org.kde.kleopatra ~]$ export 
DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
   [📦 org.kde.kleopatra ~]$ ls -l $XDG_RUNTIME_DIR/bus
   lrwxrwxrwx. 1 jay jay 17 Feb  8 15:14 /run/user/1000/bus -> 
../../flatpak/bus
   [📦 org.kde.kleopatra ~]$ kleopatra


In summary, steps to reproduce:
1. Restart agent or otherwise clear cache
2. Attempt to use private key in Flatpak app, pinentry fails
3. Use gnupg outside sandbox so that the pin gets entered and cached
3. Repeat in Flatpak app, using the private key now works


In the notes above with a clean environment, I only tested with 
Kleopatra.  Kleopatra gets access to the gpg socket by a filesystem 
permission - `$XDG_RUNTIME_DIR/gnupg` is mapped into the sandbox.  On my 
production environment, Evolution gets access using Flatpak's socket 
portal mechanism (`--socket=gpg-agent` permission).  This does not seem 
to be significant, the same behavior happens in both cases.  Both 
options are just making the socket available inside the sandbox.

I imagine there are relevant uses for the calling gpg process to 
communicate variables from it's environment to the agent process (e.g. 
PINENTRY_GEOM_HINT?).  But this also means that the calling gpg process 
can unintentionally cause the pinentry process to fail.  I haven't 
thought about this further, but could a malicious program compromise the 
agent somehow by setting some unexpected values?  I wonder if it would 
be useful to either blocklist some specific variables (e.g. 
DBUS_SESSION_BUS_ADDRESS) from being set through the socket.  Or better 
yet, perhaps the agent should only accept values for allowlisted 
variables?

BR Jay




More information about the Gnupg-users mailing list