Skip to content

Commit 0741371

Browse files
author
wildleo91
committed
docs(security): publish Sigstore verifier command after §8 dry-run
Sigstore signing was wired in round 8 (build 629, 2026-04-29) but the required §8 end-to-end verification dry-run was deferred to the first public release. Ran the dry-run on 2026-05-13 against test tag v0.0.0-sigstore-test; all 5 verification scenarios passed: - legit verify against production identity-regex: Verified OK - tamper test (appended bytes): rejected with ASN.1 signature error - foreign-signature identity-pin test (sigstore/cosign v2.4.3 RPM, which is signed via Google OIDC, not GitHub Actions): rejected by our identity-regex (added as a rigor extension beyond what RELEASE_CHECKLIST §8 required) - Rekor lookup with explicit --rekor-url: Verified OK - SBOM (.cdx.json) signature: Verified OK docs/SBOM.md is the canonical home of the verifier command. Replaced the "Recommended next-step hardening (not in this round)" section with the live "Verifying a release with cosign" instructions. The command uses <VERSION> placeholder per CLAUDE.md doc-drift rules and includes --new-bundle-format=false (required for cosign 3.x customers because the workflow currently signs with cosign v2.4.1's legacy .sig+.crt pair, not the new single-.sigstore-bundle format). README.md (Security section) and INSTALLATION.md (new Section 3.6) point to docs/SBOM.md rather than duplicating the command — avoids the doc-drift class of bug. docs/RELEASE_CHECKLIST.md §8 marked DONE with an Outcome notes block capturing the 5 verification results, three non-blocking side-findings (Node 20 deprecation in GHA, double-base64 cert quirk, app.conf:version vs. git-tag drift risk), and a pointer to the two prerequisite fixes the dry-run forced (commits 691c651 + 6f8dc79). Test release v0.0.0-sigstore-test was deleted post-verification per the runbook; the Rekor entry remains permanently in the transparency log (immutable by design — this is the security property, not a bug).
1 parent 6f8dc79 commit 0741371

4 files changed

Lines changed: 113 additions & 31 deletions

File tree

INSTALLATION.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,20 @@ deployment:
260260

261261
---
262262

263+
## Section 3.6 — Verifying the release signature (recommended before install)
264+
265+
The `.spl` is Sigstore-signed by GitHub Actions. Verifying before install
266+
confirms the artifact came from this repo's release pipeline and was not
267+
swapped on the GitHub Releases page. See
268+
[docs/SBOM.md](docs/SBOM.md#verifying-a-release-with-cosign) for the
269+
canonical `cosign verify-blob` command and identity-regex.
270+
271+
Skipping this check leaves you exposed to a release-channel takeover
272+
(an attacker who compromises the Releases page can swap both the `.spl`
273+
and the `.sha256` sidecar). Sigstore signing closes that gap.
274+
275+
---
276+
263277
## Section 4 — Post-install verification checklist
264278

265279
Run these checks after the first install:

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Built for SOC teams who need to manage detection rule exceptions without touchin
7171
- Server-side RBAC enforcement on every request
7272
- Path traversal protection, input sanitization, rate limiting
7373
- Control Panel restricted to admin roles
74+
- Release artifacts are Sigstore-signed; see [docs/SBOM.md](docs/SBOM.md#verifying-a-release-with-cosign) for the cosign verification command
7475

7576
### Row Expiration
7677

docs/RELEASE_CHECKLIST.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ or new filters here.
119119

120120
---
121121

122-
## 8. Sigstore Signing — End-to-End Verification (REQUIRED on first signed release)
122+
## 8. Sigstore Signing — End-to-End Verification [x] DONE — verified 2026-05-13 on tag v0.0.0-sigstore-test
123123

124124
**Background.** Round 8 (build 629, 2026-04-29) wired Sigstore keyless
125125
signing into `.github/workflows/release.yml`. The workflow uses GitHub
@@ -252,3 +252,41 @@ After this section is completed once, mark this section as
252252
leave it in place. Future releases verify automatically via the
253253
quarterly pip-audit cadence + per-release workflow run; this one-shot
254254
just proves the wiring.
255+
256+
### Outcome notes (2026-05-13 dry-run on tag v0.0.0-sigstore-test)
257+
258+
5/5 verification scenarios passed:
259+
260+
- Step 2 (legit verify against production identity-regex) — `Verified OK`
261+
- Step 3 (tamper test, appended bytes to .spl) — failed with "invalid
262+
signature when validating ASN.1 encoded signature"
263+
- Step 3b (foreign-signature identity-pin test, added as a rigor
264+
extension to this runbook) — verified that a known-good Sigstore
265+
signature from a different repo (`sigstore/cosign` v2.4.3 RPM, which
266+
is signed via Google OIDC, not GitHub Actions) is rejected by the
267+
production identity-regex. Confirms the identity pin is the actual
268+
security boundary, not just signature-integrity. Recommend adding
269+
Step 3b to this checklist permanently.
270+
- Step 4 (Rekor lookup with explicit `--rekor-url`) — `Verified OK`
271+
- Bonus: SBOM (`.cdx.json`) signature verifies the same way
272+
273+
Verifier command published in `docs/SBOM.md` (canonical) with pointers
274+
from `README.md` (Security section) and `INSTALLATION.md` (Section 3.6).
275+
276+
Three side-findings worth follow-up but not blocking:
277+
278+
- The release workflow runs `actions/checkout@v4` + `actions/setup-python@v5`
279+
which both deprecate Node 20; GitHub forces Node 24 by 2026-09-16.
280+
- `cosign-release: 'v2.4.1'` outputs `.crt` as base64-wrapped PEM
281+
(cosign-internal format). cosign verify-blob handles it transparently;
282+
only matters for openssl inspection.
283+
- `scripts/package.sh` derives `<VERSION>` for the .spl filename from
284+
`default/app.conf:version`, not from the git tag. Pre-tag-cut step
285+
must bump `app.conf:version` to match the tag, otherwise the .spl
286+
filename mismatches the tag and Sigstore identity still verifies but
287+
the customer-facing artifact name looks wrong. Add to "Tag cut"
288+
pre-checklist on next round.
289+
290+
The two prerequisite fixes that the dry-run forced (commits 691c651 +
291+
6f8dc79) are not Sigstore-related — they were pre-existing validate.sh
292+
issues that had never been exercised by a passing release run.

docs/SBOM.md

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -88,36 +88,65 @@ sha256sum -c wl_manager-<version>.spl.sha256
8888

8989
### Limitations of the current model
9090

91-
1. The .spl and the .sha256 are distributed via the same channel
92-
(GitHub Releases). An attacker who compromises GitHub Releases
93-
for this repository can replace BOTH files. The hash file is
94-
only an integrity check against in-flight tampering / CDN
95-
corruption / archive rot — NOT against a release-channel
96-
takeover.
97-
2. There is no signature on the .sha256 file. A `gpg --verify`
98-
step would tie the release to a key whose public half is
99-
distributed out-of-band (e.g., `KEYS` file in this repo,
100-
PGP keyserver, maintainer's web bio).
101-
102-
### Recommended next-step hardening (not in this round)
103-
104-
- **Sigstore / cosign keyless signing** — sign the .spl during
105-
the GitHub Actions release run using the OIDC token from the
106-
workflow itself. Verifiers can confirm the .spl was produced by
107-
`RelativisticJet/wl_manager`'s release workflow without us
108-
managing any long-lived signing key. Single best ROI.
109-
- **`KEYS` file at repo root** with a maintainer GPG public key,
110-
and `release.yml` signing the .sha256 with the matching private
111-
key (stored as a GitHub Actions secret). Heavier than Sigstore
112-
but matches what most established Splunk apps ship.
113-
- **Per-release SBOM artifact** (CycloneDX or SPDX) attached
114-
alongside the .spl. The current SBOM is small enough to live in
115-
this document, but customers running automated SCA tools expect
116-
a machine-readable file per release.
117-
118-
These are all C-tier follow-ups for a future round; the current
119-
.sha256 model is adequate for the threat the repo currently faces
120-
(opportunistic CDN tampering, unintentional corruption).
91+
1. (resolved as of 2026-05-13) — The .spl is now Sigstore-signed in
92+
addition to having a SHA-256 sidecar. Customers who run only the
93+
`sha256sum -c` check have the same exposure to a release-channel
94+
takeover as before, but customers who run the cosign verification
95+
below cannot be deceived by a Releases-page swap.
96+
2. There is still no GPG signature on the .sha256 file. A `gpg --verify`
97+
step would tie the release to a maintainer key whose public half is
98+
distributed out-of-band. Not in this round; see "Future hardening"
99+
below.
100+
101+
### Verifying a release with cosign
102+
103+
Each release ships `.sig` (signature) and `.crt` (Fulcio short-lived
104+
certificate) sidecar files for both the `.spl` and the `.cdx.json`
105+
SBOM. The signing identity is the GitHub Actions workflow
106+
(`.github/workflows/release.yml`) that produced the artifact, recorded
107+
in the Rekor transparency log.
108+
109+
Install cosign (>= v2.4):
110+
111+
- macOS: `brew install cosign`
112+
- Linux: see <https://docs.sigstore.dev/cosign/system_config/installation/>
113+
- Windows: `winget install sigstore.cosign`
114+
115+
Verify (replace `<VERSION>` with the release you're installing):
116+
117+
```bash
118+
cosign verify-blob \
119+
--new-bundle-format=false \
120+
--certificate wl_manager-<VERSION>.spl.crt \
121+
--signature wl_manager-<VERSION>.spl.sig \
122+
--certificate-identity-regexp '^https://github.com/RelativisticJet/wl_manager/\.github/workflows/release\.yml@refs/tags/.*$' \
123+
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
124+
wl_manager-<VERSION>.spl
125+
```
126+
127+
A passing verifier prints `Verified OK`. A tampered .spl or a signature
128+
from any other repo's workflow fails closed.
129+
130+
`--new-bundle-format=false` is required for cosign 3.x because the
131+
release workflow currently signs with cosign v2.4.1 (producing the
132+
legacy `.sig` + `.crt` pair, not the new single-`.sigstore`-bundle
133+
format that cosign 3.x defaults to). cosign 2.x users can drop the
134+
flag — the command works either way.
135+
136+
You can also verify the SBOM the same way by substituting
137+
`wl_manager-<VERSION>.spl.cdx.json` and its `.sig` / `.crt` pair.
138+
139+
### Future hardening (not in this round)
140+
141+
- **GPG sidecar on .sha256**: a maintainer key distributed via a `KEYS`
142+
file at repo root, with `release.yml` GPG-signing the .sha256. Heavier
143+
than Sigstore but matches what most established Splunk apps ship.
144+
Defense-in-depth against the day Fulcio's CA cert chain rotates faster
145+
than customers can update their cosign install.
146+
- **Upgrade signing to cosign v3.x new-bundle format**: removes the
147+
`--new-bundle-format=false` flag from the customer command. Wait until
148+
the customer base demonstrates they're on cosign 3.x; today most
149+
package managers still ship cosign 2.x.
121150

122151
## Subresource Integrity (SRI) — N/A by architecture
123152

0 commit comments

Comments
 (0)