Directing an SSH agent socket (UNIX domain socket) into a podman container, is as easy as 1-2-mount, so I erroneously assumed that doing similarly for GPG would be as easy. It wasn’t, at least for me.

I rose at 06:00 and yelled Eureka! at 15:05 local time. It took me so long because I tried to follow GNU documentation which speaks of using extra-socket. To cut a far too long story short, on the Debian host I’m doing this none of that business worked: the gpg-agent was just not creating the UNIX domain socket where I configured it to.

What I then did was to completely deactivate system’d socket activation for GPG: stop and disable the service and the socket activations. The main reason: changes in the unit file for the service, e.g. --extra-socket, weren’t being applied.

Then I launched the daemon manually and began looking at some logs. (No, I’m not a guru, but I hoped I’d see lots of things; omg did I.)

$ gpg-agent --daemon --debug-level guru > /tmp/glog 2>&1

Slowly but surely, after downloading the source to GPG and looking at what the agent does, it dawns on me that something magic is happening, and I blame either the distro and/or systemd. Be that as it may, the debian files in /usr/share/doc don’t mention anything about deactivating the extra socket, so shrug.

What I can’t get to work at all are the settings in ~/.gnupg/gpg-agent.conf, so I ignore that.

Anyway, I’m a bit further, but not really much, but it’s clear that it’s not gpg-agent’s fault.

Enough rambling, as I can’t remember all I’ve done, let me jot down how I solved the puzzle:

I reactivated all the systemd stuff, and everything’s back to normal. gpg-agent is creating the files in /run/user/1003/gnupg/ and when I try to kick the agent by decrypting a file on the host, the following files are created

$ gpgconf --kill gpg-agent
$ rm -r /run/user/1003/gnupg/
$ gpg --batch --use-agent --decrypt project/vault_passphrase.gpg
$ ls -l /run/user/1003/gnupg/
total 0
srwx------ 1 jpm staff 0 Apr 16 12:58 S.gpg-agent
srwx------ 1 jpm staff 0 Apr 16 12:58 S.gpg-agent.browser
srwx------ 1 jpm staff 0 Apr 16 12:58 S.gpg-agent.extra
srwx------ 1 jpm staff 0 Apr 16 12:58 S.gpg-agent.ssh

So be it. That’s where the sockets are. :shrug: (Note, I’m displeased, but :shrug:)

Now back to podman. The intention is to carry a socket across, so that the container can access my agent. This is the magic incantation:

--container-volume-mount /run/user/1003/gnupg/S.gpg-agent:/root/.gnupg/S.gpg-agent:Z

When the container launches (target user is root), my agent socket (user 1003) is mounted at /root/.gnupg in the target, and the root user there has access to it.

This actually works, and I want this to be able to provide encrypted vault secrets.

Total overkill, and half a day’s worth of smoke arising from the top of my skull, but if somebody asks “can it?”, I can answer: “yes, it can!”

The actual fun begins then, with Ansible accessing the vault, configured in an ansible.cfg for the project:

[defaults]
vault_password_file = vault_pass.sh

The vault_pass.sh program which is taken across in the ansible-runner project now does this (output to stderr so that it’s carried back to the host which invoked ansible-runner):

gpg --list-keys >&2             # will create the .gnupg directory
cat > ~/.gnupg/gpg.conf <<EOF1
use-agent
EOF1

cat << EOF2 | gpg --import -
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQGNBGYc9KgBDACm0FOP70HFdSfGVL9W8X0HkasdTxYrz5RWJmM+Z3oxBZEpzAuP
4572CAdZC4MS/8B/ABlnojJBegrATySNu+fB7wHURKkz4BOEnF3oobJQoj+PYOT4
...
rzkFQg==
=xTtx
-----END PGP PUBLIC KEY BLOCK-----
EOF2

gpg --homedir /root/.gnupg --batch --use-agent --decrypt vault_passphrase.gpg
echo "Outta here" >&2

Output (stdout) from gpg is consumed by Ansible to unlock vault secrets.

And yes, this does function:

$ ansible-runner run \
        --container-volume-mount /run/user/1003/gnupg/S.gpg-agent:/root/.gnupg/S.gpg-agent:Z \
        . -p v1.yml
gpg: keybox '/root/.gnupg/pubring.kbx' created
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key 353C430586B83FE5: public key "JP Ansible (Runner Vault tests) <jp@example>" import
ed
gpg: WARNING: server 'gpg-agent' is older than us (2.2.40 < 2.3.3)
gpg: Note: Outdated servers may lack important security fixes.
gpg: Note: Use the command "gpgconf --kill all" to restart them.
gpg: Total number processed: 1
gpg:               imported: 1
gpg: encrypted with rsa3072 key, ID 3F9FC1C9EA3FE42F, created 2024-04-15
      "JP Ansible (Runner Vault tests) <jp@example>"
gpg: WARNING: server 'gpg-agent' is older than us (2.2.40 < 2.3.3)
gpg: Note: Outdated servers may lack important security fixes.
gpg: Note: Use the command "gpgconf --kill all" to restart them.
gpg: problem with fast path key listing: IPC parameter error - ignored
Outta here

PLAY [Vault test] **************************************************************

TASK [ping] ********************************************************************
ok: [w00]

TASK [Can we unvault this var?] ************************************************
ok: [w00] => {
    "msg": "The cow jumped over the moon."
}

The variable was unvaulted.

One important bit: the socket activation of gpg-agent only occasionally triggers the pinentry program on the host; I’ve noticed it works smoothest if I do so myself by decrypting a file on the host. (I’m not now feeling up to how to configure TTLs on the agent-cached secrets; I hope that’s more straightforward than what I dug into today.)

Moo.

Update

In desperation I thought I’d see what happens when gpg-agent finds a file in the place it wants to create a directory. I wish I hadn’t done that, but see for yourselves. The result is what I’ve spent 3/4 of a day trying to accomplish.

creating a file in /run/user inhibits socket creation there

Yeah, my use of man gpg-agent at the end is sarcastic. I could have written worse: info gnupg

Update2

I download the Debian source package for gpg-agent, and therein I do a grep -ri run.user .. To be fair, they’ve documented it somewhat:

Users who don’t want systemd to manage their gpg-agent in this way for all future sessions should do: systemctl --user mask --now gpg-agent.service gpg-agent.socket gpg-agent-ssh.socket gpg-agent-extra.socket gpg-agent-browser.socket

The reason I didn’t find that sentence yesterday? I looked in /usr/share/doc/gnupg. Beginner error. It’s in /usr/share/doc/gpg-agent (list of files). Sighs.

But there’s no word on the fallback to $HOME/.gnupg that I can find.

gpg and podman :: 16 Apr 2024 :: e-mail