docs(supply-chain): document monitoring your own signing identity - #1959
docs(supply-chain): document monitoring your own signing identity#1959lockwobr wants to merge 6 commits into
Conversation
Coverage ReportCoverage data unavailable for this run. This can happen if the qualification workflow was cancelled or failed before generating coverage artifacts. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds documentation for monitoring signing identities through Rekor, covering keyless, KMS, public-good, private, and disabled transparency-log configurations. Includes identity extraction commands, checkpoint handling, response guidance, related references, and a pointer from artifact verification documentation. Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/integrator/supply-chain-verification.md`:
- Around line 712-714: Revise step “Re-verify what you already published” to
clarify that --require-creator only confirms signer identity, not artifact
authenticity after compromise. Instruct users to compare each artifact’s digest
and provenance with an independent release record, and quarantine any
unrecognized artifacts.
- Around line 507-518: Update the signing-mode table to separate private Fulcio
selection from Rekor selection: keep a private-Fulcio row whose log reflects the
independently selected Rekor v2 default, and add a distinct Rekor v1 row that
applies only when --rekor-url is used. Ensure the surrounding text and row
descriptions do not imply --fulcio-url selects Rekor v1.
- Around line 660-665: Update the certSubject and issuer regex values in the
monitoredValues example to anchor both patterns with ^ and $, ensuring only the
exact certificate identity subject and issuer are matched.
- Around line 581-586: Update the workflow examples in supply-chain verification
documentation: add a reviewed full-length SHA ref for the NVIDIA/aicr checkout,
replace the actions/setup-go@v7 tag with its reviewed full-length SHA, and
replace the v1 reusable workflow’s `@main` reference with its reviewed full-length
SHA.
- Around line 593-601: Update the checkpoint artifact selection in the gh api/jq
pipeline to restrict candidates to artifacts produced by trusted runs from this
repository’s main branch before sorting and selecting the newest unexpired
artifact. Preserve the existing no-checkpoint handling and download flow, while
ensuring fork PR artifacts cannot be selected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 00da2987-4f24-4b2b-a172-551f7a60c58c
📒 Files selected for processing (2)
docs/integrator/supply-chain-verification.mddocs/user/artifact-verification.md
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/integrator/supply-chain-verification.md`:
- Around line 643-647: Update the supply-chain verification guidance to
explicitly require pinning the source checked out by the earlier git clone
example for tools/rekor-monitor. In addition to Actions, reusable workflows, and
ref values, instruct users to use a reviewed full-length commit SHA, or an
intentionally reviewed release ref, instead of the mutable default branch.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 3370b051-b4c2-4909-b6b3-14434496738d
📒 Files selected for processing (1)
docs/integrator/supply-chain-verification.md
ArangoGutierrez
left a comment
There was a problem hiding this comment.
Solid section — the threat model is right, and I confirmed the two stated coverage gaps and the fork-rejection jq against the code (the select clauses match the production workflow exactly). Six things worth fixing, the version pin being the big one.
- The pinned tag predates the contract described right below it: at v0.18.0 the monitor never prints a CLASSIFICATION= line, writes no .scan/.stall companions, and realMain returns only 0, 1 or 2 — never 3. Following this verbatim wires a Sigstore or network blip to exit 1, which this page defines as a security finding. (docs/integrator/supply-chain-verification.md:544)
- Same version gap in the workflow example: at v0.18.0 there is no CLASSIFICATION= output to branch on, and the .scan/.stall paths uploaded below are never produced — which if-no-files-found: ignore then quietly hides. The CLASSIFICATION= hardening advice further down cannot be applied at this ref. (docs/integrator/supply-chain-verification.md:592)
- This pairs an email SAN with the GitHub Actions issuer, a combination Fulcio never issues — under that issuer the SAN is the workflow identity URL, exactly as the table above states and as AICR's own monitor is configured. Upstream ANDs subject and issuer, so a copied config matches nothing and the monitor stays green while watching an identity that cannot exist. Same pairing in the shell example above. (docs/integrator/supply-chain-verification.md:577)
- issuers is matched against the certificate's OIDC issuer extension (1.3.6.1.4.1.57264.1.8), which carries the identity provider that authenticated the signer — not the Fulcio CA that issued the cert. A private Fulcio URL here never matches; this wants the IdP issuer. (docs/integrator/supply-chain-verification.md:689)
- git clone --branch only accepts a branch or tag name, so this instruction cannot be followed as written — a SHA fails with 'fatal: Remote branch not found in upstream origin'. A clone followed by git checkout would work. (docs/integrator/supply-chain-verification.md:648)
- Worth a one-line note on the branch assumption: this literal main rejects every artifact in a repo whose default branch is named differently, leaving the monitor permanently in first-run state — baselining and scanning nothing, on every run, without saying so. (docs/integrator/supply-chain-verification.md:614)
yuanchen8911
left a comment
There was a problem hiding this comment.
This is a well-researched section and the signing-mode table, tool flag descriptions, upstream workflow inputs, and coverage-gap framing all check out against the code. But the two copy-paste workflows and the v1/private-log guidance have several silent false-negative paths, and the examples pin a tool version that lacks the behavior the text documents. Since silent false negatives are exactly what this section exists to prevent, these need fixing before merge. Details inline; the short list:
- Both examples pin v0.18.0, which predates the CLASSIFICATION/exit-code/resume behavior the prose documents.
- The v2 example's SAN/issuer pair can never match a real certificate, and the v1 example puts the Fulcio CA URL in the OIDC issuers field. Both configurations run green while matching nothing.
- A missing or expired checkpoint artifact silently re-baselines the monitor at the current head, permanently skipping the gap.
- The tool has no acknowledgment path for legitimate matches, so an actively signing identity wedges the monitor into permanent re-alerting.
- The upstream reusable workflow cannot sustain identity coverage on the public-good v1 log and cannot verify a private v1 log, so the "Monitoring Rekor v1 and a private Rekor" section recommends a tool that works for neither of its two headline cases.
- Private Fulcio into the public v2 log is a third coverage gap: tools/rekor-monitor silently skips entries whose chain doesn't validate against the public Fulcio roots.
- The incident-response steps assume interactive keyless with a public bundle; the containment and re-verification guidance doesn't hold for CI workload identities, KMS, or evidence artifacts.
njhensley
left a comment
There was a problem hiding this comment.
Reviewed with a multi-persona panel (docs-accuracy, security/supply-chain, CI-DX) plus adversarial verification against the resolved code at a5335c96.
The standing feedback from @yuanchen8911 and @ArangoGutierrez already covers the crux issues — the v0.18.0 pin lacking the CLASSIFICATION=/exit-3/.scan/.stall contract, the SAN/issuer pairs that can never match, git clone --branch <sha>, the hard-coded head_branch == "main", and the coverage-gap framing — and I independently reproduced those against the code; they still hold at this head. Rather than restate them, the inline comments below are only the net-new findings those reviews did not raise, so this isn't piling on.
Net-new (inline):
- 🟠 The v1 example runs an unpinned
@mainthird-party reusable workflow withid-token: write— the copied block can mint OIDC as the reader's repo. Production grants no such token and pins nothing to@main. - 🟡 The v1 workflow's top-level
permissions: read-allis over-broad and inconsistent with the v2 example'spermissions: {}. - 🟡 The signing-mode table keys its KMS/private/v1 rows on flags (
--rekor-url,--signing-key,--signing-config,--tlog-upload) that exist only onaicr bundle— thevalidate --push/evidence publish/evidence signpaths are keyless-only, always Rekor v2 (pkg/cli/validate_evidence.go:117). - 🔵 The v2 example drops production's
workflow_dispatch, theretention-daysrationale, and the retry/atomic-write fetch wrapper.
Nothing here is a hard blocker on its own; the merge-blocking items are the ones already in @yuanchen8911's standing review. This is really strong, carefully-researched work — the fix set is small and mostly mechanical.
Addresses review on #1959. The section documented behavior the pinned version could not perform and examples that could never match. - Repin both examples to d4f7bef (#1929). v0.18.0 predates the documented contract: no CLASSIFICATION= line, no .scan/.stall state, and exit codes 0/1/2 with no 3, so a network blip would have read as a security finding. The commit is in no tag. Use git checkout --detach, since git clone --branch resolves only branches and tags. - Fix the identity pairs. Both examples paired an email SAN with the GitHub Actions issuer, which Fulcio never issues together; matching is AND-ed, so a copied config matched nothing and the monitor stayed green while watching an identity that cannot exist. - Correct the v1 issuers field: it matches the certificate's OIDC issuer extension, the IdP that authenticated the signer, not the Fulcio CA. - Make bootstrap explicit. A hard-coded main filter and artifact expiry both silently re-baselined and exited clean. Scheduled runs now fail closed when no checkpoint is found; bootstrap is a workflow_dispatch input; the branch comes from the repository's actual default branch. - Demote the Rekor v1 cases to coverage gaps: upstream's v1 identity scan cannot keep up with log growth, and private v1 fails at verifier setup because the checkpoint key is resolved from the public TUF root. Keep the config format and KMS fingerprint content. - Add a third gap: private Fulcio into public-good v2 is monitorable by neither tool, since entries failing chain validation are skipped. - State the alerting limitation: a match holds the cursor and re-alerts every run, and the only suppression is --known-tags-file. - SHA-pin the upstream reusable workflow in the example itself. It runs with id-token: write, which upstream requires, so unpinned third-party code could mint an OIDC token as the reader's repo. - Tighten the v1 example to permissions: {}, add workflow_dispatch to the v2 example, scope flag-keyed table rows to aicr bundle, split the response guidance by signing mode, and fix the retention-days rationale. Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/integrator/supply-chain-verification.md`:
- Around line 722-724: Update the private Rekor v1 documentation example
following the unsupported-workflow guidance: remove the private `url` value and
clearly label the configuration-shape block as non-turnkey, or replace it with a
direct-binary example that uses `--tuf-repository` and `--tuf-root-path`. Ensure
no copyable workflow implies private-Rekor monitoring is supported.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: d5d47c41-53af-44a4-b89d-61369a0c51c5
📒 Files selected for processing (1)
docs/integrator/supply-chain-verification.md
Addresses review on #1959. The section documented behavior the pinned version could not perform and examples that could never match. - Repin both examples to d4f7bef (#1929). v0.18.0 predates the documented contract: no CLASSIFICATION= line, no .scan/.stall state, and exit codes 0/1/2 with no 3, so a network blip would have read as a security finding. The commit is in no tag. Use git checkout --detach, since git clone --branch resolves only branches and tags. - Fix the identity pairs. Both examples paired an email SAN with the GitHub Actions issuer, which Fulcio never issues together; matching is AND-ed, so a copied config matched nothing and the monitor stayed green while watching an identity that cannot exist. - Correct the v1 issuers field: it matches the certificate's OIDC issuer extension, the IdP that authenticated the signer, not the Fulcio CA. - Make bootstrap explicit. A hard-coded main filter and artifact expiry both silently re-baselined and exited clean. Scheduled runs now fail closed when no checkpoint is found; bootstrap is a workflow_dispatch input; the branch comes from the repository's actual default branch. - Demote the Rekor v1 cases to coverage gaps: upstream's v1 identity scan cannot keep up with log growth, and private v1 fails at verifier setup because the checkpoint key is resolved from the public TUF root. Keep the config format and KMS fingerprint content. - Add a third gap: private Fulcio into public-good v2 is monitorable by neither tool, since entries failing chain validation are skipped. - State the alerting limitation: a match holds the cursor and re-alerts every run, and the only suppression is --known-tags-file. - SHA-pin the upstream reusable workflow in the example itself. It runs with id-token: write, which upstream requires, so unpinned third-party code could mint an OIDC token as the reader's repo. - Tighten the v1 example to permissions: {}, add workflow_dispatch to the v2 example, scope flag-keyed table rows to aicr bundle, split the response guidance by signing mode, and fix the retention-days rationale. Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
d3fc34e to
7f05dc2
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/integrator/supply-chain-verification.md`:
- Around line 558-561: Update both tag-based examples in
docs/integrator/supply-chain-verification.md:558-561 and
docs/integrator/supply-chain-verification.md:677-683 to use legitimate tag data
by generating a known-tag file for the shell example and passing that same file
via --known-tags-file in both the shell command and scheduled workflow.
In `@docs/user/artifact-verification.md`:
- Around line 358-363: Update the verification documentation paragraph around
“transparency log” and “signing identity” to qualify unexpected entries as
possible indicators of unauthorized use, not proof, since authorized automation
or approved signers may share the identity. Describe Rekor as the durable source
for monitoring such activity while preserving the existing explanation of
keyless signing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 273ec689-8d84-49d8-a011-f91995ca3bfa
📒 Files selected for processing (2)
docs/integrator/supply-chain-verification.mddocs/user/artifact-verification.md
| Verification is only half the picture. It proves that an artifact you hold came | ||
| from the identity you expect, but it cannot tell you that somebody else signed | ||
| something *as you*: an entry in the transparency log under your signing identity | ||
| that you did not produce means the identity was used without you. Keyless signing | ||
| leaves no local trace of a signing event, so the log is the only place that | ||
| misuse shows up. For what each signing mode records and how to watch for it, see |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Qualify the misuse claim.
An unexpected entry can indicate unauthorized use. It does not prove unauthorized use because authorized automation or another approved signer may use the same identity. Replace the absolute wording in Lines 360-363 with a qualified statement, and describe Rekor as the durable monitoring source.
Proposed wording
- that you did not produce means the identity was used without you. Keyless signing
- leaves no local trace of a signing event, so the log is the only place that
- misuse shows up.
+ that you did not authorize may indicate unauthorized use. Keyless signing
+ leaves no local trace of a signing event, so the log provides the durable
+ record for detecting this misuse.This matches Issue #1462, which requests that an unexpected entry may indicate unauthorized use.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Verification is only half the picture. It proves that an artifact you hold came | |
| from the identity you expect, but it cannot tell you that somebody else signed | |
| something *as you*: an entry in the transparency log under your signing identity | |
| that you did not produce means the identity was used without you. Keyless signing | |
| leaves no local trace of a signing event, so the log is the only place that | |
| misuse shows up. For what each signing mode records and how to watch for it, see | |
| Verification is only half the picture. It proves that an artifact you hold came | |
| from the identity you expect, but it cannot tell you that somebody else signed | |
| something *as you*: an entry in the transparency log under your signing identity | |
| that you did not authorize may indicate unauthorized use. Keyless signing | |
| leaves no local trace of a signing event, so the log provides the durable | |
| record for detecting this misuse. For what each signing mode records and how to watch for it, see |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/user/artifact-verification.md` around lines 358 - 363, Update the
verification documentation paragraph around “transparency log” and “signing
identity” to qualify unexpected entries as possible indicators of unauthorized
use, not proof, since authorized automation or approved signers may share the
identity. Describe Rekor as the durable source for monitoring such activity
while preserving the existing explanation of keyless signing.
yuanchen8911
left a comment
There was a problem hiding this comment.
All previous findings were fixed and the threads are resolved. There's a blocking issue remaining, please take a look: both examples watch a refs/tags/.* release identity but never pass --known-tags-file (inline comment below). Two small same-pass fixes are also inline, and one item to defer.
On your recipe sign-catalog question: keep the text scoped to the user-facing surface as you have it. Documenting a Hidden: true CI-only command in an integrator doc adds noise without helping the reader.
One follow-up rather than a change here: neither the doc examples nor the production workflow set persist-credentials: false on actions/checkout. The example faithfully mirrors production, so I'd rather harden both together in a focused follow-up than have the doc diverge. Exposure is low (the persisted token is scoped to contents: read + actions: read).
| --file checkpoint_v2.txt \ | ||
| --restore-zip checkpoint.zip \ | ||
| --cert-subject "${CERT_SUBJECT}" \ | ||
| --cert-issuer "${CERT_ISSUER}" |
There was a problem hiding this comment.
This is blocking, and applies equally to the shell example at lines 558-562: both examples watch a refs/tags/.* release identity but never pass --known-tags-file. Per the limitation stated just above, on an identity match the tool holds the cursor and returns without persisting the chunk's progress, so the reader's first legitimate release after baseline leaves the monitor re-alerting every run and — the part that matters — entries in later chunks remain unscanned until the finding is triaged. The only triage path is the flag the examples omit, so a verbatim copy suspends the monitor's core function on the first ordinary use of the watched identity. The prose says a tag-bearing release identity is turnkey via --known-tags-file; the copyable blocks for exactly that identity should wire it.
Two constraints on the fix:
-
The allowlist must come from an authoritative record of legitimately signed releases, not raw repository tags — an attacker-pushed or never-signed tag in
git tag --listwould be pre-suppressed. Production's own step is the copyable pattern: it derivesknown-tags.txtfrom the release workflow's completed run history (.github/workflows/rekor-monitor.yaml, theruns.jsontoknown-tags.txtstep), precisely because repo tags are deleted for ephemeral RCs while run history persists (Rekor v2 monitor: release identity / log consistency alert #1902). -
Worth carrying over production's caveat too: an attacker re-signing an existing release tag is suppressed by this mechanism; a per-tag entry-count/provenance check is the tracked follow-up.
For the shell example, a comment showing the run-history derivation (or pointing at production's step) is enough; for the workflow example, add the fetch step and the flag.
There was a problem hiding this comment.
Agreed, and this is the same failure mode this PR keeps hitting: the prose states a limitation correctly, then the copyable block below it does not account for it. The section says a tag-bearing release identity is turnkey via --known-tags-file, and then neither example passed it. Fixed in 247ceed.
Your second constraint is the part I would have got wrong on my own, so I took production verbatim rather than improvising. The workflow example now carries the Fetch signed release tags step from .github/workflows/rekor-monitor.yaml, including its jq unchanged:
jq -rs '[.[].workflow_runs[] | select(.head_branch != null) | .head_branch] | unique | .[]' runs.json > known-tags.txt
with RELEASE_WORKFLOW_FILE in env and production's note to change it and CERT_SUBJECT together, plus the rationale that the allowlist must come from an authoritative record rather than git tag --list and that run history persists after ephemeral RC tags are deleted (#1902). The one substitution is plain gh api --paginate in place of .github/scripts/gh-api-retry.sh, since a reader's repo has no such script; the jq -rs slurp is kept because --paginate emits one object per page. The shell example gets the flag plus a comment stating the derivation and the never-from-git tag --list constraint.
Production's caveat is carried as its own paragraph: an attacker re-signing an existing release tag is suppressed because that tag is legitimately allowlisted, and the allowlist keys on a completed release run rather than proof the signing step succeeded. Cited #1887 as the tracked follow-up, which is what docs/contributor/maintaining.md names for the same gap.
| tree head and scans nothing, and every later run scans only the window added | ||
| since. The tool writes two companions alongside it, `<file>.scan` and | ||
| `<file>.stall`, holding partial progress and catch-up trend so that a large | ||
| backlog is scanned across several bounded runs; all three must survive between |
There was a problem hiding this comment.
Small accuracy fix: "all three must survive between runs or the scan restarts" is true of <file> and <file>.scan, but not .stall. A missing .scan makes readProgress() return 0 and the window rescans from the start; a missing .stall only resets the catch-up convergence history feeding the degraded classification — scan progress is untouched (checkpoint.go: readProgress vs readScanTrend). Suggest splitting the clause: the checkpoint and .scan must survive or the scan restarts; .stall only preserves the stall-detection history.
There was a problem hiding this comment.
Correct, thanks. Fixed in 247ceed. Verified before rewriting: progressPath() is .scan (checkpoint.go:112) and readProgress returns 0 when absent, so the window rescans from the start; stallPath() is .stall (checkpoint.go:161) and readScanTrend returns the zero value, feeding only bestRemaining/stall/passes for the degraded classification. The clause is now split so .scan carries the must-survive requirement and .stall is described as costing stall detection rather than scan progress.
| something *as you*: an entry in the transparency log under your signing identity | ||
| that you did not produce means the identity was used without you. Keyless signing | ||
| leaves no local trace of a signing event, so the log is the only place that | ||
| misuse shows up. For what each signing mode records and how to watch for it, see |
There was a problem hiding this comment.
"Means" overstates what the entry proves: an identity legitimately shared with CI automation or another approved signer produces entries you did not personally make, and the integrator guide's own "rule yourself out first" step anticipates exactly that. Suggest "may indicate the identity was used without you" (or similar), keeping the point that the log is the only durable record. The same absolute wording appears in docs/integrator/supply-chain-verification.md line 471 ("means your identity was used without you") — please fix both in the same pass.
There was a problem hiding this comment.
Agreed, and it was inconsistent with the section's own "rule yourself out first" step, which exists precisely because a shared or automated identity produces entries you did not personally make. Fixed in both files in 247ceed: may indicate that the identity was used without you in the integrator guide and the matching change in docs/user/artifact-verification.md, keeping the point that the log is the only durable record.
Users of the signing paths upload to a transparency log by default but were never told to watch that log for their own identity. An entry under your identity that you did not produce is the signal the identity was used without you, and for keyless signing it is the only signal: no private key on disk to audit, and a short-lived Fulcio certificate. Adds a Monitoring Your Signing Identity section to the integrator guide and a pointer from the user verification page. The upstream sigstore/rekor-monitor reusable workflow cannot cover the default path: it resolves its target from Sigstore's default signing config, which lists only Rekor v1, while AICR signing has defaulted to Rekor v2 since #1650. The section routes by log version instead, using tools/rekor-monitor for v2 and the upstream workflow for v1 and private v1, and states the two remaining coverage gaps rather than hiding them. Fixes #1462 Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
- Split the private-Sigstore table row: --fulcio-url selects the CA, not the log, so a private CA alone still publishes to public-good Rekor v2; only --rekor-url / --signing-config change the log. - Inline the checkpoint-artifact trust filter into the sample workflow (same-repo, head_branch main) instead of leaving it to prose. A fork pull_request run can upload a same-named artifact, and a poisoned checkpoint passes the consistency proof while collapsing the identity-scan window. - Anchor the Rekor v1 certSubject and issuers regexes, matching the guidance given for the v2 flags. Upstream compiles both with regexp.Compile and matches with MatchString. - Correct the response guidance: --require-creator narrows the set to artifacts signed by that identity but cannot separate yours from the attacker's within it, since a compromised identity satisfies the check. Establish legitimacy from a record the attacker does not control. - Pin the checkout ref for the cloned tool rather than floating on our default branch, and broaden the pinning advice to every external reference in the examples. Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
The shell example cloned NVIDIA/aicr's default branch, so the monitor being trusted could change between runs, and the pinning guidance enumerated actions, the reusable workflow, and ref: but not the clone. Pin the clone to v0.18.0 and extend the guidance to cover it. Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
Addresses review on #1959. The section documented behavior the pinned version could not perform and examples that could never match. - Repin both examples to d4f7bef (#1929). v0.18.0 predates the documented contract: no CLASSIFICATION= line, no .scan/.stall state, and exit codes 0/1/2 with no 3, so a network blip would have read as a security finding. The commit is in no tag. Use git checkout --detach, since git clone --branch resolves only branches and tags. - Fix the identity pairs. Both examples paired an email SAN with the GitHub Actions issuer, which Fulcio never issues together; matching is AND-ed, so a copied config matched nothing and the monitor stayed green while watching an identity that cannot exist. - Correct the v1 issuers field: it matches the certificate's OIDC issuer extension, the IdP that authenticated the signer, not the Fulcio CA. - Make bootstrap explicit. A hard-coded main filter and artifact expiry both silently re-baselined and exited clean. Scheduled runs now fail closed when no checkpoint is found; bootstrap is a workflow_dispatch input; the branch comes from the repository's actual default branch. - Demote the Rekor v1 cases to coverage gaps: upstream's v1 identity scan cannot keep up with log growth, and private v1 fails at verifier setup because the checkpoint key is resolved from the public TUF root. Keep the config format and KMS fingerprint content. - Add a third gap: private Fulcio into public-good v2 is monitorable by neither tool, since entries failing chain validation are skipped. - State the alerting limitation: a match holds the cursor and re-alerts every run, and the only suppression is --known-tags-file. - SHA-pin the upstream reusable workflow in the example itself. It runs with id-token: write, which upstream requires, so unpinned third-party code could mint an OIDC token as the reader's repo. - Tighten the v1 example to permissions: {}, add workflow_dispatch to the v2 example, scope flag-keyed table rows to aicr bundle, split the response guidance by signing mode, and fix the retention-days rationale. Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
The subsection states that a private Rekor v1 fails at verifier setup and that the reusable workflow cannot pass private trust material, then the example below it still set url to a private log and told readers to do the same. Remove the url, label the block as illustrating the monitoredValues shape rather than being a turnkey recipe, and point at the direct-binary path for private trust. Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
Both examples watch a tag-bearing release identity, and the section states that such an identity is turnkey via --known-tags-file, but neither example passed it. Since an identity match holds the cursor without persisting the chunk's progress, a verbatim copy leaves the monitor re-alerting every run and, more importantly, leaves later chunks unscanned until the finding is triaged. Derive the allowlist from the release workflow's completed run history, copying production's step verbatim, rather than from git tag --list: an attacker-pushed or never-signed tag would otherwise be pre-suppressed, and run history persists after ephemeral RC tags are deleted. Carry production's residual-gap caveat too, since an attacker re-signing an existing release tag is suppressed by this mechanism. Also correct two smaller inaccuracies: - Only the checkpoint and .scan must survive between runs; losing .stall costs stall detection, not scan progress, since it feeds the degraded classification rather than readProgress. - An entry you did not produce may indicate misuse rather than meaning it, since an identity shared with CI automation legitimately produces entries you did not personally make. Softened in both files. Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
7f05dc2 to
247ceed
Compare
Summary
Documents how a signer monitors the transparency log for their own signing identity, the producer-side complement to the consumer-side verification the rest of these docs cover. Adds a
Monitoring Your Signing Identitysection to the integrator guide and a pointer to it from the user-facing verification page.Motivation / Context
Users of
aicr bundle --attest,aicr validate --emit-attestation --push, and the evidence signing commands sign under their own identity and upload to a transparency log by default. AICR documented how to sign and how to verify, but never told signers to watch the log for their own identity. An entry under your identity that you did not produce is the signal that the identity was used without you, and for keyless signing it is the only signal: there is no private key on disk whose use could be audited, and the Fulcio certificate is short-lived.Fixes: #1462
Related: #1149, #1153, #1650
Type of Change
Component(s) Affected
docs/,examples/)Implementation Notes
The issue's proposed approach does not work as written, and the docs say so. #1462 proposed pointing users at "the same
sigstore/rekor-monitorreusable workflow". That workflow resolves its Rekor target from Sigstore's default signing config, which lists only Rekor v1, and Sigstore has stated the public-good instance will keep v1 as the default log for the foreseeable future. Since #1650, AICR's signing paths default to Rekor v2. So the upstream workflow cannot monitor the log that AICR users sign to by default.The section therefore routes by log version rather than pretending one tool covers everything:
tools/rekor-monitor, which resolves the v2 shard from the same signing config AICR signs against and takes the watched identity as flags. Includes a drop-in scheduled workflow derived from AICR's ownrekor-monitor.yaml, with the checkpoint-artifact round trip and the two hardening steps called out.fingerprintsmode, with thecosign public-keypipeline to derive the fingerprint.Two coverage gaps are stated plainly rather than papered over: a KMS key signing to the default Rekor v2 has no turnkey monitor (
tools/rekor-monitorexposes only certificate-identity flags, though the upstream library it builds on does match v2 entries by fingerprint), and a private Rekor v2 reached via--signing-configis out of reach for both tools.The per-mode identity table is grounded in the code rather than inferred: KMS entries carry a public key with no SAN or issuer to match, and
--tlog-upload=falseuploads nothing, so there is nothing to monitor.Five upstream Sigstore references were added, each at the claim it supports: the threat model and Rekor overview for why monitoring is the detection control, the Rekor evolution post as the primary source for the v1-default statement, the
sigstore/rekor-monitorrepository, and Sigstore's rekor-monitor walkthrough for the consistency-check versus identity-search split and the fingerprint mode.Testing
Docs-only change; no code paths touched.
tools/check-docs-mdxpasses (OK: all doc files are MDX-safe), including the${{ ... }}expressions inside the YAML fences. The lychee job infern-docs-ci.yamlruns with--offline, so it gates local links and anchors only; the five new external URLs were each fetched and confirmed to return the expected content. The one new intra-repo anchor (supply-chain-verification.md#monitoring-your-signing-identity) resolves, and no duplicate heading slugs were introduced.Risk Assessment
Rollout notes: N/A. Additive documentation only; no existing headings renamed or restructured, so inbound anchor links are unaffected.
Checklist
make testwith-race)make lint)git commit -S)