Skip to content

Commit efe0345

Browse files
author
keeper
committed
ci(release): block egress, trim the commentary
1 parent ae3f758 commit efe0345

5 files changed

Lines changed: 80 additions & 76 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
# CI: build, vet, test, lint, and govulncheck on every push and pull request, so the repo
2-
# shows green checks + a live badge and satisfies OpenSSF Scorecard's CI-Tests check. Actions
3-
# are pinned to full commit SHAs (Scorecard: Pinned-Dependencies); Renovate keeps them current.
1+
# Build, vet, test, lint, govulncheck. The gate on every push and pull request.
42
name: ci
53

64
on:

.github/workflows/codeql.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# CodeQL static analysis (Go) on the GitHub mirror -> Security tab. Satisfies OpenSSF
2-
# Scorecard's SAST check. Actions pinned to SHAs; Renovate updates them.
1+
# CodeQL static analysis (Go), results to the Security tab.
32
name: codeql
43

54
on:

.github/workflows/dependency-review.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
# Dependency Review, on PRs, block newly-introduced dependencies with known vulns or
2-
# disallowed licenses before they merge. Satisfies OpenSSF Scorecard's Dependency-Update
3-
# posture alongside Renovate. Actions pinned to SHAs.
1+
# Blocks pull requests that introduce dependencies with known vulns or bad licenses.
42
name: dependency-review
53

64
on:

.github/workflows/release.yml

Lines changed: 76 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
# Release, triggered by a v*.*.* tag. GoReleaser builds the static linux binaries +
2-
# multi-arch GHCR image, cosign keyless-signs the checksums and the image (GitHub OIDC, no
3-
# key material), publishes a GitHub Release, and attaches build provenance to the binaries.
4-
# Uses the built-in GITHUB_TOKEN for GHCR and Releases, no personal access token.
1+
# Build, sign, attest, publish, then read it all back. Triggered by a v*.*.* tag.
52
name: release
63

74
on:
@@ -19,20 +16,39 @@ jobs:
1916
packages: write # push the image to GHCR
2017
id-token: write # cosign keyless signing (Sigstore OIDC)
2118
attestations: write # store the build provenance
22-
# Lets the attest action also record where the artifact is hosted and whether it is
23-
# still active — the "linked artifacts" view. Separate from the attestation itself,
24-
# which works without it; push-to-registry without this permission is what produced
25-
# the storage-record warnings on v0.1.6.
26-
artifact-metadata: write
19+
artifact-metadata: write # required by push-to-registry, else it warns and skips
2720
steps:
28-
# Audit, not block, and deliberately: a release reaches Sigstore, GHCR, the Go proxy,
29-
# the Docker and Chainguard registries, and three tool downloads. One endpoint missing
30-
# from an allowlist here means a failed release, not a caught intrusion. The read-only
31-
# workflows are blocked; this one is watched, and its egress is reviewed per release.
21+
# Allowlist built from audited runs. A new dependency that phones somewhere new fails
22+
# here, loudly, which is the point. Add the endpoint only after checking what it is.
3223
- name: Harden the runner
3324
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
3425
with:
35-
egress-policy: audit
26+
egress-policy: block
27+
allowed-endpoints: >
28+
*.actions.githubusercontent.com:443
29+
*.blob.core.windows.net:443
30+
api.github.com:443
31+
github.com:443
32+
objects.githubusercontent.com:443
33+
raw.githubusercontent.com:443
34+
release-assets.githubusercontent.com:443
35+
apk.cgr.dev:443
36+
auth.docker.io:443
37+
cgr.dev:443
38+
fulcio.sigstore.dev:443
39+
get.anchore.io:443
40+
ghcr.io:443
41+
goreleaser.com:443
42+
pkg-containers.githubusercontent.com:443
43+
production.cloudfront.docker.com:443
44+
proxy.golang.org:443
45+
registry-1.docker.io:443
46+
rekor.sigstore.dev:443
47+
toolbox-data.anchore.io:443
48+
tuf-repo-cdn.sigstore.dev:443
49+
tuf-repo.github.com:443
50+
uploads.github.com:443
51+
*.r2.cloudflarestorage.com:443
3652
- name: Checkout
3753
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
3854
with:
@@ -50,9 +66,7 @@ jobs:
5066
- name: Install cosign
5167
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
5268
with:
53-
# Pin: cosign >= 2.6 defaults to the new Sigstore bundle format, which breaks
54-
# GoReleaser's --output-signature/--output-certificate sign-blob config. v2.5.3
55-
# keeps the classic .sig/.pem outputs that any cosign version can verify.
69+
# >= 2.6 emits Sigstore bundles, which GoReleaser's sign-blob config cannot consume.
5670
cosign-release: 'v2.5.3'
5771
- name: Install syft (SBOMs)
5872
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
@@ -69,27 +83,16 @@ jobs:
6983
args: release --clean
7084
env:
7185
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
72-
# Build the tag that triggered this run, not whatever `git describe` picks.
73-
#
74-
# When two tags sit on the same commit — a release candidate and the release that
75-
# promotes it unchanged — describe chooses one of them, and it chose the candidate:
76-
# the run built 0.1.6-rc.2 under the v0.1.6 tag and failed uploading assets that
77-
# already existed. Silent in the worst case, since a green run would have published
78-
# the wrong version.
86+
# Without this, `git describe` picks a candidate tag sharing the commit and builds
87+
# the wrong version under the right tag.
7988
GORELEASER_CURRENT_TAG: ${{ github.ref_name }}
80-
# Build provenance for every artifact in checksums.txt: what built it, from which
81-
# commit, in which workflow. Signed through Sigstore and written to the public
82-
# transparency log. Verify with:
83-
# gh attestation verify gitdr_0.1.0_linux_amd64.tar.gz --repo gitdr-io/gitdr
89+
# Verify with: gh attestation verify <tarball> --repo gitdr-io/gitdr
8490
- name: Attest build provenance
8591
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
8692
with:
8793
subject-checksums: dist/checksums.txt
8894

89-
# The image needs its own subject. checksums.txt covers the tarballs only, so until
90-
# now someone pulling the container could verify who signed it but not where it was
91-
# built — a signature says "we made this", provenance says "here is the commit and
92-
# the workflow run that made it". Both, or the claim is half a claim.
95+
# checksums.txt covers the tarballs only; the image needs its own subject.
9396
- name: Read the published image digest
9497
id: image
9598
run: |
@@ -106,8 +109,7 @@ jobs:
106109
subject-digest: ${{ steps.image.outputs.digest }}
107110
push-to-registry: true
108111

109-
# Package the Helm chart and push it to GHCR as an OCI artifact, then cosign-sign it by
110-
# digest (keyless, GitHub OIDC). Install: helm install oci://ghcr.io/gitdr-io/charts/gitdr
112+
# Install: helm install oci://ghcr.io/gitdr-io/charts/gitdr
111113
chart:
112114
name: helm chart
113115
needs: [goreleaser]
@@ -117,12 +119,26 @@ jobs:
117119
packages: write # push the chart to GHCR
118120
id-token: write # cosign keyless
119121
attestations: write # store the chart's build provenance
120-
artifact-metadata: write # record where the chart is hosted, see the goreleaser job
122+
artifact-metadata: write
121123
steps:
122124
- name: Harden the runner
123125
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
124126
with:
125-
egress-policy: audit
127+
egress-policy: block
128+
allowed-endpoints: >
129+
*.actions.githubusercontent.com:443
130+
*.blob.core.windows.net:443
131+
api.github.com:443
132+
github.com:443
133+
objects.githubusercontent.com:443
134+
raw.githubusercontent.com:443
135+
release-assets.githubusercontent.com:443
136+
fulcio.sigstore.dev:443
137+
get.helm.sh:443
138+
ghcr.io:443
139+
pkg-containers.githubusercontent.com:443
140+
rekor.sigstore.dev:443
141+
tuf-repo-cdn.sigstore.dev:443
126142
- name: Checkout
127143
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
128144
with:
@@ -132,9 +148,7 @@ jobs:
132148
- name: Install cosign
133149
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
134150
with:
135-
# Pin: cosign >= 2.6 defaults to the new Sigstore bundle format, which breaks
136-
# GoReleaser's --output-signature/--output-certificate sign-blob config. v2.5.3
137-
# keeps the classic .sig/.pem outputs that any cosign version can verify.
151+
# >= 2.6 emits Sigstore bundles, which GoReleaser's sign-blob config cannot consume.
138152
cosign-release: 'v2.5.3'
139153
- name: Log in to GHCR
140154
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
@@ -168,14 +182,8 @@ jobs:
168182
subject-digest: ${{ steps.push.outputs.digest }}
169183
push-to-registry: true
170184

171-
# Read the release back.
172-
#
173-
# Everything above signs and attests; nothing above checks that it worked. That gap is how
174-
# v0.1.5 shipped with a signed image carrying no provenance and nobody noticing until
175-
# someone went looking by hand. It is the same failure this product exists to prevent, one
176-
# layer up: a process that reports success is not evidence, the artifact is.
177-
#
178-
# This job fails the release if any published artifact is unsigned or unattested.
185+
# Read the release back. Everything above signs and attests; this is what checks it
186+
# worked. v0.1.5 shipped a signed image with no provenance because nothing did.
179187
verify:
180188
name: verify the release
181189
needs: [goreleaser, chart]
@@ -185,29 +193,35 @@ jobs:
185193
packages: read # read the image and chart from GHCR
186194
attestations: read # read the provenance store
187195
env:
188-
# The identity that is allowed to have signed any of this. A signature from anything
189-
# else is not a pass.
196+
# The only identity allowed to have signed any of this.
190197
IDENTITY: https://github.com/gitdr-io/gitdr/.github/workflows/release.yml@refs/tags/${{ github.ref_name }}
191198
ISSUER: https://token.actions.githubusercontent.com
192199
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
193200
steps:
194201
- name: Harden the runner
195202
uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1
196203
with:
197-
egress-policy: audit
204+
egress-policy: block
205+
allowed-endpoints: >
206+
*.actions.githubusercontent.com:443
207+
*.blob.core.windows.net:443
208+
api.github.com:443
209+
github.com:443
210+
objects.githubusercontent.com:443
211+
raw.githubusercontent.com:443
212+
release-assets.githubusercontent.com:443
213+
fulcio.sigstore.dev:443
214+
ghcr.io:443
215+
pkg-containers.githubusercontent.com:443
216+
rekor.sigstore.dev:443
217+
search.sigstore.dev:443
218+
tuf-repo-cdn.sigstore.dev:443
198219
- name: Install cosign
199220
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
200221

201-
# The asset set itself, before anything is verified.
202-
#
203-
# tflint shipped 0.22.0 without checksums.txt.sig and broke every install script that
204-
# verified before installing; the file was simply absent and nothing noticed. A missing
205-
# signature is caught below by the verification failing, but an artifact that quietly
206-
# stops being produced — an SBOM, an architecture — is not: there is nothing left to
207-
# fail. So the expected set is asserted by name.
208-
#
209-
# Adding an artifact means adding it here. That is the point: the set consumers depend
210-
# on should not be able to change by accident.
222+
# A missing signature fails verification below. An artifact that silently stops being
223+
# produced leaves nothing to fail, so the set is asserted by name. tflint shipped 0.22.0
224+
# without checksums.txt.sig and broke every install script that verified first.
211225
- name: Every expected artifact is present
212226
env:
213227
TAG: ${{ github.ref_name }}
@@ -238,18 +252,15 @@ jobs:
238252
--certificate-identity "$IDENTITY" \
239253
--certificate-oidc-issuer "$ISSUER"
240254
241-
# Every line of the signed list, against the bytes actually published.
242255
sha256sum -c checksums.txt
243256
244257
for f in *.tar.gz; do
245258
gh attestation verify "$f" --repo "$GITHUB_REPOSITORY" \
246259
--signer-workflow gitdr-io/gitdr/.github/workflows/release.yml
247260
done
248261
249-
# slsaprovenance1, not slsaprovenance: the bare shorthand means the SLSA v0.2 predicate,
250-
# while actions/attest-build-provenance emits v1. The wrong one rejects a perfectly good
251-
# attestation with "none of the attestations matched the predicate type", which is a
252-
# false alarm from the very job whose purpose is to be trusted when it complains.
262+
# slsaprovenance1, not slsaprovenance: the bare name means the v0.2 predicate and
263+
# rejects the v1 one the attest action emits.
253264
- name: Image — signature and provenance
254265
env:
255266
VERSION: ${{ github.ref_name }}

.github/workflows/scorecard.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
# OpenSSF Scorecard, supply-chain posture score, published to the Scorecard API (badge)
2-
# and the GitHub Security tab. Publishing requires a public repo, so results appear once
3-
# the mirror is public. Actions pinned to SHAs; Renovate updates them.
1+
# OpenSSF Scorecard, published to the Scorecard API and the Security tab.
42
name: scorecard
53

64
on:

0 commit comments

Comments
 (0)