Repository navigation
refactor(actions): step 12, move gpg-verify-trust-chain checks into a script - #58
seantronsen wants to merge 5 commits into
Conversation
| fi | ||
|
|
||
| # shellcheck disable=SC2086 # intentional word splitting | ||
| set -- $FILES |
There was a problem hiding this comment.
Just a quick question: are we purposefully supporting glob expansion here?
E.g. FILES='checksums[1].txt' expanding into checksums1.txt.
4dc5bc0 to
a696a31
Compare
a696a31 to
2d9aa88
Compare
| gpg --batch --with-colons --check-sigs "$subject" 2>/dev/null \ | ||
| | awk -F: -v id="${signer_id^^}" -v prefix="${prefix,,}" ' | ||
| $1=="uid" { name=tolower($10); ok=(prefix=="" || name==prefix || index(name, prefix " ")==1) } | ||
| $1=="sig" && $2=="!" && ok && toupper($5)==id { found=1 } |
There was a problem hiding this comment.
Moved from #50 (#50 (comment)), originally posted by @synackd:
With this implementation, revoked and expired certifications still pass verification.
certified_by() currently accepts any cryptographically valid certification from the expected issuer:
$1=="sig" && $2=="!" && ok && toupper($5)==id { found=1 }GPG's ! does not establish that the certification is still effective. sig:! proves the certification's cryptographic validity, but the code ignores certification expiry and revocation records. Expired certifications remain sig:!, and revoked certifications remain present alongside a rev:! record. usable() checks the primary keys, so it does not catch either case. Consequently, a repo key whose authorization the root has revoked can still pass release admission.
I reproduced this with isolated keys using GPG 2.2.41:
valid-chain exit=0
expired-cert exit=0 # expected failure
revoked-unexpired-cert exit=0 # expected failure
wrong-repo-control exit=1
tampered-file-control exit=1
For the revocation case, all keys were unexpired. After the root revoked its certification using:
gpg --batch --yes --local-user "$master_fpr" \
--quick-revoke-sig "$repo_fpr" "$master_fpr"
--check-sigs emitted both records:
rev:!::22:F36D951D3599FDDF:1793059097:...
sig:!::22:F36D951D3599FDDF:1793059096:...
Certification expiry and revocation should be checked per-UID and per-issuer, for both links in the chain. An awk fix prototype:
# Pass the full expected issuer fingerprint and the current time.
awk -F: -v issuer="${1^^}" -v prefix="${3,,}" \
-v now="$(date +%s)" '
function finish_uid() {
if (eligible && cert_time > rev_time)
found = 1
}
$1 == "uid" {
finish_uid()
cert_time = rev_time = 0
name = tolower($10)
eligible = ($2 != "r" && $2 != "e") &&
(prefix == "" || name == prefix ||
index(name, prefix " ") == 1)
next
}
$1 == "pub" || $1 == "sub" {
finish_uid()
eligible = 0
cert_time = rev_time = 0
next
}
eligible && $2 == "!" && toupper($13) == issuer {
if ($1 == "rev" && $11 ~ /^30/ && $6 > rev_time)
rev_time = $6 + 0
if ($1 == "sig" && $11 ~ /^1[0-3]/ &&
$6 <= now && ($7 == "" || $7 == 0 || $7 > now) &&
$6 > cert_time)
cert_time = $6 + 0
}
END {
finish_uid()
exit !found
}
'2d9aa88 to
28d312e
Compare
Signed-off-by: Sean Tronsen <sean.tronsen@gmail.com>
Signed-off-by: Sean Tronsen <sean.tronsen@gmail.com>
28d312e to
d0e9585
Compare
Signed-off-by: Sean Tronsen <sean.tronsen@gmail.com>
Signed-off-by: Sean Tronsen <sean.tronsen@gmail.com>
106d615 to
b20b294
Compare
Signed-off-by: Sean Tronsen <sean.tronsen@gmail.com>
Description
Moves
gpg-verify-trust-chain's checks out of the action's inlinerun:intoscripts/gpg-verify-trust-chain.sh, so they can run outside Actions. The distributor (central repo for #43) will fetch the script by commit SHA and call it directly, which lets its admit job verify, download, and check a release in one script instead of three phases wrapped around the action.scripts/gpg-verify-trust-chain.sh: the action's logic, unchanged. Takes the inputs as environment variables (MASTER_PUBLIC_KEY,MASTER_FINGERPRINT,REPO_KEY_FILE,SIGNING_KEY_FILE,FILES,REPO); unset ones default to the action's defaults, explicitly empty ones still fail as before. On success it appendsfingerprint-{master,repo,signing}=<fpr>toOUTPUT_FILE(default stdout). Needsgpg; installs stay in the action.actions/gpg-verify-trust-chain/action.yml: the verify step now calls the script withOUTPUT_FILE="$GITHUB_OUTPUT". Inputs, outputs, and the GnuPG install step are unchanged.gpg-sign-artifacts: repins the action from the orphanedb95f3bdto the refactored one.Stacked on #53.
Behavior changes
MASTER_PUBLIC_KEY/MASTER_FINGERPRINTinstead ofpublic-key-master/fingerprint-master). Nothing else changes for callers.Testing
Testbed: OpenCHAMI/github-actions-testbed#17 (
signon av*tag push).Locally in
ubuntu:24.04with a real master -> repo -> signing key chain: a valid chain passes and writes all three fingerprints; a wrong master fingerprint, a wrongREPO, and a tamperedchecksums.txteach fail with the expected error. The action's new run line, withGITHUB_ACTION_PATH/GITHUB_OUTPUTsimulated, passes and writes the outputs; an emptyFILESfails. Not yet run in Actions;gpg-sign-artifactsexercises the action on av*tag push in the testbed.Checklist
make test(or equivalent) locally and all tests passgit commit -s) with my real name and email<filename>.licensesidecarLICENSES/directoryType of Change