From 95c2d0d0bf2ec4f65e46cfb7ccfeab31cba2dc6d Mon Sep 17 00:00:00 2001 From: Justin Marquis <34fathombelow@protonmail.com> Date: Wed, 12 Jul 2023 12:14:36 -0700 Subject: [PATCH] ci: generate attestations during a release (#2785) * ci: use keyless signing for main and release branches Signed-off-by: Justin Marquis <34fathombelow@protonmail.com> * fix typo Signed-off-by: Justin Marquis <34fathombelow@protonmail.com> * ci: generate attestations during a release Signed-off-by: Justin Marquis <34fathombelow@protonmail.com> * add release trigger script Signed-off-by: Justin Marquis <34fathombelow@protonmail.com> --------- Signed-off-by: Justin Marquis <34fathombelow@protonmail.com> Signed-off-by: zachaller Co-authored-by: zachaller Signed-off-by: zachaller --- .github/workflows/README.md | 39 +++ .github/workflows/release.yaml | 368 ++++++++++++++----------- docs/release-action.png | Bin 48610 -> 0 bytes docs/releasing.md | 65 +++-- docs/{ => security}/security.md | 0 docs/security/signed-release-assets.md | 116 ++++++++ hack/trigger-release.sh | 56 ++++ mkdocs.yml | 6 +- 8 files changed, 459 insertions(+), 191 deletions(-) create mode 100644 .github/workflows/README.md delete mode 100644 docs/release-action.png rename docs/{ => security}/security.md (100%) create mode 100644 docs/security/signed-release-assets.md create mode 100755 hack/trigger-release.sh diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000000..158a90d4c1 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,39 @@ +# Workflows + +| Workflow | Description | +|---------------------|-----------------------------------------------------------------| +| changelog.yml | Updates changelog when a release is published | +| codeql.yaml | CodeQL analysis | +| docker-publish.yaml | Build container image for PR's & publish for push events | +| image-reuse.yaml | Build, push, and Sign container images | +| go.yaml | lint, build, codegen | +| pr-title-check.yaml | Lint PR for semantic information | +| init-release.yaml | Build manifests and version then create a PR for release branch | +| release.yaml | Build images, cli-binaries, provenances, and post actions | + + +# Reusable workflows + +## image-reuse.yaml + +- The resuable workflow can be used to publish or build images with multiple container registries(Quay,GHCR, dockerhub), and then sign them with cosign when an image is published. +- A GO version `must` be specified e.g. 1.19 +- The image name for each registry *must* contain the tag. Note: multiple tags are allowed for each registry using a CSV type. +- Multiple platforms can be specified e.g. linux/amd64,linux/arm64 +- Images are not published by default. A boolean value must be set to `true` to push images. +- An optional target can be specified. + +| Inputs | Description | Type | Required | Defaults | +|-------------------|-------------------------------------|-------------|----------|-----------------| +| go-version | Version of Go to be used | string | true | none | +| quay_image_name | Full image name and tag | CSV, string | false | none | +| ghcr_image_name | Full image name and tag | CSV, string | false | none | +| docker_image_name | Full image name and tag | CSV, string | false | none | +| platforms | Platforms to build (linux/amd64) | CSV, string | false | linux/amd64 | +| push | Whether to push image/s to registry | boolean | false | false | +| target | Target build stage | string | false | none | + +| Outputs | Description | Type | +|-------------|------------------------------------------|-------| +|image-digest | Image digest of image container created | string| + diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 8794e839d9..ceaf0b0d49 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,132 +1,178 @@ name: Release - on: - workflow_dispatch: - inputs: - tag: - description: Git tag to build release from - required: true - update_stable_tag: - description: 'Update stable tag' - required: true - type: boolean - default: 'false' -permissions: - contents: read + push: + tags: + - 'v*' + +permissions: {} + +env: + GOLANG_VERSION: '1.19' # Note: go-version must also be set in job controller-image.with.go-version & plugin-image.with.go-version. jobs: - release-images: + controller-image: + permissions: + contents: read + packages: write # Required and used to push images to `ghcr.io` if used. + id-token: write # For creating OIDC tokens for signing. + uses: ./.github/workflows/image-reuse.yaml + with: + quay_image_name: quay.io/argoproj/argo-rollouts:${{ github.ref_name }} + # Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations) + go-version: 1.19 + platforms: linux/amd64,linux/arm64 + push: true + secrets: + quay_username: ${{ secrets.QUAY_USERNAME }} + quay_password: ${{ secrets.QUAY_ROBOT_TOKEN }} + + plugin-image: + permissions: + contents: read + packages: write # Required and used to push images to `ghcr.io` if used. + id-token: write # For creating OIDC tokens for signing. + uses: ./.github/workflows/image-reuse.yaml + with: + quay_image_name: quay.io/argoproj/kubectl-argo-rollouts:${{ github.ref_name }} + # Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations) + go-version: 1.19 + platforms: linux/amd64,linux/arm64 + push: true + target: kubectl-argo-rollouts + secrets: + quay_username: ${{ secrets.QUAY_USERNAME }} + quay_password: ${{ secrets.QUAY_ROBOT_TOKEN }} + + controller-image-provenance: + needs: + - controller-image + permissions: + actions: read # for detecting the Github Actions environment. + id-token: write # for creating OIDC tokens for signing. + packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues) + # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.6.0 + with: + image: quay.io/argoproj/argo-rollouts + digest: ${{ needs.controller-image.outputs.image-digest }} + secrets: + registry-username: ${{ secrets.QUAY_USERNAME }} + registry-password: ${{ secrets.QUAY_ROBOT_TOKEN }} + + plugin-image-provenance: + needs: + - plugin-image + permissions: + actions: read # for detecting the Github Actions environment. + id-token: write # for creating OIDC tokens for signing. + packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues) + # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.6.0 + with: + image: quay.io/argoproj/kubectl-argo-rollouts + digest: ${{ needs.plugin-image.outputs.image-digest }} + secrets: + registry-username: ${{ secrets.QUAY_USERNAME }} + registry-password: ${{ secrets.QUAY_ROBOT_TOKEN }} + + + release-artifacts: + permissions: + contents: write # for softprops/action-gh-release to create GitHub release runs-on: ubuntu-latest + outputs: + hashes: ${{ steps.hash.outputs.hashes }} steps: - name: Checkout - uses: actions/checkout@v3.1.0 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: - ref: ${{ github.event.inputs.tag }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - - name: Get SHA - id: get-sha - run: echo "::set-output name=sha::$(git log -1 --format='%H')" + - name: Setup Golang + uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 + with: + go-version: ${{ env.GOLANG_VERSION }} - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - with: - config-inline: | - [worker.oci] - gc = false + uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2.5.0 - - name: Print Disk Usage + - name: Generate release artifacts run: | - df -ah - docker buildx du + make release-plugins + make checksums + make manifests IMAGE_TAG=${{ github.ref_name }} - - name: Docker meta (controller) - id: controller-meta - uses: docker/metadata-action@v4 - with: - images: | - quay.io/argoproj/argo-rollouts - # ghcr.io/argoproj/argo-rollouts - tags: | - type=semver,pattern={{version}},prefix=v,value=${{ github.event.inputs.tag }} - flavor: | - latest=false - - - name: Docker meta (plugin) - id: plugin-meta - uses: docker/metadata-action@v4 - with: - images: | - quay.io/argoproj/kubectl-argo-rollouts - # ghcr.io/argoproj/kubectl-argo-rollouts - tags: | - type=semver,pattern={{version}},prefix=v,value=${{ github.event.inputs.tag }} - flavor: | - latest=false - - # - name: Login to GitHub Container Registry - # if: github.event_name != 'pull_request' - # uses: docker/login-action@v2 - # with: - # registry: ghcr.io - # username: ${{ github.repository_owner }} - # password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to Quay.io - if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + - name: Draft release + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15 with: - registry: quay.io - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_ROBOT_TOKEN }} + tag_name: ${{ github.event.inputs.tag }} + draft: true + files: | + dist/kubectl-argo-rollouts-linux-amd64 + dist/kubectl-argo-rollouts-linux-arm64 + dist/kubectl-argo-rollouts-darwin-amd64 + dist/kubectl-argo-rollouts-darwin-arm64 + dist/kubectl-argo-rollouts-windows-amd64 + dist/argo-rollouts-checksums.txt + manifests/dashboard-install.yaml + manifests/install.yaml + manifests/namespace-install.yaml + manifests/notifications-install.yaml + docs/features/kustomize/rollout_cr_schema.json + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push (controller-image) - uses: docker/build-push-action@37abcedcc1da61a57767b7588cb9d03eb57e28b3 # v3.3.0 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.controller-meta.outputs.tags }} - provenance: false - sbom: false - - - name: Build and push (plugin-image) - uses: docker/build-push-action@37abcedcc1da61a57767b7588cb9d03eb57e28b3 # v3.3.0 - with: - context: . - target: kubectl-argo-rollouts - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.plugin-meta.outputs.tags }} - provenance: false - sbom: false + - name: Generate hashes for provenance + id: hash + run: | + echo "hashes=$(sha256sum ./dist/kubectl-argo-rollouts-* ./manifests/*.yaml | base64 -w0)" >> "$GITHUB_OUTPUT" - release-artifacts: + release-artifacts-provenance: + needs: + - release-artifacts permissions: - contents: write # for softprops/action-gh-release to create GitHub release + actions: read # for detecting the Github Actions environment + id-token: write # Needed for provenance signing and ID + contents: write # Needed for release uploads + # Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.6.0 + with: + base64-subjects: "${{ needs.release-artifacts.outputs.hashes }}" + provenance-name: "argo-rollouts.intoto.jsonl" + upload-assets: true + draft-release: true + + generate-sbom: + name: Create Sbom and sign assets + needs: + - release-artifacts + - release-artifacts-provenance + permissions: + contents: write # Needed for release uploads + id-token: write # Needed for signing Sbom runs-on: ubuntu-latest - needs: release-images - steps: - - name: Checkout - uses: actions/checkout@v3.1.0 + - name: Checkout code + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.3.0 with: - ref: ${{ github.event.inputs.tag }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Golang - uses: actions/setup-go@v3 + uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: - go-version: 1.19 + go-version: ${{ env.GOLANG_VERSION }} - - name: Generate release artifacts - run: | - make release-plugins - make checksums - make manifests IMAGE_TAG=${{ github.event.inputs.tag }} + - name: Install cosign + uses: sigstore/cosign-installer@204a51a57a74d190b284a0ce69b44bc37201f343 # v3.0.3 + with: + cosign-release: 'v2.0.2' - name: Generate SBOM (spdx) id: spdx-builder @@ -138,8 +184,8 @@ jobs: # comma delimited list of project relative folders to inspect for package # managers (gomod, yarn, npm). PROJECT_FOLDERS: ".,./ui" - # full qualified name of the docker image to be inspected - DOCKER_IMAGE: quay.io/argoproj/argo-rollouts:${{ github.event.inputs.tag }} + # full qualified name of the container image to be inspected + CONTAINER_IMAGE: quay.io/argoproj/argo-rollouts:${{ github.event.inputs.tag }} run: | yarn install --cwd ./ui @@ -152,82 +198,74 @@ jobs: generator -p $folder -o /tmp done - # Generate SPDX for binaries analyzing the docker image - if [[ ! -z $DOCKER_IMAGE ]]; then - bom generate -o /tmp/bom-docker-image.spdx -i $DOCKER_IMAGE + # Generate SPDX for binaries analyzing the container image + if [[ ! -z CONTAINER_IMAGE ]]; then + bom generate -o /tmp/bom-docker-image.spdx -i $CONTAINER_IMAGE fi cd /tmp && tar -zcf sbom.tar.gz *.spdx - - name: Login to Quay.io - if: github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - registry: quay.io - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_ROBOT_TOKEN }} + - name: Sign SBOM + run: | + cosign sign-blob \ + --output-certificate=/tmp/sbom.tar.gz.pem \ + --output-signature=/tmp/sbom.tar.gz.sig \ + --yes \ + /tmp/sbom.tar.gz - - name: Install cosign - uses: sigstore/cosign-installer@main + - name: Upload SBOM and signature assets + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - cosign-release: 'v1.13.1' + tag_name: ${{ github.ref_name }} + draft: true + files: | + /tmp/sbom.tar.* - - name: Install crane to get digest of image - uses: imjasonh/setup-crane@v0.2 + post-release: + needs: + - release-artifacts + - generate-sbom + permissions: + contents: write # Needed to push commit to update stable tag + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.3.0 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - - name: Get digest of controller-image + - name: Setup Git author information run: | - echo "CONTROLLER_DIGEST=$(crane digest quay.io/argoproj/argo-rollouts:${{ github.event.inputs.tag }})" >> $GITHUB_ENV + set -ue + git config --global user.email 'ci@argoproj.com' + git config --global user.name 'CI' - - name: Get digest of plugin-image + - name: Check if tag is the latest version and not a pre-release run: | - echo "PLUGIN_DIGEST=$(crane digest quay.io/argoproj/kubectl-argo-rollouts:${{ github.event.inputs.tag }})" >> $GITHUB_ENV + set -xue + # Fetch all tag information + git fetch --prune --tags --force - - name: Sign Argo Rollouts Images - run: | - cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/argo-rollouts@${{ env.CONTROLLER_DIGEST }} - cosign sign --key env://COSIGN_PRIVATE_KEY quay.io/argoproj/kubectl-argo-rollouts@${{ env.PLUGIN_DIGEST }} - env: - COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}} - COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}} + LATEST_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | tail -n1) - - name: Sign checksums and create public key for release assets - run: | - cosign sign-blob --key env://COSIGN_PRIVATE_KEY ./dist/argo-rollouts-checksums.txt > ./dist/argo-rollouts-checksums.sig - cosign public-key --key env://COSIGN_PRIVATE_KEY > ./dist/argo-rollouts-cosign.pub - cosign sign-blob --key env://COSIGN_PRIVATE_KEY /tmp/sbom.tar.gz > /tmp/sbom.tar.gz.sig - # Displays the public key to share. - cosign public-key --key env://COSIGN_PRIVATE_KEY - env: - COSIGN_PRIVATE_KEY: ${{secrets.COSIGN_PRIVATE_KEY}} - COSIGN_PASSWORD: ${{secrets.COSIGN_PASSWORD}} + PRE_RELEASE=false + # Check if latest tag is a pre-release + if echo $LATEST_TAG | grep -E -- '-rc[0-9]+$';then + PRE_RELEASE=true + fi - - name: update stable tag for docs + # Ensure latest tag matches github.ref_name & not a pre-release + if [[ $LATEST_TAG == ${{ github.ref_name }} ]] && [[ $PRE_RELEASE != 'true' ]];then + echo "TAG_STABLE=true" >> $GITHUB_ENV + else + echo "TAG_STABLE=false" >> $GITHUB_ENV + fi + + - name: Update stable tag to latest version run: | - git tag -f stable ${{ github.event.inputs.tag }} + git tag -f stable ${{ github.ref_name }} git push -f origin stable - if: ${{ inputs.update_stable_tag }} - - - name: Draft release - uses: softprops/action-gh-release@v1 - with: - tag_name: ${{ github.event.inputs.tag }} - draft: true - files: | - dist/kubectl-argo-rollouts-linux-amd64 - dist/kubectl-argo-rollouts-linux-arm64 - dist/kubectl-argo-rollouts-darwin-amd64 - dist/kubectl-argo-rollouts-darwin-arm64 - dist/kubectl-argo-rollouts-windows-amd64 - dist/argo-rollouts-checksums.txt - dist/argo-rollouts-checksums.sig - dist/argo-rollouts-cosign.pub - manifests/dashboard-install.yaml - manifests/install.yaml - manifests/namespace-install.yaml - manifests/notifications-install.yaml - docs/features/kustomize/rollout_cr_schema.json - /tmp/sbom.tar.gz - /tmp/sbom.tar.gz.sig - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: ${{ env.TAG_STABLE == 'true' }} \ No newline at end of file diff --git a/docs/release-action.png b/docs/release-action.png deleted file mode 100644 index 6c0dc61f5ffbee0e7065fd3dcf3c73cdf5b3113e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48610 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%0AH_-2I(`qP$tX8mXXCv{f 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 MqUOC)c`M}q0?L$ym;e9( diff --git a/docs/releasing.md b/docs/releasing.md index d4571c076c..5b0fc7780a 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,45 +1,62 @@ # Releasing -1. Tag a commit to release from using semantic versioning (e.g. v1.0.0-rc1) +1. Ensure that the `release branch` already exist. -1. Visit the [Release GitHub Action](https://github.com/argoproj/argo-rollouts/actions/workflows/release.yaml) - and enter the tag. +1. Checkout the release branch. Example: `git fetch upstream && git + checkout release-1.5` -1. To automatically update the stable tag, select `Update stable tag`. (false by default) +1. Run the script found at `hack/trigger-release.sh` as follows: -[![GitHub Release Action](release-action.png)](release-action.png) +```shell +./hack/trigger-release.sh +``` + +Example: +```shell +./hack/trigger-release.sh v1.6.0-rc1 upstream +``` + +!!! tip +The tag must be in one of the following formats to trigger the GH workflow:
+* GA: `v..`
+* Pre-release: `v..-rc` + +Once the script is executed successfully, a GitHub workflow will start +execution. You can follow its progress under the [Actions](https://github.com/argoproj/argo-rollouts/actions/workflows/release.yaml) tab, the name of the action is `Release`. 1. When the action completes, visit the generated draft [Github releases](https://github.com/argoproj/argo-rollouts/releases) and enter the details about the release: - * Getting started (copy from previous release and new version) - * Changelog + * Getting started (copy from previous release and new version) + * Changelog + +### Update Brew formula 1. Update Brew formula: - * Fork the repo https://github.com/argoproj/homebrew-tap - * Run the following commands to update the brew formula: + * Fork the repo https://github.com/argoproj/homebrew-tap + * Run the following commands to update the brew formula: ```bash cd homebrew-tap ./update.sh kubectl-argo-rollouts $VERSION ``` - * If there is a new minor version we want to update the versioned formula as well: - * Run the following commands to update the versioned brew formula: - ```bash - ./update.sh kubectl-argo-rollouts $VERSION @ - ``` - * Example: If the new version is `v1.3.2`, we want to update the formula for `v1.3` as well. + * If there is a new minor version we want to update the versioned formula as well: + * Run the following commands to update the versioned brew formula: ```bash - ./update.sh kubectl-argo-rollouts v1.3.2 @1.3 + ./update.sh kubectl-argo-rollouts $VERSION @ ``` - * Commit and push the changes to your fork - ```bash - git commit -am "Update kubectl-argo-rollouts to $VERSION" - ``` - * Create a PR with the modified files pointing to upstream/master - * Once the PR is approved by a maintainer, it can be merged. + * Example: If the new version is `v1.3.2`, we want to update the formula for `v1.3` as well. + ```bash + ./update.sh kubectl-argo-rollouts v1.3.2 @1.3 + ``` + * Commit and push the changes to your fork + ```bash + git commit -am "Update kubectl-argo-rollouts to $VERSION" + ``` + * Create a PR with the modified files pointing to upstream/master + * Once the PR is approved by a maintainer, it can be merged. ### Verify -1. Install locally using the command below and follow the [Getting Started Guide](https://argoproj.github.io/argo-rollouts/getting-started/): +1. Install locally using the command below and follow the [Getting Started Guide](https://argo-rollouts.readthedocs.io/en/stable/getting-started/): ```bash kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/download/${VERSION}/install.yaml @@ -50,4 +67,4 @@ ```bash brew upgrade kubectl-argo-rollouts kubectl argo rollouts version - ``` + ``` \ No newline at end of file diff --git a/docs/security.md b/docs/security/security.md similarity index 100% rename from docs/security.md rename to docs/security/security.md diff --git a/docs/security/signed-release-assets.md b/docs/security/signed-release-assets.md new file mode 100644 index 0000000000..7f6bf3bbfd --- /dev/null +++ b/docs/security/signed-release-assets.md @@ -0,0 +1,116 @@ +# Verification of Argo Rollouts Artifacts + +## Prerequisites +- cosign `v2.0.0` or higher [installation instructions](https://docs.sigstore.dev/cosign/installation) +- slsa-verifier [installation instructions](https://github.com/slsa-framework/slsa-verifier#installation) + +*** +## Release Assets +| Asset | Description | +|-------------------------------------|--------------------------------------------------| +| argo-rollouts-checksums.txt | Checksums of binaries | +| argo-rollouts-cli.intoto.jsonl | Attestation of CLI binaries & manifiest | +| dashboard-install.yaml | Dashboard install | +| install.yaml | Standard installation method | +| kubectl-argo-rollouts-darwin-amd64 | CLI Binary | +| kubectl-argo-rollouts-darwin-arm64 | CLI Binary | +| kubectl-argo-rollouts-linux-amd64 | CLI Binary | +| kubectl-argo-rollouts-linux-arm64 | CLI Binary | +| kubectl-argo-rollouts-windows-amd64 | CLI Binary | +| namespace-install.yaml | Namespace installation | +| notifications-install.yaml | Notification installation | +| rollout_cr_schema.json | Schema | +| sbom.tar.gz | Sbom | +| sbom.tar.gz.pem | Certificate used to sign sbom | +| sbom.tar.gz.sig | Signature of sbom | + +*** +## Verification of container images + +Argo Rollouts container images are signed by [cosign](https://github.com/sigstore/cosign) using identity-based ("keyless") signing and transparency. Executing the following command can be used to verify the signature of a container image: + +```bash +cosign verify \ +--certificate-identity-regexp https://github.com/argoproj/argo-rollouts/.github/workflows/image-reuse.yaml@refs/tags/v \ +--certificate-oidc-issuer https://token.actions.githubusercontent.com \ +quay.io/argoproj/argo-rollouts:v1.5.0 | jq +``` +The command should output the following if the container image was correctly verified: +```bash +The following checks were performed on each of these signatures: + - The cosign claims were validated + - Existence of the claims in the transparency log was verified offline + - The code-signing certificate was verified using trusted certificate authority certificates +[ + { + "critical": { + "identity": { + "docker-reference": "quay.io/argoproj/argo-rollouts" + }, + "image": { + "docker-manifest-digest": "sha256:519522f8c66c7b4c468f360ebe6c8ba07b8d64f5f948e71ae52c01b9953e1eb9" + }, + "type": "cosign container image signature" + }, + "optional": { + "1.3.6.1.4.1.57264.1.1": "https://token.actions.githubusercontent.com", + "1.3.6.1.4.1.57264.1.2": "push", + "1.3.6.1.4.1.57264.1.3": "aa1afcb418fcebcc68b063377c48225f5a9d1511", + "1.3.6.1.4.1.57264.1.4": "Release", + "1.3.6.1.4.1.57264.1.5": "argoproj/argo-rollouts", + "1.3.6.1.4.1.57264.1.6": "refs/tags/v1.5.0", + ... +``` + +*** +## Verification of container image attestations + +A [SLSA](https://slsa.dev/) Level 3 provenance is generated using [slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator). + +The following command will verify the signature of an attestation and how it was issued. It will contain the payloadType, payload, and signature. +```bash +cosign verify-attestation --type slsaprovenance \ +--certificate-identity-regexp https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/tags/v \ +--certificate-oidc-issuer https://token.actions.githubusercontent.com \ +quay.io/argoproj/argo-rollouts:v1.5.0 | jq +``` +The payload is a non-falsifiable provenance which is base64 encoded and can be viewed by using the command below: +```bash +cosign verify-attestation --type slsaprovenance \ +--certificate-identity-regexp https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/tags/v \ +--certificate-oidc-issuer https://token.actions.githubusercontent.com \ +quay.io/argoproj/argo-rollouts:v1.5.0 | jq -r .payload | base64 -d | jq +``` +!!! tip + `cosign` or `slsa-verifier` can both be used to verify image attestations. + Check the documentation of each binary for detailed instructions. + +*** +## Verification of CLI artifacts with attestations + +A single attestation (`argo-rollouts.intoto.jsonl`) from each release is provided. This can be used with [slsa-verifier](https://github.com/slsa-framework/slsa-verifier#verification-for-github-builders) to verify that a CLI binary or manifest was generated using Argo Rollouts workflows on GitHub and ensures it was cryptographically signed. +```bash +slsa-verifier verify-artifact kubectl-argo-rollouts-linux-amd64 --provenance-path kubectl-argo-rollouts.intoto.jsonl --source-uri github.com/argoproj/argo-rollouts +``` +## Verifying an artifact and output the provenance + +```bash +slsa-verifier verify-artifact kubectl-argo-rollouts-linux-amd64 --provenance-path kubectl-argo-rollouts.intoto.jsonl --source-uri github.com/argoproj/argo-rollouts --print-provenance | jq +``` +## Verification of Sbom + +```bash +cosign verify-blob --signature sbom.tar.gz.sig --certificate sbom.tar.gz.pem \ +--certificate-identity-regexp ^https://github.com/argoproj/argo-rollouts/.github/workflows/release.yaml@refs/tags/v \ +--certificate-oidc-issuer https://token.actions.githubusercontent.com \ + ~/Downloads/sbom.tar.gz | jq +``` + +*** +## Verification on Kubernetes + +### Policy controllers +!!! note + We encourage all users to verify signatures and provenances with your admission/policy controller of choice. Doing so will verify that an image was built by us before it's deployed on your Kubernetes cluster. + +Cosign signatures and SLSA provenances are compatible with several types of admission controllers. Please see the [cosign documentation](https://docs.sigstore.dev/cosign/overview/#kubernetes-integrations) and [slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#verification) for supported controllers. diff --git a/hack/trigger-release.sh b/hack/trigger-release.sh new file mode 100755 index 0000000000..b7f843fad7 --- /dev/null +++ b/hack/trigger-release.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# This script requires bash shell - sorry. + +NEW_TAG="${1}" +GIT_REMOTE="${2}" + +set -ue + +if test "${NEW_TAG}" = "" -o "${GIT_REMOTE}" = ""; then + echo "!! Usage: $0 " >&2 + exit 1 +fi + +# Target (version) tag must match version scheme vMAJOR.MINOR.PATCH with an +# optional pre-release tag. +if ! echo "${NEW_TAG}" | egrep -q '^v[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)*$'; then + echo "!! Malformed version tag: '${NEW_TAG}', must match 'vMAJOR.MINOR.PATCH(-rcX)'" >&2 + exit 1 +fi + +# Check whether we are in correct branch of local repository +RELEASE_BRANCH="${NEW_TAG%\.[0-9]*}" +RELEASE_BRANCH="release-${RELEASE_BRANCH#*v}" + +currentBranch=$(git branch --show-current) +if test "$currentBranch" != "${RELEASE_BRANCH}"; then + echo "!! Please checkout branch '${RELEASE_BRANCH}' (currently in branch: '${currentBranch}')" >&2 + exit 1 +fi + +echo ">> Working in release branch '${RELEASE_BRANCH}'" + +echo ">> Ensuring release branch is up to date." +# make sure release branch is up to date +git pull ${GIT_REMOTE} ${RELEASE_BRANCH} + +# Check for target (version) tag in local repo +if git tag -l | grep -q -E "^${NEW_TAG}$"; then + echo "!! Target version tag '${NEW_TAG}' already exists in local repository" >&2 + exit 1 +fi + +# Check for target (version) tag in remote repo +if git ls-remote ${GIT_REMOTE} refs/tags/${NEW_TAG} | grep -q -E "${NEW_TAG}$"; then + echo "!! Target version tag '${NEW_TAG}' already exists in remote '${GIT_REMOTE}'" >&2 + exit 1 +fi + +echo ">> Creating new release '${NEW_TAG}' by pushing '${NEW_TAG}' to '${GIT_REMOTE}'" + +# Create new tag in local repository +git tag ${NEW_TAG} + +# Push the new tag to remote repository +git push ${GIT_REMOTE} ${NEW_TAG} diff --git a/mkdocs.yml b/mkdocs.yml index 3781e5d314..48e39a2578 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -124,8 +124,10 @@ nav: - Best Practices: best-practices.md - Migrating: migrating.md - FAQ: FAQ.md -- Security: security.md -- Roadmap: roadmap.md +- Security: + - Security Policy: security/security.md + - Verify Release Artifacts: security/signed-release-assets.md +- Roadmap/Release Schedule: roadmap.md - Contributing: - Contribution Guide: CONTRIBUTING.md - Environment Setup: getting-started/setup/index.md