Skip to content

Instantly share code, notes, and snippets.

@codejedi365
Last active July 22, 2023 13:06
Show Gist options
  • Save codejedi365/673c24a940824c5f3d73262913cc9079 to your computer and use it in GitHub Desktop.
Save codejedi365/673c24a940824c5f3d73262913cc9079 to your computer and use it in GitHub Desktop.
Transfer Public GPG keys to devcontainer

GOAL

Provide an automated method to load GPG keys from a host machine into a Docker container for git commit signing.

Problem

VSCode's Remote - Containers extension v0.209.5 does not support GnuPG/MacGPG2 v1.x.x original key storage format. VSCode's extension only looks for a pubring.kbx file to copy and that is it. This means any one who has the older but still Gnupg compatible format (pubring.gpg) will not work with the VSCode extension.

WHY

The keybox format was introduced in GnuPG 2.1 and it serves as an intermediate version which supports both the old and the new formats. However, up through GnuPG/MacGPG2 v2.2.32, libgcrypt 1.8.8 it will default to which ever format is found inside the GNUPG_HOME_DIRECTORY (~/.gnupg). This means if you didn't completely remove the pubring.gpg files from disk upon upgrade from the older version, then on gpg --import, it will import the keys into pubring.gpg. If and only if, pubring.gpg does not exist, then a pubring.kbx keybox file will be created.

If you have upgraded the gpg program but not re-built the ~/.gnupg/ directory from scratch, GPG maintains the old key storage format.

EASY SOLUTION: Manually export your public & private keys, backup the ~/.gnupg/ directory by moving it to ~/.gnupg_pre_2.1, rebuild the ~/.gnupg folder, and then run gpg --import for key files.

{
"build": {
"dockerfile": "Dockerfile",
"args": {
"VARIANT": "bullseye"
}
},
// Pre-build localhost command
"initializeCommand": "./.devcontainer/precontainerbuild.sh",
// ...
}
ARG VARIANT="16-bullseye"
FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:${VARIANT}
# ALTERNATIVE for GnuPG/MacGPG2 2.2.27 related to https://github.com/microsoft/vscode-remote-release/issues/5527
# publickeys.asc is created by initializeCommand's script
COPY --chown=node:node ./publickeys.asc /tmp/
# only imports usable & necessary signatures into keybox
RUN su node -c "gpg --import-filter drop-sig='expired == 0' --import-options import-clean --import /tmp/publickeys.asc" \
&& rm /tmp/publickeys.asc
#!/bin/sh
# Function to parse gpg list keys output & only export
# public keys that are not expired (by gathered fingerprints)
# creates file ./publickeys.asc
export_public_gpgkeys() {
EXPORTED_KEYS_FILE="$1"
rm -f "$EXPORTED_KEYS_FILE"
if ! output="$(gpg --list-keys --with-colons | grep -v '^(pub|fpr):')"; then
printf '%s\n' "Retrieving gpg keys failed." >&2
exit 1
fi
fingerprints=""
make_tmpfile="mktmp"
if ! command -v "$make_tmpfile" 1>/dev/null 2>&1; then
make_tmpfile="mktemp" # Mac OS
if ! command -v "$make_tmpfile" 1>/dev/null 2>&1; then
error "ERROR: unable to find command to create temporary file."
exit 1
fi
fi
tmp="$($make_tmpfile)"
printf '%s\n' "$output" > "$tmp"
find_pub=true
while IFS= read -r line <&3 || [ -n "$line" ]; do
{
if [ "$find_pub" = "true" ]; then
if printf '%s\n' "$line" | grep -q "^pub:[^en]:"; then
# pub:e:* means expired, pub:n:* means not trusted
find_pub=false
continue
fi
else
if printf '%s\n' "$line" | grep -q "^fpr:"; then
if ! fpr="$(printf '%s\n' "$line" | sed -r 's/^fpr::*([A-F0-9]*):$/\1/')"; then
printf '%s\n' "Failed to parse fingerprint in '$line'."
continue
fi
printf '%s\n' "Adding fingerprint: $fpr"
if [ -n "$fingerprints" ]; then
fingerprints="$fingerprints\n"
fi
fingerprints="${fingerprints}${fpr}"
find_pub=true
continue
fi
fi
} 3<&-
done 3< "$tmp"
rm -f "$tmp"
# collapse newlines to spaces
fingerprints="$(printf '%b\n' "$fingerprints" | tr '\n' ' ')"
# Only export necessary elements of public keys
export_filter="drop-subkey='secret == 0 || expired == 0 || revoked == 0 || disabled == 0'"
# export the cleanest public keys possible selected by their fingerprint strings
if ! gpg --export --armor --output "$EXPORTED_KEYS_FILE" --export-filter "$export_filter" --export-options export-clean $fingerprints; then
printf >&2 '%s\n' "Failed to export public keys to $EXPORTED_KEYS_FILE"
exit 1
fi
printf '%s\n' "Exported public keys to file $EXPORTED_KEYS_FILE"
}
export_public_gpgkeys ".devcontainer/publickeys.asc"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment