From 5b8cb1cf31894ebfa495134d169a5cc525d3dcc9 Mon Sep 17 00:00:00 2001 From: Devon Bautista <17506592+synackd@users.noreply.github.com> Date: Tue, 15 Sep 2026 17:15:01 -0600 Subject: [PATCH] ci: standardize and tighten permissions across reusable workflows Ensure all reusable workflows follow a consistent permission model: - Explicit baseline of `contents: read` at the workflow level - Job-level overrides for any elevated permissions (e.g., write, packages, id-token, attestations, security-events) Fix docker-build-release.yml by removing invalid step-level permissions and scoping required rights to the build-push-images job only. This resolves GitHub's validation error when called from workflows that do not grant the full `write-all` set. Apply the same discipline to all other workflows, adding explicit permissions and descriptive comments where missing. This enforces least-privilege and makes future audits straightforward. Signed-off-by: Devon Bautista <17506592+synackd@users.noreply.github.com> --- .../build-publish-container-goreleaser.yml | 7 ++++ .github/workflows/build-rpm-quadlet.yml | 4 +++ .github/workflows/ci.yml | 3 ++ .github/workflows/dependency-review.yml | 2 +- .github/workflows/docker-build-release.yml | 36 +++++++++++-------- .github/workflows/go-build-release.yml | 6 +++- .github/workflows/govulncheck.yml | 2 +- .github/workflows/gpg-sign-artifacts.yml | 4 +++ .github/workflows/lint-workflows.yml | 6 ++-- .github/workflows/pr-registry-cleanup.yml | 2 +- .../workflows/release-signed-artifacts.yml | 2 +- .github/workflows/trivy-image-scan.yml | 4 +-- .github/workflows/validate-rpm-quadlet.yml | 3 ++ 13 files changed, 56 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-publish-container-goreleaser.yml b/.github/workflows/build-publish-container-goreleaser.yml index 80b8b77..1882568 100644 --- a/.github/workflows/build-publish-container-goreleaser.yml +++ b/.github/workflows/build-publish-container-goreleaser.yml @@ -41,6 +41,13 @@ on: registry_subject_name: type: string required: true + +permissions: + contents: write # release creation, uploading assets + packages: write # docker login, image push, upload container provenance + id-token: write # Sigstore signing (attest-build-provenance) + attestations: write # write build provenance attestations + jobs: container_build_publish: runs-on: ubuntu-latest diff --git a/.github/workflows/build-rpm-quadlet.yml b/.github/workflows/build-rpm-quadlet.yml index 4cf0614..4004cab 100644 --- a/.github/workflows/build-rpm-quadlet.yml +++ b/.github/workflows/build-rpm-quadlet.yml @@ -13,6 +13,10 @@ on: description: 'Artifact-name for unsigned RPM artifacts' default: 'rpms-unsigned' type: string + +permissions: + contents: read # baseline for checkout + jobs: rpmbuild: runs-on: ubuntu-latest diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4098a00..c843c63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,9 @@ on: push: branches: [ main ] +permissions: + contents: read # baseline for checkout + jobs: lint: runs-on: ubuntu-latest diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 1eba6c8..1cf93e0 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -36,7 +36,7 @@ on: default: 'on-failure' permissions: - contents: read + contents: read # baseline for checkout pull-requests: write # for comment-summary-in-pr jobs: diff --git a/.github/workflows/docker-build-release.yml b/.github/workflows/docker-build-release.yml index 3913ee0..53e1e62 100644 --- a/.github/workflows/docker-build-release.yml +++ b/.github/workflows/docker-build-release.yml @@ -51,12 +51,18 @@ on: type: string default: '' -permissions: write-all # Necessary for provenance and SBOM attestations +permissions: + contents: read # baseline for all jobs jobs: build-push-images: environment: 'Docker Push' runs-on: ubuntu-latest + permissions: + contents: read # checkout repository + packages: write # docker login, image push, upload container provenance + id-token: write # Sigstore signing (built-in provenance) + attestations: write # built-in provenance & SBOM steps: - name: checkout repository uses: actions/checkout@v6.0.3 @@ -151,18 +157,18 @@ jobs: needs: build-push-images if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') permissions: - contents: write + contents: write # create GitHub release steps: - - name: Parse semver string - id: semver_parser - uses: booxmedialtd/ws-action-parse-semver@v1.4.7 - with: - input_string: ${{ github.event.ref }} - version_extractor_regex: 'refs/tags/v(.*)$' - - uses: ncipollo/release-action@v1.21.0 - with: - # by default this will use the tag push tag as the tag and name - # if we want to trigger tagging from the workflow, "tag" and "commit" - # need to be set to create a new one - prerelease: ${{ steps.semver_parser.outputs.prerelease != '' }} - skipIfReleaseExists: true + - name: Parse semver string + id: semver_parser + uses: booxmedialtd/ws-action-parse-semver@v1.4.7 + with: + input_string: ${{ github.event.ref }} + version_extractor_regex: 'refs/tags/v(.*)$' + - uses: ncipollo/release-action@v1.21.0 + with: + # by default this will use the tag push tag as the tag and name + # if we want to trigger tagging from the workflow, "tag" and "commit" + # need to be set to create a new one + prerelease: ${{ steps.semver_parser.outputs.prerelease != '' }} + skipIfReleaseExists: true diff --git a/.github/workflows/go-build-release.yml b/.github/workflows/go-build-release.yml index 3323aaa..9cee8fe 100644 --- a/.github/workflows/go-build-release.yml +++ b/.github/workflows/go-build-release.yml @@ -71,7 +71,11 @@ on: type: string default: '' -permissions: write-all # Necessary for the generate-build-provenance action with containers +permissions: + contents: write # release creation, uploading assets + packages: write # docker login, image push, upload container provenance + id-token: write # Sigstore signing (attest-build-provenance) + attestations: write # write build provenance attestations jobs: goreleaser: diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index dbb3b8c..7652753 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -25,7 +25,7 @@ on: default: './...' permissions: - contents: read + contents: read # baseline for checkout jobs: govulncheck: diff --git a/.github/workflows/gpg-sign-artifacts.yml b/.github/workflows/gpg-sign-artifacts.yml index 55ffcc1..0ac1222 100644 --- a/.github/workflows/gpg-sign-artifacts.yml +++ b/.github/workflows/gpg-sign-artifacts.yml @@ -25,6 +25,10 @@ on: description: 'Name for the public key composite artifact' type: string default: 'public-keys' + +permissions: + contents: read # baseline for checkout + jobs: artifacts-sign: runs-on: ubuntu-latest diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml index e0a9fb9..49d23de 100644 --- a/.github/workflows/lint-workflows.yml +++ b/.github/workflows/lint-workflows.yml @@ -11,7 +11,7 @@ on: workflow_call: permissions: - contents: read + contents: read # baseline for checkout jobs: actionlint: @@ -29,8 +29,8 @@ jobs: name: zizmor runs-on: ubuntu-latest permissions: - contents: read - security-events: write # for SARIF upload to GHAS + contents: read # baseline for checkout + security-events: write # for SARIF upload to GHAS steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: diff --git a/.github/workflows/pr-registry-cleanup.yml b/.github/workflows/pr-registry-cleanup.yml index 3521e19..bc3934f 100644 --- a/.github/workflows/pr-registry-cleanup.yml +++ b/.github/workflows/pr-registry-cleanup.yml @@ -19,7 +19,7 @@ on: type: string permissions: - packages: write + packages: write # delete container image versions from GHCR jobs: cleanup: diff --git a/.github/workflows/release-signed-artifacts.yml b/.github/workflows/release-signed-artifacts.yml index 5e5955a..981daf9 100644 --- a/.github/workflows/release-signed-artifacts.yml +++ b/.github/workflows/release-signed-artifacts.yml @@ -7,7 +7,7 @@ name: Release signed artifacts run-name: Generate release with signed artifacts for ${{ github.ref }} permissions: - contents: write + contents: write # create GitHub Release and upload artifacts on: workflow_call: inputs: diff --git a/.github/workflows/trivy-image-scan.yml b/.github/workflows/trivy-image-scan.yml index 6d5b143..0edd45f 100644 --- a/.github/workflows/trivy-image-scan.yml +++ b/.github/workflows/trivy-image-scan.yml @@ -36,8 +36,8 @@ on: default: '1' permissions: - contents: read - security-events: write # for SARIF upload to GHAS + contents: read # baseline for checkout + security-events: write # for SARIF upload to GHAS jobs: trivy: diff --git a/.github/workflows/validate-rpm-quadlet.yml b/.github/workflows/validate-rpm-quadlet.yml index c3fc0fc..509292e 100644 --- a/.github/workflows/validate-rpm-quadlet.yml +++ b/.github/workflows/validate-rpm-quadlet.yml @@ -18,6 +18,9 @@ on: required: true type: string +permissions: + contents: read # baseline for checkout + jobs: parse: runs-on: ubuntu-latest