diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml index 400cbc9..72636ee 100644 --- a/.github/workflows/publish-image.yml +++ b/.github/workflows/publish-image.yml @@ -1,33 +1,24 @@ --- name: Docker -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. +# This workflow builds, signs and validates Docker images for CNCF GitHub Actions runners on: push: branches: [main] - # Publish semver tags as releases. tags: ['v*.*.*'] pull_request: branches: [main] release: types: [published] + workflow_dispatch: # Manual trigger capability env: - # Use docker.io for Docker Hub if empty REGISTRY: ghcr.io - # IMAGE_NAME maps to the Docker image name IMAGE_NAME: cncf/external-gha-runner - - # Map current release tag to the version of booty - BOOTY_VERSION: $GITHUB_REF_NAME - # Map last repo update to the build timestamp + BOOTY_VERSION: ${{ github.ref_name }} BOOTY_TIMESTAMP: ${{ github.event.repository.updated_at}} - jobs: build: outputs: @@ -37,122 +28,105 @@ jobs: permissions: contents: read packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. id-token: write steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - # Install the cosign tool except on PR - # https://github.com/sigstore/cosign-installer - name: Install cosign - if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@main + uses: sigstore/cosign-installer@v2.8.1 + with: + cosign-release: 'v2.1.1' + - name: Set up QEMU uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - # Login against a Docker registry except on PR - # https://github.com/docker/login-action - - name: Log into registry ${{ env.REGISTRY }} - if: github.event_name != 'pull_request' - uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + - name: Log into registry + # Only log in when pushing is required + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # Extract metadata (tags, labels) for Docker - # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + uses: docker/metadata-action@v4 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - # Build and push Docker image with Buildx (don't push on PR) - # https://github.com/docker/build-push-action - name: Build and push Docker image id: build-and-push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: ./ci/gha-runner-image/ platforms: linux/amd64,linux/arm64 - push: ${{ github.event_name != 'pull_request' }} + # Allow pushing from PRs from the same repository + push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - # Build and push Docker image with Buildx (don't push on PR) - # https://github.com/docker/build-push-action + - name: Sign the published Docker image - if: ${{ github.event_name != 'pull_request' }} + # Only sign when we've pushed the image + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository env: COSIGN_EXPERIMENTAL: "true" - # uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. run: | - cosign sign --yes \ - ${{env.REGISTRY}}/${{env.IMAGE_NAME}}@${{steps.build-and-push.outputs.digest}} + cosign sign --yes ${{env.REGISTRY}}/${{env.IMAGE_NAME}}@${{steps.build-and-push.outputs.digest}} + - name: Output image id: image run: | - # NOTE: We need to use the image and digest in order to make sure - # that the image we attest has not been modified. - # NOTE: The digest output from docker/build-push-action is of the - # form "sha256:" - image_name="${REGISTRY}/${IMAGE_NAME}:${{ github.ref_name }}" - echo "::set-output name=image::$image_name" + echo "image=${REGISTRY}/${IMAGE_NAME}:${{ github.ref_name }}" >> $GITHUB_OUTPUT + build-openeuler: outputs: image: ${{ steps.image.outputs.image }} - digest: ${{ steps.build-and-push.outputs.digest }} + digest: ${{ steps.build-and-push-openeuler.outputs.digest }} runs-on: ubuntu-latest permissions: contents: read packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. id-token: write steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - # Install the cosign tool except on PR - # https://github.com/sigstore/cosign-installer - name: Install cosign if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@main + uses: sigstore/cosign-installer@v2.8.1 + with: + cosign-release: 'v2.1.1' + - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - # Login against a Docker registry except on PR - # https://github.com/docker/login-action - - name: Log into registry ${{ env.REGISTRY }} + - name: Log into registry if: github.event_name != 'pull_request' - uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # Extract metadata (tags, labels) for Docker - # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + uses: docker/metadata-action@v4 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - # Build and push Docker image with Buildx (don't push on PR) - # https://github.com/docker/build-push-action - name: Build and push OpenEuler Docker image id: build-and-push-openeuler - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v4 with: context: ./ci/gha-runner-image/ platforms: linux/amd64,linux/arm64 @@ -161,63 +135,57 @@ jobs: ghcr.io/cncf/gha-runner:openeuler labels: ${{ steps.meta.outputs.labels }} file: ./ci/gha-runner-image/Dockerfile.openeuler + - name: Sign the published OpenEuler Docker image if: ${{ github.event_name != 'pull_request' }} env: COSIGN_EXPERIMENTAL: "true" - # Uses the identity token to provision an ephemeral certificate - # against the sigstore community Fulcio instance. run: cosign sign -y ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:openeuler + - name: Output image id: image run: | - # NOTE: We need to use the image and digest in order to make sure - # that the image we attest has not been modified. - # NOTE: The digest output from docker/build-push-action is of the - # form "sha256:" - image_name="${REGISTRY}/${IMAGE_NAME}:${{ github.ref_name }}" - echo "::set-output name=image::$image_name" - # Generate SLSA provenance for the image - # Upload the provenance to ghcr.io - provenance: - needs: [build] - permissions: - id-token: write # For signing. - actions: read # For reading workflow info. - packages: write # For uploading attestations. - uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@main - with: - image: ${{ needs.build.outputs.image }} - digest: ${{ needs.build.outputs.digest }} - registry-username: ${{ github.actor }} - compile-generator: true - secrets: - registry-password: ${{ secrets.GITHUB_TOKEN }} - # Verify the created provenance attestation. - verify: - # NOTE: this name is used as the status check name and by protected - # branches for required status checks. It should have a unique name among - # other pre-submits. - name: verify container provenance - needs: [build, provenance] - permissions: - packages: read # For reading attestations. + echo "image=${REGISTRY}/${IMAGE_NAME}:openeuler" >> $GITHUB_OUTPUT + + # Image validation job + validate: + name: validate images + needs: [build, build-openeuler] runs-on: ubuntu-latest - if: ${{ always() }} + # Run validation on PRs from the same repository + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + permissions: + packages: read steps: - - uses: sigstore/cosign-installer@main + - name: Install cosign + uses: sigstore/cosign-installer@v2.8.1 with: - cosign-release: 'v2.4.2' - - env: - REGISTRY_USERNAME: ${{ github.actor }} - REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + cosign-release: 'v2.1.1' + + - name: Verify standard image + env: IMAGE_NAME: ${{ needs.build.outputs.image }} IMAGE_DIGEST: ${{ needs.build.outputs.digest }} run: | - cosign version \ - COSIGN_EXPERIMENTAL=1 cosign verify-attestation \ - --certificate-identity-regexp=".*" \ - --certificate-oidc-issuer-regexp=".*" \ - --type slsaprovenance "${{ env.IMAGE_NAME }}@${{env.IMAGE_DIGEST}}" - # TODO (github.com/slsa-framework/slsa-verifier/issues/92): - # Add step to verify using slsa-verifier + echo "Verifying image: $IMAGE_NAME@$IMAGE_DIGEST" + + if [ -z "$IMAGE_DIGEST" ]; then + echo "Error: Image digest is empty" + exit 1 + fi + + COSIGN_EXPERIMENTAL=1 cosign verify $IMAGE_NAME@$IMAGE_DIGEST + + - name: Verify OpenEuler image + env: + IMAGE_NAME: ${{ needs.build-openeuler.outputs.image }} + IMAGE_DIGEST: ${{ needs.build-openeuler.outputs.digest }} + run: | + echo "Verifying OpenEuler image: $IMAGE_NAME@$IMAGE_DIGEST" + + if [ -z "$IMAGE_DIGEST" ]; then + echo "Error: OpenEuler image digest is empty" + exit 1 + fi + + COSIGN_EXPERIMENTAL=1 cosign verify $IMAGE_NAME@$IMAGE_DIGEST