Skip to content

Pin external APT signing keys by primary-key fingerprint - #172

Merged
BrettKinny merged 2 commits into
mainfrom
hardening/apt-key-fingerprints
Oct 1, 2026
Merged

BrettKinny merged 2 commits into
mainfrom
hardening/apt-key-fingerprints

Conversation

@BrettKinny

Copy link
Copy Markdown
Collaborator

Summary

Dockerfile step "2a. External APT repos" downloaded the GitHub CLI and Eza APT signing keys with no fingerprint check (Eza's from the mutable main branch), so a changed or compromised key URL would silently become a trusted signer for the build.

  • New ARGs GH_CLI_KEY_FINGERPRINTS and EZA_KEY_FINGERPRINTS (space-separated, upper-case, 40-hex).
  • verify_apt_key extracts primary-key fingerprints only (pub → following fpr; subkeys ignored) via gpg --show-keys --with-colons in a throwaway GNUPGHOME, and requires the downloaded keyring to contain exactly the expected set. Mismatches fail the build with expected/actual output, before any keyring is installed into /etc/apt/keyrings or any source list is written.
  • Keys now download to files (curl -o) rather than curl | tee, so a failed fetch can no longer pass silently.
  • The Eza key URL is pinned to eza-community/eza@1cff499fb218f2a133aafa01824ddab090f4389e, the only commit that touches deb.asc. The file at that commit is byte-identical to main.
  • Existing cleanup is unchanged: repos and keyrings are removed after gh/eza install.
  • New tests/test-apt-key-policy.sh (picked up by the find tests ... test-*.sh loop in build.yml) checks that every downloaded key is verified before use, that the ARG values are 40-hex, and that the Eza URL is SHA-pinned. I mutation-checked it against six Dockerfile mutations and against origin/main, and it failed in every case.
  • SECURITY.md: updated the Tool-tier table row, and added a section on the control and the out-of-band rotation procedure.

Pinned fingerprints

Key Primary fingerprint(s) Cross-check
GitHub CLI 2C6106201985B60E6C7AC87323F3D4EA75716059 (expired 2026-09, still shipped in the keyring), 7F38BBB59D064DBCB3D84D725612B36462313325 (current) Both are listed in cli/cli docs/install_linux.md. The downloaded keyring's SHA-256 (6084d5d7…811b) matches the doc. The live cli.github.com/packages/dists/stable/Release.gpg issuer is 7F38BB…1325.
Eza (deb.gierens.de) 1548BC8A4B4D2688F9B0DAF7EC29E2090CE3FD43 The live deb.gierens.de/dists/stable/Release.gpg issuer fpr matches. deb.asc has a single commit in history (2023-08-20). keys.openpgp.org and keyserver.ubuntu.com don't host this key, and eza's INSTALL.md publishes no fingerprint.

Test plan

  • podman build --build-arg SQUAREBOX_VERSION=dev -t squarebox:keytest . succeeds and logs Verified … for both keys. gh 2.102.0 and eza 0.23.5 are installed, and /etc/apt/keyrings plus the extra source lists are empty afterwards.
  • scripts/e2e-test.sh smoke against that image: 62/62 pass.
  • Negative: --build-arg GH_CLI_KEY_FINGERPRINTS=000…0 fails at step 2a with GitHub CLI APT signing key fingerprint mismatch.
  • Negative: an Eza expected set with an extra fingerprint fails with Eza APT signing key fingerprint mismatch, which shows the comparison is an exact set and not a subset.
  • All 20 host tests/test-*.sh pass. shellcheck (koalaman/shellcheck:stable, severity=warning) is clean on the new test.

Note: GitHub's keyring still includes the expired 2C61… key. When GitHub drops it, this build will fail by design until the ARG is updated after review.

🤖 Generated with Claude Code

BrettKinny and others added 2 commits October 1, 2026 12:13
The image build trusted the GitHub CLI and Eza APT signing keys on download
alone, and fetched the Eza key from the mutable eza `main` branch. A changed
or compromised key URL would silently become a trusted build signer.

- Add GH_CLI_KEY_FINGERPRINTS / EZA_KEY_FINGERPRINTS ARGs holding the full
  40-hex primary-key fingerprints, and require each downloaded keyring to
  contain exactly that primary-key set (subkeys ignored) before it is
  installed under /etc/apt/keyrings or referenced by a source list.
- Download keys to files instead of piping curl, so a failed fetch fails.
- Pin the Eza key URL to commit 1cff499f (the only commit touching deb.asc).
- Add tests/test-apt-key-policy.sh static policy test.
- Document the control and the out-of-band rotation procedure in SECURITY.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@BrettKinny
BrettKinny merged commit 3af2940 into main Oct 1, 2026
4 checks passed
@BrettKinny
BrettKinny deleted the hardening/apt-key-fingerprints branch October 1, 2026 04:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant