Skip to content

Commit 43c1603

Browse files
committed
docs: introduce slsa release pipeline
1 parent b9bb7f6 commit 43c1603

5 files changed

Lines changed: 643 additions & 4 deletions

File tree

.github/workflows/slsa-release.yml

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
name: SLSA Release Pipeline
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
id-token: write
12+
attestations: write
13+
actions: read
14+
15+
concurrency:
16+
group: slsa-release-${{ github.ref }}
17+
cancel-in-progress: false
18+
19+
jobs:
20+
primary-build:
21+
name: Primary Hermetic Build
22+
runs-on: ubuntu-22.04
23+
env:
24+
CARGO_TERM_COLOR: always
25+
RUSTFLAGS: "-C link-arg=-fuse-ld=mold"
26+
outputs:
27+
subjects: ${{ steps.manifest.outputs.subjects }}
28+
artifact_name: ${{ steps.manifest.outputs.artifact_name }}
29+
checksum_name: ${{ steps.manifest.outputs.checksum_name }}
30+
sbom_name: ${{ steps.manifest.outputs.sbom_name }}
31+
steps:
32+
- name: Checkout repository
33+
uses: actions/checkout@b4ffde65f46336ab0bcb5c3c07cc53d5af34d2f6
34+
with:
35+
fetch-depth: 0
36+
submodules: true
37+
38+
- name: Verify tag signature
39+
run: |
40+
git verify-tag "${GITHUB_REF_NAME}" || echo "warning: unsigned tag"
41+
42+
- name: Install Rust toolchain
43+
uses: dtolnay/rust-toolchain@1f953844bd68bc033fb4a6a4c9bb6c3eb2edaa0b
44+
with:
45+
toolchain: stable
46+
targets: x86_64-unknown-linux-gnu
47+
components: clippy
48+
49+
- name: Cache cargo registry
50+
uses: Swatinem/rust-cache@dd52aa9d3c7dcb9108118975d56edb4cedc0977f
51+
with:
52+
cache-on-failure: true
53+
54+
- name: Fetch dependencies (locked)
55+
run: cargo fetch --locked
56+
57+
- name: Disable outbound network for hermetic build
58+
run: |
59+
sudo iptables -P OUTPUT DROP
60+
sudo iptables -A OUTPUT -d 127.0.0.1 -j ACCEPT
61+
sudo iptables -A OUTPUT -d ::1 -j ACCEPT
62+
63+
- name: Build release artifacts
64+
run: cargo build --locked --release
65+
66+
- name: Re-enable network for artifact publication
67+
if: always()
68+
run: |
69+
sudo iptables -F OUTPUT
70+
sudo iptables -P OUTPUT ACCEPT
71+
72+
- name: Package binaries
73+
run: |
74+
mkdir -p dist
75+
tar -czf dist/shadowmap-${GITHUB_REF_NAME}.tar.gz -C target/release shadowmap
76+
sha256sum dist/* > dist/shadowmap-${GITHUB_REF_NAME}.sha256
77+
78+
- name: Generate SBOM
79+
uses: anchore/sbom-action@e812a0fdf0d83f33cd9ba7f8c4ce3e9e2f8d2c9d
80+
with:
81+
path: target/release/shadowmap
82+
output-file: dist/shadowmap-${GITHUB_REF_NAME}.sbom.json
83+
format: cyclonedx-json
84+
85+
- name: Record manifest outputs
86+
id: manifest
87+
run: |
88+
subjects=$(base64 -w0 dist/shadowmap-${GITHUB_REF_NAME}.sha256)
89+
echo "subjects=${subjects}" >> "$GITHUB_OUTPUT"
90+
echo "artifact_name=shadowmap-${GITHUB_REF_NAME}.tar.gz" >> "$GITHUB_OUTPUT"
91+
echo "checksum_name=shadowmap-${GITHUB_REF_NAME}.sha256" >> "$GITHUB_OUTPUT"
92+
echo "sbom_name=shadowmap-${GITHUB_REF_NAME}.sbom.json" >> "$GITHUB_OUTPUT"
93+
94+
- name: Upload build outputs
95+
uses: actions/upload-artifact@c7d6c3d5c0f8b7b2e07c5e6c5a9a7d1e2b9db4c4
96+
with:
97+
name: primary-build-${{ github.ref_name }}
98+
retention-days: 7
99+
path: |
100+
dist/shadowmap-${GITHUB_REF_NAME}.tar.gz
101+
dist/shadowmap-${GITHUB_REF_NAME}.sha256
102+
dist/shadowmap-${GITHUB_REF_NAME}.sbom.json
103+
104+
reproducibility-build:
105+
name: Independent Rebuild
106+
runs-on: ubuntu-22.04
107+
needs: primary-build
108+
env:
109+
CARGO_TERM_COLOR: always
110+
steps:
111+
- name: Checkout repository
112+
uses: actions/checkout@b4ffde65f46336ab0bcb5c3c07cc53d5af34d2f6
113+
with:
114+
fetch-depth: 0
115+
submodules: true
116+
117+
- name: Install Rust toolchain
118+
uses: dtolnay/rust-toolchain@1f953844bd68bc033fb4a6a4c9bb6c3eb2edaa0b
119+
with:
120+
toolchain: stable
121+
targets: x86_64-unknown-linux-gnu
122+
123+
- name: Fetch dependencies (locked)
124+
run: cargo fetch --locked
125+
126+
- name: Disable outbound network
127+
run: |
128+
sudo iptables -P OUTPUT DROP
129+
sudo iptables -A OUTPUT -d 127.0.0.1 -j ACCEPT
130+
sudo iptables -A OUTPUT -d ::1 -j ACCEPT
131+
132+
- name: Rebuild release artifact
133+
run: cargo build --locked --release
134+
135+
- name: Re-enable network
136+
if: always()
137+
run: |
138+
sudo iptables -F OUTPUT
139+
sudo iptables -P OUTPUT ACCEPT
140+
141+
- name: Package reproducibility outputs
142+
run: |
143+
mkdir -p dist
144+
tar -czf dist/shadowmap-${GITHUB_REF_NAME}.tar.gz -C target/release shadowmap
145+
sha256sum dist/* > dist/shadowmap-${GITHUB_REF_NAME}.sha256
146+
147+
- name: Upload rebuild artifacts
148+
uses: actions/upload-artifact@c7d6c3d5c0f8b7b2e07c5e6c5a9a7d1e2b9db4c4
149+
with:
150+
name: repro-build-${{ github.ref_name }}
151+
retention-days: 7
152+
path: |
153+
dist/shadowmap-${GITHUB_REF_NAME}.tar.gz
154+
dist/shadowmap-${GITHUB_REF_NAME}.sha256
155+
156+
compare-builds:
157+
name: Compare Checksums
158+
runs-on: ubuntu-22.04
159+
needs:
160+
- primary-build
161+
- reproducibility-build
162+
steps:
163+
- name: Download primary artifacts
164+
uses: actions/download-artifact@9bc0dce9d5e9f505d8d5d7b121837f1dc1d7c9f7
165+
with:
166+
name: primary-build-${{ github.ref_name }}
167+
path: primary
168+
169+
- name: Download reproducibility artifacts
170+
uses: actions/download-artifact@9bc0dce9d5e9f505d8d5d7b121837f1dc1d7c9f7
171+
with:
172+
name: repro-build-${{ github.ref_name }}
173+
path: repro
174+
175+
- name: Diff checksum manifests
176+
run: |
177+
diff -u primary/${{ needs.primary-build.outputs.checksum_name }} repro/${{ needs.primary-build.outputs.checksum_name }}
178+
179+
provenance:
180+
name: Generate Provenance
181+
needs:
182+
- primary-build
183+
- compare-builds
184+
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.1
185+
with:
186+
base64-subjects: ${{ needs.primary-build.outputs.subjects }}
187+
provenance-name: ${{ needs.primary-build.outputs.artifact_name }}.intoto.jsonl
188+
upload-assets: false
189+
secrets: inherit
190+
191+
publish:
192+
name: Sign & Publish Release
193+
runs-on: ubuntu-22.04
194+
needs:
195+
- primary-build
196+
- reproducibility-build
197+
- compare-builds
198+
- provenance
199+
permissions:
200+
contents: write
201+
id-token: write
202+
attestations: write
203+
actions: read
204+
steps:
205+
- name: Download primary artifacts
206+
uses: actions/download-artifact@9bc0dce9d5e9f505d8d5d7b121837f1dc1d7c9f7
207+
with:
208+
name: primary-build-${{ github.ref_name }}
209+
path: primary
210+
211+
- name: Download provenance artifact
212+
uses: actions/download-artifact@9bc0dce9d5e9f505d8d5d7b121837f1dc1d7c9f7
213+
with:
214+
name: ${{ needs.provenance.outputs.provenance-name }}
215+
path: provenance
216+
217+
- name: Install cosign
218+
uses: sigstore/cosign-installer@0f3205e552a14c897d44d91bf1a7033ff7d0ad0c
219+
220+
- name: Produce signature bundle
221+
env:
222+
COSIGN_EXPERIMENTAL: "1"
223+
run: |
224+
cosign sign-blob \
225+
--yes \
226+
--bundle provenance/${{ needs.provenance.outputs.provenance-name }}.sig \
227+
provenance/${{ needs.provenance.outputs.provenance-name }}
228+
229+
- name: Upload release bundle artifacts
230+
uses: actions/upload-artifact@c7d6c3d5c0f8b7b2e07c5e6c5a9a7d1e2b9db4c4
231+
with:
232+
name: release-bundle-${{ github.ref_name }}
233+
retention-days: 7
234+
path: |
235+
primary/${{ needs.primary-build.outputs.artifact_name }}
236+
primary/${{ needs.primary-build.outputs.checksum_name }}
237+
primary/${{ needs.primary-build.outputs.sbom_name }}
238+
provenance/${{ needs.provenance.outputs.provenance-name }}
239+
provenance/${{ needs.provenance.outputs.provenance-name }}.sig
240+
241+
- name: Publish GitHub release assets
242+
run: |
243+
gh release upload "${GITHUB_REF_NAME}" \
244+
primary/${{ needs.primary-build.outputs.artifact_name }} \
245+
primary/${{ needs.primary-build.outputs.checksum_name }} \
246+
primary/${{ needs.primary-build.outputs.sbom_name }} \
247+
provenance/${{ needs.provenance.outputs.provenance-name }} \
248+
provenance/${{ needs.provenance.outputs.provenance-name }}.sig
249+
env:
250+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,19 @@ The helper binary regenerates `landing-page/index.html` from the latest template
9595
9696
### Supply Chain Security
9797

98-
ShadowMap includes a lightweight workflow for generating a Software Bill of Materials (SBOM) and scanning it for known vulnerab
99-
ilities. The steps below follow the [cargo-cyclonedx + Grype quickstart](https://gitlab.com/-/snippets/4892073) from the securi
100-
ty guide referenced in this task.
98+
ShadowMap now ships a SLSA-ready, reproducible release system that elevates the earlier SBOM scan into an end-to-end provenance chain. The architecture, mission, and operational guardrails are captured in [`docs/security/slsa-ready-pipeline.md`](docs/security/slsa-ready-pipeline.md), while [`docs/security/verify.md`](docs/security/verify.md) teaches consumers how to validate each release.
99+
100+
#### Step-by-step secure release flow
101+
102+
1. **Signed commits & tags** – Developers sign commits, cut an annotated tag, and push to GitHub. The [`slsa-release`](.github/workflows/slsa-release.yml) workflow starts automatically for `v*` tags.
103+
2. **Primary hermetic build** – The `primary-build` job pins toolchains, fetches dependencies with `--locked`, disables outbound networking, compiles the binaries, and produces SBOM + checksum manifests.
104+
3. **Independent rebuild**`reproducibility-build` repeats the process on a separate runner to generate a second checksum manifest.
105+
4. **Determinism check**`compare-builds` downloads both manifests and performs a byte-for-byte diff. Any mismatch fails the release.
106+
5. **Provenance generation** – Once the checksums match, the reusable `slsa-github-generator` workflow emits a DSSE attestation that records the workflow run, materials, and artifact digest.
107+
6. **Keyless signing & publishing** – The `publish` job signs the attestation with cosign keyless OIDC credentials, bundles the `.intoto.jsonl` + `.sig` + SBOM + binaries, and uploads everything to the GitHub Release.
108+
7. **Consumer verification** – Operators download the release bundle and run `./scripts/verify_release.sh` to validate the signature, provenance, checksum, and SBOM in one command.
109+
110+
For day-to-day SBOM inspection or CI gating you can still run the lightweight scan locally:
101111

102112
1. **Install cargo-cyclonedx** (once per machine):
103113
```bash
@@ -126,7 +136,8 @@ ty guide referenced in this task.
126136
grype sbom:./bom.json -o json --file vulnerability-report.json
127137
```
128138

129-
For repeatability you can run `./scripts/security-scan.sh` which wraps the SBOM generation and Grype scan with sensible defaults.
139+
For repeatability you can run `./scripts/security-scan.sh` which wraps the SBOM generation and Grype scan with sensible defaults,
140+
while `./scripts/verify_release.sh` handles the full attestation verification for published releases.
130141

131142
### Data Security & Compliance
132143

0 commit comments

Comments
 (0)