GPG in a box

Using GPG inside a container

Sometimes I use GPG to encrypt secrets used by applications.
This has the advantage that most software have one way or another of accessing GPG
so it also help sharing secrets between tools.
For example sharing the same data file between Ansible and Terraform rather than using different methods.
This also maintains one source of truth.

The downside is of course setting up a keychain on each machine that needs access.
There is also the extra step of doing this over and over in a container like Docker or Kubernetes.

This is one possibility to address it:
- mount the encrypted files: GPG secret key, ownertrust ID, public key
(only the secret key is sensitive really but I like to keep them together)
- mount the public keys we may want to encrypt for
- get the GPG passphrase so we can decrypt our key
- get the keygrip so we know which key is ours TODO: put it in the private key pair file

Initialise the keychain at the start of your process or inside an init container.
We don’t want to have both the GPG_PASSPHRASE and the seret key it decrypt stored in the same place;
this is why eventually the GPG_PASSPHRASE is passed as an environment variable.
It could just as well be another mount point , but I don’t like having secrets “on disk”.

[[ ! $# -eq 2 ]] && usage
gpg_private_keyfile=$1
gpg_ownertrust_file=$2
[[ ! -f ${gpg_private_keyfile} ]] && die "file not found:" ${gpg_private_keyfile}
[[ ! -f ${gpg_ownertrust_file} ]] && die "file not found:" ${gpg_ownertrust_file}
[[ -z ${GPG_PASSPHRASE} ]] && die "GPG_PASSPHASE environment variable not set."
[[ -z ${GPG_KEYGRIP} ]] && die "GPG_KEYGRIP environment variable not set."

info "Importing GPG private key"
gpg --batch --import ${gpg_private_keyfile}

info "Importing GPG ownertrust"
gpg --import-ownertrust ${gpg_ownertrust_file}

warn "Restarting gpg-agent"
gpgconf --kill gpg-agent
gpg-agent --daemon --allow-preset-passphrase --max-cache-ttl 3600

info "Importing GPG passphrase"
/usr/libexec/gpg-preset-passphrase --preset --passphrase ${GPG_PASSPHRASE} ${GPG_KEYGRIP}

info "Current GPG keys"
gpg --with-colons -K

We start our Pod by mounting all the secrets and not so secrets data files
from ConfigMap and Secret and send the Passphrase via environment. TODO: change this

---
apiVersion: v1
kind: Pod
metadata:
  name: worker
spec:
  imagePullSecrets:
  - name: docker-registry-creds
  containers:
  - name: worker
    image: "ansible:latest"
    imagePullPolicy: Always
    tty: true
    command:
    - cat
    volumeMounts:
    - mountPath: /tmpfs/.secrets
      name: gpg-keyring
    - mountPath: /tmpfs/gpg-public-keys
      name: gpg-public-keys
    env:
    - name: GPG_PASSPHRASE
      valueFrom:
        secretKeyRef:
          name: gpg-keyring
          key: passphrase
    - name: GPG_KEYGRIP
      valueFrom:
        secretKeyRef:
          name: gpg-keyring
          key: keygrip
  volumes:
  - name: gpg-keyring
    secret:
      secretName: gpg-keyring
  - name: gpg-public-keys
    configMap:
      name: gpg-public-keys

Contents