diff --git a/.github/workflows/release-workflow.yml b/.github/workflows/release-workflow.yml new file mode 100644 index 000000000..3638e6348 --- /dev/null +++ b/.github/workflows/release-workflow.yml @@ -0,0 +1,106 @@ +on: + workflow_call: + inputs: + secret_registry: + type: boolean + description: All registry related inputs should be treated as secret values + required: true + + image: + type: string + description: Static image value for the build + + password: + type: string + description: Registry password key to lookup in secrets + required: true + + username: + type: string + description: Username for the registry login + required: true + + registry: + type: string + description: Destination registry for image push + required: true + + + tag: + type: string + description: Tag for the built image + required: true + + arch: + type: string + description: Architecture for the image + required: true + + org: + type: string + description: Organization part of the image name + required: false + default: "rancher-sandbox" + +jobs: + build: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + packages: write + outputs: + digest: ${{ steps.image.outputs.digest }} + image: ${{ steps.image.outputs.image }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Build the image + id: image + uses: ./.github/workflows/release_build + with: + arch: ${{ inputs.arch }} + tag: ${{ inputs.tag }} + org: ${{ inputs.org }} + registry: ${{ inputs.secret_registry && secrets[inputs.registry] || inputs.registry }} + username: ${{ inputs.secret_registry && secrets[inputs.username] || inputs.username }} + password: ${{ secrets[inputs.password] }} + + sign: + runs-on: ubuntu-latest + needs: [build] + permissions: + actions: read + id-token: write + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Sign image with cosign + uses: ./.github/workflows/release_sign + with: + image: ${{ needs.build.outputs.image || format('{0}-{1}', vars[inputs.image], inputs.arch) }} + digest: ${{ needs.build.outputs.digest }} + identity: https://github.com/${{ inputs.org }}/rancher-turtles/.github/workflows/release.yaml@refs/tags/${{ inputs.tag }} + oids-issuer: https://token.actions.githubusercontent.com + registry: ${{ inputs.secret_registry && secrets[inputs.registry] || inputs.registry }} + username: ${{ inputs.secret_registry && secrets[inputs.username] || inputs.username }} + password: ${{ secrets[inputs.password] }} + + provenance: + needs: [sign, build] + permissions: + actions: read + id-token: write + packages: write + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0 + with: + digest: ${{ needs.build.outputs.digest }} + image: ${{ needs.build.outputs.image || format('{0}-{1}', vars[inputs.image], inputs.arch) }} + secrets: + registry-username: ${{ inputs.secret_registry && secrets[inputs.username] || inputs.username }} + registry-password: ${{ secrets[inputs.password] }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2cc21b5f6..16fd20819 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,205 +1,53 @@ + name: release on: + workflow_dispatch: push: tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 - -permissions: - contents: write # Allow to create a release. + - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 jobs: - - build-ghcr: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - outputs: - multiarch_image: ${{ steps.ghcr-images.outputs.multiarch_image }} - multiarch_digest: ${{ steps.ghcr-images.outputs.multiarch_digest }} - amd64_image: ${{ steps.ghcr-images.outputs.amd64_image }} - amd64_digest: ${{ steps.ghcr-images.outputs.amd64_digest }} - arm64_digest: ${{ steps.ghcr-images.outputs.arm64_digest }} - arm64_image: ${{ steps.ghcr-images.outputs.arm64_image }} - s390x_image: ${{ steps.ghcr-images.outputs.s390x_image }} - s390x_digest: ${{ steps.ghcr-images.outputs.s390x_digest }} - env: - TAG: ${{ github.ref_name }} - REGISTRY: ghcr.io - USERNAME: ${{ github.actor }} - ORG: rancher-sandbox - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: setupGo - uses: actions/setup-go@v4 - with: - go-version: '=1.20.7' - - name: Docker login to ghcr registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.USERNAME }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build docker image for gh registry - run: make docker-build-all TAG=${{ env.TAG }} REGISTRY=${{ env.REGISTRY }} - - name: Push docker image to gh registry - run: make docker-push-all TAG=${{ env.TAG }} REGISTRY=${{ env.REGISTRY }} - - name: Store list of ghcr images and digests - id: ghcr-images - run: | - ./scripts/image-digest.sh ${{ env.REGISTRY }} ${{ env.ORG }} ${{ env.TAG }} - - ghcr-sign: - runs-on: ubuntu-latest - needs: [build-ghcr] - permissions: - packages: write - id-token: write - strategy: - matrix: - images: [ - { - "image":"${{ needs.build-ghcr.outputs.multiarch_image }}", - }, - { - "image":"${{ needs.build-ghcr.outputs.amd64_image }}", - }, - { - "image":"${{ needs.build-ghcr.outputs.arm64_image }}", - }, - { - "image":"${{ needs.build-ghcr.outputs.s390x_image }}", - } - ] - env: - TAG: ${{ github.ref_name }} - REGISTRY: ghcr.io - USERNAME: ${{ github.actor }} - ORG: rancher-sandbox - steps: - - name: Docker login to ghcr registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.USERNAME }} - password: ${{ secrets.GITHUB_TOKEN }} - - uses: sigstore/cosign-installer@v3.1.2 - - name: Sign manifests - env: - COSIGN_EXPERIMENTAL: 1 - run: | - image=$(echo ${{ matrix.images.image }} | base64 -d | base64 -d) - cosign sign --yes ${image} - - name: Verify pushed ghcr images - env: - COSIGN_EXPERIMENTAL: 1 - run: | - image=$(echo ${{ matrix.images.image }} | base64 -d | base64 -d) - cosign verify ${image} --certificate-identity=https://github.com/rancher-sandbox/rancher-turtles/.github/workflows/release.yaml@refs/tags/${{ env.TAG }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com - - build-prod: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - outputs: - multiarch_image: ${{ steps.prod-images.outputs.multiarch_image }} - multiarch_digest: ${{ steps.prod-images.outputs.multiarch_digest }} - amd64_image: ${{ steps.prod-images.outputs.amd64_image }} - amd64_digest: ${{ steps.prod-images.outputs.amd64_digest }} - arm64_digest: ${{ steps.prod-images.outputs.arm64_digest }} - arm64_image: ${{ steps.prod-images.outputs.arm64_image }} - s390x_image: ${{ steps.prod-images.outputs.s390x_image }} - s390x_digest: ${{ steps.prod-images.outputs.s390x_digest }} - env: - TAG: ${{ github.ref_name }} - PROD_REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }} - PROD_USERNAME: ${{ secrets.REGISTRY_USERNAME }} - PROD_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} - PROD_ORG: rancher-sandbox - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: setupGo - uses: actions/setup-go@v4 - with: - go-version: '=1.20.7' - - name: Docker login to prod registry - uses: docker/login-action@v3 - with: - registry: ${{ env.PROD_REGISTRY }} - username: ${{ env.PROD_USERNAME }} - password: ${{ env.PROD_PASSWORD }} - - name: Build docker image for prod registry - run: make docker-build-all TAG=${{ env.TAG }} REGISTRY=${{ env.PROD_REGISTRY }} ORG=${{ env.PROD_ORG }} - - name: Push docker image to prod registry - run: make docker-push-all TAG=${{ env.TAG }} REGISTRY=${{ env.PROD_REGISTRY }} ORG=${{ env.PROD_ORG }} - - name: Store list of prod images and digests - id: prod-images - run: | - ./scripts/image-digest.sh ${{ env.PROD_REGISTRY }} ${{ env.PROD_ORG }} ${{ env.TAG }} - - prod-sign: - runs-on: ubuntu-latest - needs: [build-prod] + build-push-services: permissions: + actions: read + contents: write packages: write id-token: write strategy: matrix: - images: [ - { - "image":"${{ needs.build-prod.outputs.multiarch_image }}", - }, - { - "image":"${{ needs.build-prod.outputs.amd64_image }}", - }, - { - "image":"${{ needs.build-prod.outputs.arm64_image }}", - }, - { - "image":"${{ needs.build-prod.outputs.s390x_image }}", - } - ] - env: - TAG: ${{ github.ref_name }} - PROD_REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }} - PROD_USERNAME: ${{ secrets.REGISTRY_USERNAME }} - PROD_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} - steps: - - name: Docker login to prod registry - uses: docker/login-action@v3 - with: - registry: ${{ env.PROD_REGISTRY }} - username: ${{ env.PROD_USERNAME }} - password: ${{ env.PROD_PASSWORD }} - - uses: sigstore/cosign-installer@v3.1.2 - - name: Sign manifests - env: - COSIGN_EXPERIMENTAL: 1 - run: | - image=$(echo ${{ matrix.images.image }} | base64 -d | base64 -d) - cosign sign --yes ${image} - - name: Verify pushed ghcr images - env: - COSIGN_EXPERIMENTAL: 1 - run: | - image=$(echo ${{ matrix.images.image }} | base64 -d | base64 -d) - cosign verify ${image} --certificate-identity=https://github.com/rancher-sandbox/rancher-turtles/.github/workflows/release.yaml@refs/tags/${{ env.TAG }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com + destination: [ghcr, prod] + arch: [amd64, arm64, s390x] + image: [REGISTRY_IMAGE] + include: + - destination: ghcr + registry: ghcr.io + username: ${{ github.actor }} + password: GITHUB_TOKEN + secret_registry: false + - destination: prod + registry: REGISTRY_ENDPOINT + username: REGISTRY_USERNAME + password: REGISTRY_PASSWORD + secret_registry: true + name: Release + uses: ./.github/workflows/release-workflow.yml + with: + password: ${{ matrix.password }} + username: ${{ matrix.username }} + registry: ${{ matrix.registry }} + tag: ${{ github.ref_name }} + arch: ${{ matrix.arch }} + image: ${{ matrix.image }} + secret_registry: ${{ matrix.secret_registry }} + secrets: inherit release: name: Create helm release - needs: [prod-sign] + needs: [build-push-services] runs-on: ubuntu-latest env: TAG: ${{ github.ref_name }} - PROD_REGISTRY: ${{ secrets.REGISTRY_ENDPOINT }} PROD_ORG: rancher-sandbox RELEASE_DIR: .cr-release-packages CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" @@ -208,12 +56,10 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Configure Git run: | git config user.name "$GITHUB_ACTOR" git config user.email "$GITHUB_ACTOR@users.noreply.github.com" - - name: Package operator chart run: RELEASE_TAG=${GITHUB_REF##*/} CHART_PACKAGE_DIR=${RELEASE_DIR} REGISTRY=${{ env.PROD_REGISTRY }} ORG=${{ env.PROD_ORG }} make release diff --git a/.github/workflows/release_build/action.yaml b/.github/workflows/release_build/action.yaml new file mode 100644 index 000000000..b98c19d92 --- /dev/null +++ b/.github/workflows/release_build/action.yaml @@ -0,0 +1,63 @@ +name: "Build release" +description: "Builds release image and pushes to the registry" +inputs: + arch: + description: "Architecture of the built image" + required: true + type: string + tag: + description: "Image tag" + type: string + default: "github-actions" + org: + description: "Organization part of the image path" + required: false + default: "rancher-sandbox" + type: string + registry: + description: "The registry to login" + required: true + type: string + username: + description: "The username to registry" + required: true + type: string + password: + required: true + description: "The password for registry login" + type: string +outputs: + digest: + description: "Image digest" + value: ${{ steps.image_info.outputs.digest }} + image: + description: "Image name" + value: ${{ steps.image_info.outputs.image }} + +runs: + using: "composite" + steps: + - name: setupGo + uses: actions/setup-go@v4 + with: + go-version: "=1.20.7" + - name: Docker login to ghcr registry + uses: docker/login-action@v3 + with: + registry: ${{ inputs.registry }} + username: ${{ inputs.username }} + password: ${{ inputs.password }} + - name: Build docker image + shell: bash + run: make docker-build-${{ inputs.arch }} TAG=${{ inputs.tag }} REGISTRY=${{ inputs.registry }} ORG=${{ inputs.org }} + - name: Push docker image to registry + shell: bash + run: make docker-push-${{ inputs.arch }} TAG=${{ inputs.tag }} REGISTRY=${{ inputs.registry }} ORG=${{ inputs.org }} + - name: Store image and digest + shell: bash + id: image_info + run: | + digest=$( docker images --digests --format "{{.Digest}}" | head -1 ) + image=$( docker images --digests --format "{{.Repository}}" | head -1 ) + echo "digest=${digest}" >> $GITHUB_OUTPUT + echo "image=${image}" >> $GITHUB_OUTPUT diff --git a/.github/workflows/release_sign/action.yaml b/.github/workflows/release_sign/action.yaml new file mode 100644 index 000000000..88ef87440 --- /dev/null +++ b/.github/workflows/release_sign/action.yaml @@ -0,0 +1,58 @@ +name: "Cosign sign" +description: "Signs the image using digest, and pushes the metadata to the registry" +inputs: + image: + description: "The OCI image name. This must not include a tag or digest." + required: true + type: string + digest: + description: "The OCI image digest. The image digest of the form ':' (e.g. 'sha256:abcdef...')" + required: true + type: string + identity: + description: "Full oauth identity for signature verification" + required: true + type: string + oidc-provider: + description: "Specify the provider to get the OIDC token from (Optional). If unset, github-actions will be used. Options include: [spiffe, google, github-actions, filesystem, buildkite-agent]" + type: string + default: "github-actions" + oids-issuer: + description: "Full OIDS issuer URL for signature verification" + required: true + type: string + registry: + description: "The registry to login" + required: true + type: string + username: + description: "The username to registry" + required: true + type: string + password: + required: true + description: "The password key to fetch from secret store for registry login" + type: string + +runs: + using: "composite" + steps: + - name: Docker login to registry + uses: docker/login-action@v3 + with: + registry: ${{ inputs.registry }} + username: ${{ inputs.username }} + password: ${{ inputs.password }} + - uses: sigstore/cosign-installer@v3.1.2 + - name: Sign manifests + shell: bash + env: + COSIGN_EXPERIMENTAL: 1 + run: | + cosign sign --yes ${{ inputs.image }}@${{ inputs.digest }} --oidc-provider=${{ inputs.oidc-provider }} + - name: Verify pushed ghcr images + shell: bash + env: + COSIGN_EXPERIMENTAL: 1 + run: | + cosign verify ${{ inputs.image }}@${{ inputs.digest }} --certificate-identity={{ inputs.identity }} --certificate-oidc-issuer=${{ inputs.oids-issuer }}