diff --git a/.github/.goreleaser.yml b/.github/.goreleaser.yml index c6b771fec8..308535a43b 100644 --- a/.github/.goreleaser.yml +++ b/.github/.goreleaser.yml @@ -40,3 +40,25 @@ archives: checksum: name_template: "eksctl_checksums.txt" + +sboms: + - id: archive + artifacts: archive + # Defaults to `syft`, producing `.sbom.json` in CycloneDX JSON. + +signs: + # Keyless (Sigstore/Fulcio) signature over the checksum file, which in turn + # covers every archive in the release. Requires `id-token: write` on the + # calling job so cosign can exchange the Actions OIDC token for a + # short-lived certificate -- there is no long-lived signing key to hold. + - id: cosign-checksum + cmd: cosign + artifacts: checksum + output: true + certificate: "${artifact}.pem" + args: + - sign-blob + - "--output-certificate=${certificate}" + - "--output-signature=${signature}" + - "${artifact}" + - "--yes" diff --git a/.github/workflows/publish-release.yaml b/.github/workflows/publish-release.yaml index d0456a96d4..14ebb2ece4 100644 --- a/.github/workflows/publish-release.yaml +++ b/.github/workflows/publish-release.yaml @@ -55,6 +55,12 @@ jobs: - name: Setup build environment uses: ./.github/actions/setup-build + - name: Install cosign + uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 #v4.1.2 + + - name: Install syft + uses: anchore/sbom-action/download-syft@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 #v0.24.2 + - name: GoReleaser Release if: ${{ !inputs.isReleaseCandidate }} uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 #v7.2.3 diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml index f6a8495c85..2e6978b0c6 100644 --- a/.github/workflows/release-drafter.yaml +++ b/.github/workflows/release-drafter.yaml @@ -10,15 +10,22 @@ permissions: contents: read jobs: - update_release_draft: + # Renders the draft release notes. This job handles the drafter output, so it + # deliberately does not have access to EKSCTLBOT_TOKEN -- it hands the + # rendered files to update_release_draft_pr via an artifact instead. + render_release_draft: if: github.event.repository.fork == false permissions: # write permission is required to create a github release contents: write - pull-requests: write + # release-drafter only needs to read merged pull requests; the autolabeler + # that would require write is not configured in .github/release-drafter.yml. + pull-requests: read runs-on: ubuntu-latest + outputs: + tag_name: ${{ steps.draft.outputs.tag_name }} steps: - # Drafts your next Release notes as Pull Requests are merged into "master" + # Drafts your next Release notes as Pull Requests are merged into "main" - name: Draft release notes id: draft uses: release-drafter/release-drafter@34d80673e067bdc0c24568d3af899c216adcfaa9 #v7.7.0 @@ -30,40 +37,65 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: commitish: main + - name: Render release notes and pull request body + env: + TAG_NAME: ${{ steps.draft.outputs.tag_name }} + RELEASE_BODY: ${{ steps.draft.outputs.body }} + run: | + mkdir -p "${RUNNER_TEMP}/notes" + printf '%s\n' "$RELEASE_BODY" > "${RUNNER_TEMP}/notes/${TAG_NAME#v}.md" + { + printf '🤖 Copy release notes from Draft\n\n' + printf '
\n' + printf ' Full draft release notes for %s \n' "$TAG_NAME" + printf '
\n\n' + printf '%s\n' "$RELEASE_BODY" + printf '\n
\n' + printf '
\n' + printf '
\n\n' + printf 'Auto-generated by [eksctl Draft Release Notes GitHub workflow][1]\n\n' + printf '[1]: https://github.com/eksctl-io/eksctl/blob/main/.github/workflows/release-drafter.yaml\n' + } > "${RUNNER_TEMP}/notes/pull-request-body.md" + - name: Upload rendered notes + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1 + with: + name: release-notes + path: ${{ runner.temp }}/notes/ + retention-days: 1 + + # Commits the rendered notes and opens the pull request. This is the only job + # that holds EKSCTLBOT_TOKEN, and it treats the drafter output purely as file + # content -- it never interpolates it into a run step. + update_release_draft_pr: + needs: render_release_draft + permissions: + contents: read + runs-on: ubuntu-latest + env: + TAG_NAME: ${{ needs.render_release_draft.outputs.tag_name }} + steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1 with: # The Upsert pull request step below authenticates with its own token, # so there is no need to leave a credential behind in the git config. persist-credentials: false + - name: Download rendered notes + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1 + with: + name: release-notes + path: ${{ runner.temp }}/notes - name: Copy release notes from Draft - env: - TAG_NAME: ${{ steps.draft.outputs.tag_name }} - RELEASE_BODY: ${{ steps.draft.outputs.body }} run: | - printf '%s\n' "$RELEASE_BODY" > "docs/release_notes/${TAG_NAME#v}.md" + cp "${RUNNER_TEMP}/notes/${TAG_NAME#v}.md" "docs/release_notes/${TAG_NAME#v}.md" - name: Upsert pull request uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 #v8.1.1 with: token: ${{ secrets.EKSCTLBOT_TOKEN }} - commit-message: Add release notes for ${{ steps.draft.outputs.tag_name }} + commit-message: Add release notes for ${{ needs.render_release_draft.outputs.tag_name }} committer: eksctl-bot - body: | - 🤖 Copy release notes from Draft - -
- Full draft release notes for ${{ steps.draft.outputs.tag_name }} -
- - ${{ steps.draft.outputs.body }} - -
-
-
- - Auto-generated by [eksctl Draft Release Notes GitHub workflow][1] - - [1]: https://github.com/eksctl-io/eksctl/blob/main/.github/workflows/release-drafter.yaml - title: 'Add release notes for ${{ steps.draft.outputs.tag_name }}' + body-path: ${{ runner.temp }}/notes/pull-request-body.md + title: 'Add release notes for ${{ needs.render_release_draft.outputs.tag_name }}' labels: kind/improvement, skip-release-notes branch: update-release-notes + add-paths: docs/release_notes diff --git a/.github/workflows/start-release.yaml b/.github/workflows/start-release.yaml index 7257e5caa5..263cbffbce 100644 --- a/.github/workflows/start-release.yaml +++ b/.github/workflows/start-release.yaml @@ -33,6 +33,9 @@ jobs: name: release candidate secrets: customToken: ${{ secrets.EKSCTLBOT_TOKEN }} - permissions: + permissions: contents: write pull-requests: write + # Required for cosign keyless signing: lets cosign exchange the Actions + # OIDC token for a short-lived Fulcio certificate. + id-token: write diff --git a/userdocs/src/installation.md b/userdocs/src/installation.md index b7315b31c4..945dce759c 100644 --- a/userdocs/src/installation.md +++ b/userdocs/src/installation.md @@ -78,6 +78,37 @@ rm eksctl_$PLATFORM.zip The `eksctl` executable is placed in `$HOME/bin`, which is in `$PATH` from Git Bash. +### Verifying the release signature + +A checksum tells you the archive you downloaded matches `eksctl_checksums.txt`, but not that +the checksum file itself came from the eksctl release pipeline. From v0.220.0 onwards each +release also publishes a [Sigstore](https://www.sigstore.dev/) signature over the checksum +file — `eksctl_checksums.txt.sig` and `eksctl_checksums.txt.pem` — so the whole chain can be +verified back to the GitHub Actions workflow that built it. + +Verify with [cosign](https://docs.sigstore.dev/cosign/system_config/installation/): + +```sh +BASE="https://github.com/eksctl-io/eksctl/releases/latest/download" +curl -sLO "$BASE/eksctl_checksums.txt" +curl -sLO "$BASE/eksctl_checksums.txt.sig" +curl -sLO "$BASE/eksctl_checksums.txt.pem" + +cosign verify-blob eksctl_checksums.txt \ + --signature eksctl_checksums.txt.sig \ + --certificate eksctl_checksums.txt.pem \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com \ + --certificate-identity-regexp '^https://github.com/eksctl-io/eksctl/\.github/workflows/publish-release\.yaml@refs/tags/' +``` + +The two `--certificate-*` flags are the part that matters: they assert the signature was +produced by that workflow in that repository, not merely by somebody with a Sigstore +identity. Once `cosign` reports `Verified OK`, check your archive against the now-trusted +checksum file as shown above. + +Each archive additionally ships a CycloneDX SBOM at `.sbom.json`, listing the Go +modules compiled into that binary. + ### Docker For every release and RC a container image is pushed to ECR repository `public.ecr.aws/eksctl/eksctl`. Learn more about the usage on [ECR Public Gallery - eksctl](https://gallery.ecr.aws/eksctl/eksctl). For example,