Skip to content

Commit 882d29e

Browse files
jedudenclaude
andauthored
release: harden supply chain with CGO=0, SLSA provenance, cosign (#250)
* release: harden supply chain with CGO=0, SLSA provenance, cosign - Pin CGO_ENABLED=0 on the build matrix so the native linux/amd64 binary stays statically linked. Cross-compiles already auto-disable CGO; the native build would otherwise link glibc-resolved net/os-user resolvers and break the manylinux_2_17 wheel and the @mdsmith/linux-x64 npm package on Alpine/musl and old-glibc systems. - Generate SLSA build provenance attestations for the 5 binaries and the .vsix via actions/attest-build-provenance. Verifiable with `gh attestation verify <file> -R jeduden/mdsmith`. - Sign checksums.txt with cosign keyless (Sigstore + GitHub OIDC). Defeats the case where an attacker who rewrites the binary on the release page also rewrites checksums.txt; forging a matching signature would require compromising release.yml. - Document both verification paths in docs/guides/install.md. * release: address Copilot review on CGO comment + asset filename - Reword the CGO_ENABLED=0 comment to describe the concrete portability goal (avoid glibc-resolved net/os/user on linux/amd64) rather than overstating it as full static linking, which doesn't apply on macOS (libSystem) or Windows (kernel32). - Download the binary under its release-asset name (mdsmith-linux-amd64) so both `sha256sum -c` (which matches the local filename against the entry in checksums.txt) and `gh attestation verify` (which needs the attested asset name) work without renaming. install -m 0755 still places the binary on $PATH as mdsmith, leaving the source file in place for the cosign verify step that follows. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent fd54c7e commit 882d29e

2 files changed

Lines changed: 95 additions & 3 deletions

File tree

.github/workflows/release.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@ jobs:
3636
env:
3737
GOOS: ${{ matrix.goos }}
3838
GOARCH: ${{ matrix.goarch }}
39+
# Disable cgo so the native linux/amd64 build resolves
40+
# net and os/user purely in Go. Cross-compiles already
41+
# auto-disable CGO; the native build would otherwise
42+
# bind glibc's resolver into the binary, breaking the
43+
# manylinux_2_17 wheel and the @mdsmith/linux-x64 npm
44+
# package on Alpine/musl and on systems whose glibc is
45+
# older than the wheel tag claims. macOS and Windows
46+
# binaries still link the platform syscall layer
47+
# (libSystem, kernel32) — this knob is about avoiding
48+
# glibc on Linux, not achieving full static linkage.
49+
CGO_ENABLED: "0"
3950
VERSION: ${{ github.ref_name }}
4051
run: |
4152
ext=""
@@ -278,14 +289,56 @@ jobs:
278289
release:
279290
needs: [build, vscode]
280291
runs-on: ubuntu-latest
292+
# `id-token: write` mints the OIDC token that
293+
# `actions/attest-build-provenance` and `cosign sign-blob`
294+
# exchange for a short-lived Sigstore credential. No
295+
# long-lived secrets. `attestations: write` is the dedicated
296+
# scope the GitHub attestations API gates the
297+
# provenance upload on.
281298
permissions:
282299
contents: write
300+
id-token: write
301+
attestations: write
283302
steps:
284303
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
285304
with:
286305
merge-multiple: true
287306
- name: Create checksums
288307
run: sha256sum mdsmith-* > checksums.txt
308+
- name: Generate SLSA build provenance
309+
# Attests every binary the build matrix produced (and the
310+
# .vsix the vscode job uploaded — `mdsmith-*` matches both).
311+
# Each attestation ties the file's SHA-256 back to this
312+
# workflow run and the commit it was built from. Consumers
313+
# verify with:
314+
# gh attestation verify mdsmith-<plat> -R jeduden/mdsmith
315+
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
316+
with:
317+
subject-path: "mdsmith-*"
318+
- name: Install cosign
319+
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
320+
- name: Sign checksums with cosign
321+
# Keyless Sigstore signature on the checksum file. The
322+
# GitHub OIDC token binds the signature to this exact
323+
# workflow file at this exact tag, so an attacker who
324+
# rewrites checksums.txt on the release page can't also
325+
# forge a matching signature without compromising
326+
# release.yml on this repo. Verify with:
327+
# cosign verify-blob \
328+
# --certificate checksums.txt.pem \
329+
# --signature checksums.txt.sig \
330+
# --certificate-identity-regexp \
331+
# "^https://github.com/jeduden/mdsmith/.github/workflows/release.yml@" \
332+
# --certificate-oidc-issuer \
333+
# https://token.actions.githubusercontent.com \
334+
# checksums.txt
335+
env:
336+
COSIGN_YES: "true"
337+
run: |
338+
cosign sign-blob \
339+
--output-signature checksums.txt.sig \
340+
--output-certificate checksums.txt.pem \
341+
checksums.txt
289342
- name: Create release
290343
env:
291344
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -295,6 +348,8 @@ jobs:
295348
files: |
296349
mdsmith-*
297350
checksums.txt
351+
checksums.txt.sig
352+
checksums.txt.pem
298353
299354
smoke-test:
300355
# Wait until every channel is on the new version before checking

docs/guides/install.md

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,49 @@ move the binary onto `$PATH`:
130130

131131
```bash
132132
base="https://github.com/jeduden/mdsmith/releases/latest/download"
133-
curl -L -o mdsmith "$base/mdsmith-linux-amd64"
134-
curl -L -o checksums.txt "$base/checksums.txt"
133+
curl -L -o mdsmith-linux-amd64 "$base/mdsmith-linux-amd64"
134+
curl -L -o checksums.txt "$base/checksums.txt"
135135
sha256sum -c <(grep mdsmith-linux-amd64 checksums.txt)
136-
install -m 0755 mdsmith /usr/local/bin/mdsmith
136+
install -m 0755 mdsmith-linux-amd64 /usr/local/bin/mdsmith
137137
```
138138

139+
Keep the binary saved under its release-asset name
140+
(`mdsmith-linux-amd64`) until verification is done —
141+
both `sha256sum -c` and `gh attestation verify` below
142+
match local files against that exact name. `install`
143+
copies the file rather than moving it, so the original
144+
remains for the verification steps.
145+
146+
For supply-chain-sensitive deployments, the release
147+
also ships a SLSA build provenance attestation per
148+
binary and a Sigstore signature on `checksums.txt`.
149+
Verify the provenance with `gh`:
150+
151+
```bash
152+
gh attestation verify mdsmith-linux-amd64 \
153+
-R jeduden/mdsmith
154+
```
155+
156+
Verify the checksums-file signature with `cosign`:
157+
158+
```bash
159+
curl -L -o checksums.txt.sig "$base/checksums.txt.sig"
160+
curl -L -o checksums.txt.pem "$base/checksums.txt.pem"
161+
cosign verify-blob \
162+
--certificate checksums.txt.pem \
163+
--signature checksums.txt.sig \
164+
--certificate-identity-regexp \
165+
"^https://github.com/jeduden/mdsmith/.github/workflows/release.yml@" \
166+
--certificate-oidc-issuer \
167+
https://token.actions.githubusercontent.com \
168+
checksums.txt
169+
```
170+
171+
Both verifications resolve through the workflow's
172+
GitHub OIDC identity, so a forged binary or rewritten
173+
checksums file fails verification unless the attacker
174+
also controls `release.yml` on `jeduden/mdsmith`.
175+
139176
This path is also the documented fallback if any of
140177
the package channels above is unavailable on a given
141178
day.

0 commit comments

Comments
 (0)