Skip to content

ci(validate): fail on an expired key in security/ - #211

Open
bgauduch wants to merge 10 commits into
masterfrom
ci/oracle-key-expiry
Open

bgauduch wants to merge 10 commits into
masterfrom
ci/oracle-key-expiry

Conversation

@bgauduch

@bgauduch bgauduch commented Sep 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

--fast fails when a key under security/ has an expired primary key or no unexpired signing key. Expired historical subkeys are allowed. The pass line prints the earliest expiry.

The check exists because the tool it guards cannot report the condition: gpg --verify exits 0 for a good signature made by an expired key, so both vendor keys lapsed under a green build — hashicorp.asc for five months, awscliv2.asc for three years — and nothing reported it.

case result
refreshed keys (#210) PASS vendor keys in security/ are unexpired (earliest 2027-07-01), exit 0
an expired key FAIL security/<key>.asc expired on <date>, run completes, exit 1
malformed .asc FAIL ... is not a readable OpenPGP key

Based on fix/refresh-vendor-gpg-keys (#210), which refreshes both keys; GitHub retargets this to master when that merges. No longer a draft: with #210 in place the check passes, so ADR-0016's "always green on master" criterion holds.

The function always returns 0 so one failure does not abort the run under set -e; the other checks still abort, noted on #152 P2.

Roadmap phase / closes: refs #152 (P2), #106 (P0 action 2)

Breaking change

None.

Architecture Decision Record

  • This PR adds/updates an ADR under docs/adr/ (link: ADR-0016, amended)
  • This PR is not a structural change — no ADR needed

The amendment records the accepted cost: the repository does not control vendor key material, so a vendor letting a key lapse turns --fast red on master until the copy is refreshed. That is the intended reading, not a false positive.

Checklist

  • PR title follows Conventional Commits
  • Commits are scoped and reviewable
  • Docs / roadmap updated if behaviour or policy changed
  • Docs point to their single home (no restated facts); intros/sections earn their space
  • Touches only files within the declared phase scope

security/hashicorp.asc had lapsed on 2026-04-18, and nothing said so:
gpg --verify returns exit 0 for a good signature made by an expired key,
so every Terraform download kept verifying and the build kept passing while
the expiry bound had quietly gone. The key still in use, re-fetched from
the vendor, now carries a self-signature valid to 2030-03-01.

Same key, later expiry: the primary fingerprint is unchanged
(C874011F0AB405110D02105534365D9472D7468F), so the trust root does not
move. Verified before committing — every SHA256SUMS signature in security/
still reports a good signature under the refreshed copy and no longer warns
expired, including the 1.15.9 and 1.16.3 material added in #205. The one
signing subkey that expired in 2022 is a rotated historical subkey and is
carried by the vendor's own copy.

security/awscliv2.asc is expired too, since 2023-09-17, and is not fixed
here: AWS publishes its key only in the install guide, and that host is
unreachable from a hosted session. It needs the maintainer.
The oracle could not see the condition that cost this repository its
supply-chain guarantee. gpg --verify returns exit 0 for a good signature
made by an expired key, so both vendor keys lapsed under a green build:
hashicorp.asc for five months, awscliv2.asc for three years, neither
reported by anything. An expired key is only visible from outside the
verification it weakens, which is what this oracle is for.

The assertion, per key under security/: the primary key is unexpired, and
at least one signing-capable key remains unexpired. A rotated historical
signing subkey is expected to be expired and is not a finding, which the
vendors' own copies confirm. The pass line carries the earliest expiry
across the keys, so the next lapse is readable before it lands.

Fails closed, and amends ADR-0016 to record the cost: a vendor letting a
key lapse turns --fast red on master until the copy is refreshed. That is
the intended reading, not a false positive.

Refs #152 (P2 candidate list, broadened here from the HashiCorp key to
every key under security/)

Copy link
Copy Markdown
Owner Author

validate is red, and it is this PR's own check doing exactly what it was written to do.

FAIL security/awscliv2.asc expired on 2023-09-17
validate: FAILED

Blocker: the AWS CLI key itself. It cannot be refreshed from an agent session — AWS publishes it only inline in the CLI install guide, and docs.aws.amazon.com is blocked by the egress proxy, as are keys.openpgp.org and the Wayback host. keyserver.ubuntu.com answers but serves a copy that is itself stale (expiry 2026-07-07, also past). No re-run changes this, so I am not spending one.

What unblocks it: the current key block from the install guide, committed to security/awscliv2.asc, with the fingerprint checked to read FB5DB77FD5C118B80511ADA8A6310ACC4672475C — the existing trust root, which must not move. This check then goes green on its own, and #210 has already done the same for the HashiCorp half.

Staying a draft until then: ADR-0016's acceptance criterion is that --fast is always green on master, so merging this before the key would break the rule this PR exists to enforce.


Generated by Claude Code

security/awscliv2.asc had lapsed on 2023-09-17, three years, and failed
silently the same way the HashiCorp key did: gpg --verify returns exit 0
for a good signature made by an expired key, so every AWS CLI download
kept verifying and nothing reported that the expiry bound had gone.

Same key, later expiry: the primary fingerprint is unchanged
(FB5DB77FD5C118B80511ADA8A6310ACC4672475C), so the trust root does not
move; the self-signature now runs to 2027-07-01. Taken from the CLI
install guide, the only place AWS publishes it.

Verified before committing: the committed signatures for 2.36.49 on both
published architectures report a good signature under the refreshed copy
and no longer warn expired, checked against the real archives downloaded
from awscli.amazonaws.com.

The repo's copy was two generations behind. 2023-09-17, then the
keyserver's 2026-07-07, now 2027-07-01.
@bgauduch
bgauduch marked this pull request as ready for review September 24, 2026 14:32
bgauduch added a commit that referenced this pull request Sep 25, 2026
## Summary

Both keys under `security/` had lapsed, and neither failure was visible:
`gpg --verify` exits 0 for a good signature made by an expired key, so
every download kept verifying while the expiry bound was gone.

| key | was | now | fingerprint |
|---|---|---|---|
| `hashicorp.asc` | expired 2026-04-18 | 2030-03-01 |
`C874011F0AB405110D02105534365D9472D7468F`, unchanged |
| `awscliv2.asc` | expired 2023-09-17 | 2027-07-01 |
`FB5DB77FD5C118B80511ADA8A6310ACC4672475C`, unchanged |

Same keys, later expiries — neither trust root moves. Sources:
HashiCorp's `/.well-known/pgp-key.txt`, and the CLI install guide for
AWS, the only place it publishes one. The AWS copy was two generations
behind: 2023-09-17, then the keyserver's 2026-07-07, now 2027-07-01.

Verified before pushing: every `SHA256SUMS` signature under `security/`
(1.14.9, 1.15.9, 1.16.3) and both published architectures' AWS CLI
signatures for 2.36.49 report a good signature under the refreshed keys
and no longer warn `expired` — the AWS archives downloaded from
`awscli.amazonaws.com` for the check. `validate.sh --fast` green;
`build-test` green on the full matrix.

Unblocks #211, whose check now reports `PASS vendor keys in security/
are unexpired (earliest 2027-07-01)`.

Roadmap phase / closes: Phase 3 — closes #106 P0 action 2

## Breaking change

None.

## Architecture Decision Record

- [ ] This PR adds/updates an ADR under `docs/adr/` (link: ____)
- [x] This PR is **not** a structural change — no ADR needed

Key material only; no policy change.

## Checklist

- [x] PR title follows Conventional Commits
- [x] Commits are scoped and reviewable
- [x] Docs / roadmap updated if behaviour or policy changed
- [x] Docs point to their single home (no restated facts);
intros/sections earn their space
- [x] Touches only files within the declared phase scope
Base automatically changed from fix/refresh-vendor-gpg-keys to master September 25, 2026 05:40
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