diff --git a/.github/workflows/k8s-content.yaml b/.github/workflows/k8s-content.yaml new file mode 100644 index 00000000000..3289f7a5277 --- /dev/null +++ b/.github/workflows/k8s-content.yaml @@ -0,0 +1,161 @@ +--- +name: Kubernetes content + +on: + push: + branches: [ 'master' ] + +env: + COSIGN_EXPERIMENTAL: 1 + IMAGE_NAME: ghcr.io/ComplianceAsCode/k8scontent + +jobs: + container: + runs-on: ubuntu-latest + + permissions: + packages: write + + outputs: + image-digest: ${{ steps.container_info.outputs.image-digest }} + image-tags: ${{ steps.container_info.outputs.image-tags }} + + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Login to ghcr.io + uses: docker/login-action@v1.14.1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # TODO: Rename ocp4_content to k8s_content and make it more general + - name: Build container image + run: | + revision="$(git rev-parse "${GITHUB_REF_NAME:-HEAD}")" + docker build \ + -f ./Dockerfiles/ocp4_content \ + -t "${IMAGE_NAME}:latest" \ + -t "${IMAGE_NAME}:${GITHUB_REF_NAME}" \ + -t "${IMAGE_NAME}:${revision}" \ + --label "org.opencontainers.image.source=https://github.com/ComplianceAsCode/content" \ + --label "org.opencontainers.image.created=$(date --iso-8601=seconds)" \ + --label "org.opencontainers.image.title=k8scontent" \ + --label "org.opencontainers.image.revision=${revision}" \ + --label "org.opencontainers.image.version=${GITHUB_REF_NAME}" \ + --label "org.opencontainers.image.licenses=BSD" \ + . + + - name: Publish Container images + run: docker push "${IMAGE_NAME}" --all-tags + + - name: Get container info + id: container_info + run: | + image_digest="$(docker inspect "${IMAGE_NAME}:latest" --format '{{ index .RepoDigests 0 }}' | awk -F '@' '{ print $2 }')" + image_tags="latest,${GITHUB_REF_NAME},$(git rev-parse "${GITHUB_REF_NAME:-HEAD}")" + echo "::set-output name=image-digest::${image_digest}" + echo "::set-output name=image-tags::${image_tags}" + + sign: + runs-on: ubuntu-latest + needs: [container] + + permissions: + packages: write + id-token: write + + env: + IMAGE_DIGEST: ${{ needs.container.outputs.image-digest }} + + steps: + - name: Install cosign + uses: sigstore/cosign-installer@v2.2.1 + + - name: Login to ghcr.io + uses: docker/login-action@v1.14.1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Sign image + run: | + cosign sign "${IMAGE_NAME}@${IMAGE_DIGEST}" + echo "::notice title=Verify signature::COSIGN_EXPERIMENTAL=1 cosign verify ${IMAGE_NAME}@${IMAGE_DIGEST} | jq '.[0]'" + echo "::notice title=Inspect signature bundle::COSIGN_EXPERIMENTAL=1 cosign verify ${IMAGE_NAME}@${IMAGE_DIGEST} | jq '.[0].optional.Bundle.Payload.body |= @base64d | .[0].optional.Bundle.Payload.body | fromjson'" + echo "::notice title=Inspect certificate::COSIGN_EXPERIMENTAL=1 cosign verify ${IMAGE_NAME}@${IMAGE_DIGEST} | jq -r '.[0].optional.Bundle.Payload.body |= @base64d | .[0].optional.Bundle.Payload.body | fromjson | .spec.signature.publicKey.content |= @base64d | .spec.signature.publicKey.content' | openssl x509 -text" + + sbom: + runs-on: ubuntu-latest + needs: [container] + + permissions: + packages: write + id-token: write + + env: + IMAGE_DIGEST: ${{ needs.container.outputs.image-digest }} + + steps: + - name: Install cosign + uses: sigstore/cosign-installer@v2.2.1 + + - name: Install Syft + uses: anchore/sbom-action/download-syft@v0.11.0 + + - name: Login to ghcr.io + uses: docker/login-action@v1.14.1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Attach SBOM to image + run: | + syft "${IMAGE_NAME}@${IMAGE_DIGEST}" -o spdx-json=sbom-spdx.json + cosign attest --predicate sbom-spdx.json --type spdx "${IMAGE_NAME}@${IMAGE_DIGEST}" + echo "::notice title=Verify SBOM attestation::COSIGN_EXPERIMENTAL=1 cosign verify-attestation ${IMAGE_NAME}@${IMAGE_DIGEST} | jq '.payload |= @base64d | .payload | fromjson | select(.predicateType == \"https://spdx.dev/Document\") | .predicate.Data | fromjson'" + + provenance: + runs-on: ubuntu-latest + needs: [container] + + permissions: + packages: write + id-token: write + + env: + IMAGE_DIGEST: ${{ needs.container.outputs.image-digest }} + PROVENANCE_FILE: provenance.att + + steps: + - name: Install cosign + uses: sigstore/cosign-installer@v2.2.1 + + - name: Generate provenance + uses: philips-labs/slsa-provenance-action@v0.7.2 + with: + command: generate + subcommand: container + arguments: --repository "${IMAGE_NAME}" --output-path "${PROVENANCE_FILE}" --digest "${IMAGE_DIGEST}" --tags "${IMAGE_TAGS}" + env: + COSIGN_EXPERIMENTAL: 0 + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + IMAGE_TAGS: ${{ needs.container.outputs.image-tags }} + + - name: Login to ghcr.io + uses: docker/login-action@v1.14.1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Attach provenance + run: | + jq '.predicate' "${PROVENANCE_FILE}" > provenance-predicate.att + cosign attest --predicate provenance-predicate.att --type slsaprovenance "${IMAGE_NAME}@${IMAGE_DIGEST}" + echo "::notice title=Verify provenance attestation::COSIGN_EXPERIMENTAL=1 cosign verify-attestation ${IMAGE_NAME}@${IMAGE_DIGEST} | jq '.payload |= @base64d | .payload | fromjson | select(.predicateType == \"https://slsa.dev/provenance/v0.2\")'" \ No newline at end of file