Testing GPG Keys With Docker... and fail

As a password-store user, GPG is particularly important and sensitive, I use it for pretty much everything authentication / encryption related. Also, about a year ago I got myself a pair of Yubikeys, and they are now involved in all of the mentioned workflows.

Now on the topic, as my keys are a crucial part of my online life, I wanted to make sure I had those backuped safely, and moreover, that this backup is usable in an empty environment by simply importing the public and private keys. Among the various possibilities, I thought firing up a basic docker container with an interactive shell would be my fastest bet. How wrong I was.

So I wrote this short Dockerfile:

FROM alpine:latest
LABEL maintainer "Emile `iMil' Heitor <imil@home.imil.net>"

# Install packages
RUN apk --no-cache add gnupg bash

CMD ["/bin/bash"]

A perfectly fine Dockerfile don’t you think?

A build later, I was able to enter the container which has access to a directory containing my key’s backups:

$ docker run -it --rm -v /home/imil/bkp:/mnt imil/gpgshell

From there, I imported my (previously AES 256 encrypted) keys the way I thought I should:

bash-5.0# gpg --import /mnt/master.asc
gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
gpg: key 48A007E9B35E9F9D: 30 signatures not checked due to missing keys
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key 48A007E9B35E9F9D: public key "Emile Heitor (iMil) <imil@home.imil.net>" imported
gpg: key 48A007E9B35E9F9D/48A007E9B35E9F9D: error sending to agent: Not a tty
gpg: error building skey array: Not a tty
gpg: error reading '/mnt/master.asc': Not a tty
gpg: import from '/mnt/master.asc' failed: Not a tty
gpg: Total number processed: 0
gpg:               imported: 1
gpg:       secret keys read: 1
gpg: no ultimately trusted keys found

The messages about not being on a tty should have gave me the answer straight away but I must say I really thought my backuped keyring was wrong or corrupted, because from there, I was unable to decrypt a previously encrypted file:

bash-5.0# gpg -d test/dummy.gpg
gpg: WARNING: unsafe ownership on homedir '/root/.gnupg'
gpg: error getting version from 'scdaemon': No SmartCard daemon
gpg: encrypted with RSA key, ID 0000000000000000
gpg: decryption failed: No secret key

And as a matter of fact, gpg -K showed no private keys. Cold sweat.

The main fear about having corrupted my backups was that once you import your subkeys to the Yubikey, it is not possible to export them anymore! Fortunately, past-me was clairvoyant enough to have backed up a working copy of the complete keyring to an encrypted USB key, so I was sure it was the GPG / docker duo who was misbehaving.

Of course I tried to set the GPG_TTY variable inside the container, but the results were not better, worse, misleading:

bash-5.0# export GPG_TTY="$(tty)"
bash-5.0# gpg --import /mnt/gpg_master.asc
gpg: key 48A007E9B35E9F9D: 30 signatures not checked due to missing keys
gpg: key 48A007E9B35E9F9D: "Emile Heitor (iMil) <imil@home.imil.net>" not changed
gpg: key 48A007E9B35E9F9D/48A007E9B35E9F9D: error sending to agent: No such file or directory
gpg: error building skey array: No such file or directory
gpg: error reading '/mnt/master.asc': No such file or directory
gpg: import from '/mnt/master.asc' failed: No such file or directory
gpg: Total number processed: 0
gpg:              unchanged: 1
gpg:       secret keys read: 1

I later learned that $(tty) inside the container returned nothing…

After many tries, disappointments and deceptions, I finally got over this machiavellian plot:

FROM alpine:latest
LABEL maintainer "iMil"

ENV GPG_TTY /dev/console
# Install packages
RUN apk --no-cache add gnupg bash

CMD ["/bin/bash"]

Just like that, declaring the GPG_TTY environment variable into the Dockerfile, GnuPG was happy:

bash-5.0# for f in /mnt/gpg_*.asc; do gpg --import $f; done
gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
gpg: key 48A007E9B35E9F9D: 30 signatures not checked due to missing keys
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key 48A007E9B35E9F9D: public key "Emile Heitor (iMil) <imil@home.imil.net>" imported
gpg: key 48A007E9B35E9F9D: secret key imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg:       secret keys read: 1
gpg:   secret keys imported: 1
gpg: no ultimately trusted keys found
gpg: key 48A007E9B35E9F9D: 30 signatures not checked due to missing keys
gpg: key 48A007E9B35E9F9D: "Emile Heitor (iMil) <imil@home.imil.net>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg: key 48A007E9B35E9F9D: 30 signatures not checked due to missing keys
gpg: key 48A007E9B35E9F9D: "Emile Heitor (iMil) <imil@home.imil.net>" not changed
gpg: To migrate 'secring.gpg', with each smartcard, run: gpg --card-status
gpg: key 48A007E9B35E9F9D: secret key imported
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg:       secret keys read: 1
gpg:  secret keys unchanged: 1
bash-5.0# echo $GPG_TTY
/dev/console
bash-5.0# gpg -K
/root/.gnupg/pubring.kbx
------------------------
sec   rsa4096 2014-03-22 [SC]
      93A8ED456F888A4A80B3ECE248A007E9B35E9F9D
uid           [ unknown] Emile Heitor (iMil) <imil@home.imil.net>
uid           [ unknown] Emile Heitor (iMil) <emile.heitor@gmail.com>
uid           [ unknown] Emile Heitor (NetBSD) <imil@NetBSD.org>
uid           [ unknown] Emile Heitor (iMil) <imil@gcu.info>
uid           [ unknown] Emile Heitor (iMil) <imil@gcu-squad.org>
ssb   rsa4096 2014-03-22 [E]
ssb   rsa4096 2019-11-10 [A]
ssb   rsa4096 2019-11-10 [S]
ssb   rsa4096 2019-11-10 [E]

No more error messages, keys actually imported, and more importantly:

bash-5.0# gpg -d /mnt/password-store-master/test/dummy.gpg
gpg: error getting version from 'scdaemon': No SmartCard daemon
gpg: encrypted with RSA key, ID 0000000000000000
test

The ability to decrypt.

While at it, I made a donation to GnuPG, and you probably should too.