chore(deps): update nginxinc/nginx-unprivileged:1.27.4-alpine-slim do… #259
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: container-build | |
# Based on: | |
# https://docs.docker.com/build/ci/github-actions/multi-platform/ | |
# https://github.com/named-data/actions/blob/9cc5e94a08ae58ac033821e11fd44947d0f5f9bc/.github/workflows/docker-image.yml | |
on: | |
workflow_dispatch: | |
inputs: | |
# keep-sorted start | |
container_image_sign: | |
description: "SBOM + Sign the container image" | |
type: boolean | |
required: false | |
default: false | |
container_image_vulnerability_scan: | |
description: "Scan the container image for vulnerabilities" | |
type: boolean | |
required: false | |
default: true | |
container_registry_push: | |
description: "Push the container image to the registry" | |
type: boolean | |
required: false | |
default: false | |
# keep-sorted end | |
workflow_call: | |
inputs: | |
# keep-sorted start | |
container_image_sign: | |
type: boolean | |
required: true | |
container_registry_push: | |
type: boolean | |
required: true | |
release_tag: | |
type: string | |
required: true | |
# keep-sorted end | |
push: | |
branches-ignore: | |
- renovate/* | |
paths: | |
- Dockerfile | |
schedule: | |
- cron: 1 1 1 * * | |
permissions: | |
actions: read | |
# Use concurrency to prevent running when ghcr-cleanup is running | |
concurrency: | |
group: container-build | |
defaults: | |
run: | |
shell: bash -euxo pipefail {0} | |
env: | |
# keep-sorted start | |
container_image_authors: [email protected] | |
container_image_authors_name: Petr Ruzicka | |
container_image_category: security | |
container_image_dockerfile_location: Dockerfile | |
container_image_logo_url: https://raw.githubusercontent.com/MISP/intelligence-icons/513abc840b7ac92e4f8a4a7ecab2964007bf25f5/svg/threat_actor.svg | |
container_image_platforms: linux/amd64,linux/arm64 | |
container_image_repository_url: https://quay.io/repository/petr_ruzicka/${{ github.event.repository.name }}?tab=tags | |
container_image_vendor: MyCompany | |
container_registries: | | |
# keep-sorted start | |
- registry: docker.io | |
image_name: docker.io/peru/${{ github.event.repository.name }} | |
username: ${{ secrets.dockerhub_container_registry_user }} | |
password: ${{ secrets.dockerhub_container_registry_password }} | |
- registry: ghcr.io | |
image_name: ghcr.io/${{ github.repository }} | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- registry: quay.io | |
image_name: quay.io/petr_ruzicka/${{ github.event.repository.name }} | |
username: ${{ secrets.quay_container_registry_user }} | |
password: ${{ secrets.quay_container_registry_password }} | |
# keep-sorted end | |
# Push the container image to the registry when triggered by a scheduled execution or a push to the default branch | |
container_registry_push_image: ${{ github.event_name == 'schedule' || inputs.container_registry_push || (github.ref_name == github.event.repository.default_branch && github.event_name == 'push') }} | |
# keep-sorted end | |
jobs: | |
# Workaround (GitHub Actions: Use variables in matrix definition?): https://stackoverflow.com/questions/74072206/github-actions-use-variables-in-matrix-definition | |
container-build-push-matrix-platform: | |
runs-on: ubuntu-latest | |
outputs: | |
matrix_platform_json: ${{ steps.matrix-platform.outputs.MATRIX_PLATFORM_JSON }} | |
steps: | |
- name: Parse container_image_platforms for Matrix Platform | |
id: matrix-platform | |
env: | |
CONTAINER_IMAGE_PLATFORMS: ${{ env.container_image_platforms }} | |
run: | | |
echo "MATRIX_PLATFORM_JSON=$(jq -cR 'split(",")' <<< "${CONTAINER_IMAGE_PLATFORMS}")" | tee -a "${GITHUB_OUTPUT}" | |
container-build-push: | |
name: ${{ matrix.platform }} - build | |
runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} | |
needs: container-build-push-matrix-platform | |
outputs: | |
container_image_platform_digest: ${{ steps.docker-build-push.outputs.digest }} | |
strategy: | |
matrix: | |
platform: ${{ fromJSON(needs.container-build-push-matrix-platform.outputs.matrix_platform_json) }} | |
permissions: | |
contents: read | |
id-token: write # for creating OIDC tokens for signing | |
packages: write # for uploading attestations | |
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results | |
steps: | |
- name: Checkout | |
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
- name: Set variables | |
env: | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
CONTAINER_IMAGE_DOCKERFILE_LOCATION: ${{ env.container_image_dockerfile_location }} | |
run: | | |
# Create list of image names for docker/metadata-action ("images" parameter) | |
# Create list of image locations for docker/metadata-action ("labels" [io.artifacthub.package.alternative-locations] parameter) | |
DOCKER_IMAGE_NAMES="$(yq '[.[].image_name] | join(",") | downcase' <<< "${CONTAINER_REGISTRIES}")" | |
echo "DOCKER_IMAGE_NAMES=${DOCKER_IMAGE_NAMES}" | tee -a "${GITHUB_ENV}" | |
# Create list of topics for io.artifacthub.package.keywords | |
echo '${{ toJSON(github.event.repository.topics) }}' | jq -r '. | join(",") | "GITHUB_REPOSITORY_TOPICS=\(.)"' | tee -a "${GITHUB_ENV}" | |
# Get the directory where the Dockerfile is located | |
echo "CONTAINER_IMAGE_DOCKER_BUILD_DIRECTORY=$(dirname "${CONTAINER_IMAGE_DOCKERFILE_LOCATION}")" | tee -a "${GITHUB_ENV}" | |
# Replace "/" by "-" in platform variable to be used in "Export digest" | |
echo "PLATFORM=$(echo "${{ matrix.platform }}" | tr '/' '-')" | tee -a "${GITHUB_ENV}" | |
- name: Login to container registries | |
if: ${{ env.container_registry_push_image == 'true' }} | |
env: | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
run: | | |
# Login to all registries | |
readarray CONTAINER_REGISTRIES_ARRAY < <(yq e -o=j -I=0 '.[]' <<< "${CONTAINER_REGISTRIES}") | |
for CONTAINER_REGISTRY in "${CONTAINER_REGISTRIES_ARRAY[@]}"; do | |
REGISTRY="$(jq -r '.registry' <<< "${CONTAINER_REGISTRY}")" | |
USERNAME="$(jq -r '.username' <<< "${CONTAINER_REGISTRY}")" | |
PASSWORD="$(jq -r '.password' <<< "${CONTAINER_REGISTRY}")" | |
docker login "${REGISTRY}" --username "${USERNAME}" --password-stdin <<< "${PASSWORD}" | |
done | |
- name: Set up QEMU | |
if: ${{ !contains(matrix.platform, 'amd64') && !contains(matrix.platform, 'arm') }} | |
uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 # v3.4.0 | |
with: | |
platforms: ${{ matrix.platform }} | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0 | |
- name: Build temporary container image | |
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 | |
id: docker-build-push-temporary | |
env: | |
DOCKER_BUILD_SUMMARY: false | |
with: | |
file: ${{ env.container_image_dockerfile_location }} | |
context: ${{ env.CONTAINER_IMAGE_DOCKER_BUILD_DIRECTORY }} | |
platforms: ${{ matrix.platform }} | |
load: true | |
tags: temporary_container-${{ github.run_id }}:latest | |
- name: Grype [table] - scan container image | |
uses: anchore/scan-action@7c05671ae9be166aeb155bad2d7df9121823df32 # v6.1.0 | |
with: | |
fail-build: ${{ inputs.container_image_vulnerability_scan }} | |
image: temporary_container-${{ github.run_id }}:latest | |
only-fixed: true | |
output-format: table | |
severity-cutoff: high | |
# Write to multiple output files (like syft): https://github.com/anchore/grype/issues/648 | |
- name: Grype [sarif] - scan container image | |
uses: anchore/scan-action@7c05671ae9be166aeb155bad2d7df9121823df32 # v6.1.0 | |
if: ${{ env.container_registry_push_image == 'true' }} | |
id: grype-scan | |
with: | |
fail-build: false | |
image: temporary_container-${{ github.run_id }}:latest | |
only-fixed: true | |
output-format: sarif | |
severity-cutoff: high | |
- name: Docker metadata | |
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1 | |
if: ${{ env.container_registry_push_image == 'true' }} | |
id: docker_meta | |
with: | |
labels: | | |
## Replace deprecated NGINX maintainer: https://docs.docker.com/reference/dockerfile/#maintainer-deprecated (crane config nginxinc/nginx-unprivileged | jq '.config.Labels') | |
maintainer=${{ env.container_image_authors }} | |
## Container images repositories: https://artifacthub.io/docs/topics/repositories/container-images/ | |
# keep-sorted start sticky_comments=no | |
# org.opencontainers.image.created - it is there by default | |
# org.opencontainers.image.description - it is there by default (repository description) | |
# org.opencontainers.image.source - it is there by default | |
# org.opencontainers.image.title - it is there by default | |
# org.opencontainers.image.url - it is there by default | |
# org.opencontainers.image.version - it is there by default | |
io.artifacthub.package.alternative-locations=${{ env.DOCKER_IMAGE_NAMES }} | |
io.artifacthub.package.category=${{ env.container_image_category }} | |
io.artifacthub.package.keywords=${{ env.GITHUB_REPOSITORY_TOPICS }} | |
io.artifacthub.package.license=${{ github.event.repository.license.spdx_id }} | |
io.artifacthub.package.logo-url=${{ env.container_image_logo_url || 'https://raw.githubusercontent.com/kubernetes/community/487f994c013ea61d92cf9a341af7620037abbce3/icons/svg/resources/unlabeled/pod.svg' }} | |
io.artifacthub.package.maintainers=[{"name":"${{ env.container_image_authors_name }}","email":"${{ env.container_image_authors }}"}] | |
io.artifacthub.package.readme-url=https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}/README.md | |
org.opencontainers.image.documentation=${{ github.event.repository.html_url }}/blob/${{ github.sha }}/README.md | |
org.opencontainers.image.vendor=${{ env.container_image_vendor }} | |
# keep-sorted end | |
## The OpenContainers Annotations Spec: https://specs.opencontainers.org/image-spec/annotations/?v=v1.0.1#pre-defined-annotation-keys | |
# keep-sorted start sticky_comments=no | |
# org.opencontainers.image.created - it is there by default | |
# org.opencontainers.image.description - it is there by default (repository description) | |
# org.opencontainers.image.documentation - already set | |
# org.opencontainers.image.licenses - it is there by default | |
# org.opencontainers.image.revision - it is there by default | |
# org.opencontainers.image.source - it is there by default | |
# org.opencontainers.image.title - it is there by default | |
# org.opencontainers.image.url - it is there by default | |
# org.opencontainers.image.vendor - already set | |
# org.opencontainers.image.version - it is there by default | |
org.opencontainers.image.authors=${{ env.container_image_authors }} | |
org.opencontainers.image.ref.name=${{ github.ref_name }} | |
# keep-sorted end | |
## Label Schema Convention: https://github.com/badouralix/dockerfiles/blob/c91181b356f92574f26d0499ee3d2be2cacd0952/LABELS.md | |
# keep-sorted start sticky_comments=no | |
com.github.actions.event_name=${{ github.event_name }} | |
com.github.actions.job=${{ github.job }} | |
com.github.actions.run_id=${{ github.run_id }} | |
com.github.actions.run_url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
# keep-sorted end | |
- name: Push image to container registries | |
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 | |
if: ${{ env.container_registry_push_image == 'true' }} | |
id: docker-build-push | |
env: | |
DOCKER_BUILD_RECORD_UPLOAD: false | |
with: | |
file: ${{ env.container_image_dockerfile_location }} | |
context: ${{ env.CONTAINER_IMAGE_DOCKER_BUILD_DIRECTORY }} | |
platforms: ${{ matrix.platform }} | |
provenance: false | |
outputs: type=image,"name=${{ env.DOCKER_IMAGE_NAMES }}",push-by-digest=true,name-canonical=true,push=true | |
annotations: ${{ steps.docker_meta.outputs.labels }} | |
labels: ${{ steps.docker_meta.outputs.labels }} | |
- name: Install cosign | |
uses: sigstore/cosign-installer@c56c2d3e59e4281cc41dea2217323ba5694b171e # v3.8.0 | |
if: ${{ env.container_registry_push_image == 'true' && inputs.container_image_sign }} | |
- name: Install Syft | |
uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0 | |
if: ${{ env.container_registry_push_image == 'true' && inputs.container_image_sign }} | |
- name: Sign + create SBOM | |
if: ${{ env.container_registry_push_image == 'true' && inputs.container_image_sign }} | |
env: | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
DIGEST: ${{ steps.docker-build-push.outputs.digest }} | |
PLATFORM: ${{ matrix.platform }} | |
run: | | |
while read -r CONTAINER_REGISTRY_IMAGE_NAME; do | |
syft attest --output cyclonedx-json --platform "${PLATFORM}" "${CONTAINER_REGISTRY_IMAGE_NAME}@${DIGEST}" | |
done <<< "$(yq '.[].image_name | downcase' <<< "${CONTAINER_REGISTRIES}")" | |
- name: Export digest | |
if: ${{ env.container_registry_push_image == 'true' }} | |
working-directory: ${{ runner.temp }} | |
env: | |
DIGEST: ${{ steps.docker-build-push.outputs.digest }} | |
PLATFORM: ${{ matrix.platform }} | |
run: | | |
mkdir digests | |
touch "digests/${DIGEST#sha256:}" | |
- name: Upload digest | |
if: ${{ env.container_registry_push_image == 'true' }} | |
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 | |
with: | |
name: digests-${{ env.PLATFORM }} | |
path: ${{ runner.temp }}/digests/* | |
if-no-files-found: error | |
retention-days: 1 | |
- name: Publish SARIF to github code scanning | |
if: ${{ env.container_registry_push_image == 'true' && steps.grype-scan.outputs.sarif != '' && github.ref_name == github.event.repository.default_branch }} | |
uses: github/codeql-action/upload-sarif@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 | |
with: | |
sarif_file: ${{ steps.grype-scan.outputs.sarif }} | |
- name: Remove docker credentials | |
if: ${{ always() && env.container_registry_push_image == 'true' }} | |
env: | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
run: | | |
while read -r CONTAINER_REGISTRY; do | |
docker logout "${CONTAINER_REGISTRY}" | |
done <<< "$(yq '.[].registry' <<< "${CONTAINER_REGISTRIES}")" | |
container-build-push-merge: | |
name: Merge container images and generate sbom and signature | |
runs-on: ubuntu-latest | |
needs: container-build-push | |
if: ${{ needs.container-build-push.outputs.container_image_platform_digest != '' }} | |
permissions: | |
id-token: write # for creating OIDC tokens for signing | |
packages: write # for uploading attestations | |
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results | |
outputs: | |
container_image_digest: ${{ steps.sign-sbom.outputs.CONTAINER_IMAGE_DIGEST }} | |
steps: | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0 | |
- name: Download digests | |
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 | |
id: download | |
with: | |
path: ${{ runner.temp }}/digests | |
pattern: digests-* | |
merge-multiple: true | |
- name: Login to container registries | |
env: | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
run: | | |
# Login to all registries | |
readarray CONTAINER_REGISTRIES_ARRAY < <(yq e -o=j -I=0 '.[]' <<< "${CONTAINER_REGISTRIES}") | |
for CONTAINER_REGISTRY in "${CONTAINER_REGISTRIES_ARRAY[@]}"; do | |
REGISTRY="$(jq -r '.registry' <<< "${CONTAINER_REGISTRY}")" | |
USERNAME="$(jq -r '.username' <<< "${CONTAINER_REGISTRY}")" | |
PASSWORD="$(jq -r '.password' <<< "${CONTAINER_REGISTRY}")" | |
docker login "${REGISTRY}" --username "${USERNAME}" --password-stdin <<< "${PASSWORD}" | |
done | |
- name: Set variables | |
env: | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
run: | | |
# Create list of image names for docker/metadata-action ("images" parameter) | |
CONTAINER_REGISTRY_IMAGE_NAMES=$(yq ".[].image_name | downcase" <<< "${CONTAINER_REGISTRIES}" ) | |
echo "CONTAINER_REGISTRY_IMAGE_NAMES<<EOF"$'\n'"${CONTAINER_REGISTRY_IMAGE_NAMES}"$'\n'EOF | tee -a "${GITHUB_ENV}" | |
- name: Docker metadata | |
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1 | |
id: docker_meta | |
env: | |
# https://github.com/docker/docs/issues/18835 | |
DOCKER_METADATA_ANNOTATIONS_LEVELS: index | |
with: | |
images: | | |
${{ env.CONTAINER_REGISTRY_IMAGE_NAMES }} | |
tags: | | |
# Whever the pipeline runs form "main" - use "latest" container tag | |
type=raw,value=latest,enable={{is_default_branch}} | |
# When the pipeline is not executed form 'main' branch use "br-<mybranch>" tag | |
type=ref,prefix=br-,event=branch,enable=${{ github.ref_name != github.event.repository.default_branch }} | |
# Create "1.1.1", "1.1", "1" tags when called from release-please workflow (inputs.release_tag is "1.1.1") | |
type=semver,pattern={{version}},value=${{ inputs.release_tag }},enable=${{ inputs.release_tag != '' }} | |
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.release_tag }},enable=${{ inputs.release_tag != '' }} | |
type=semver,pattern={{major}},value=${{ inputs.release_tag }},enable=${{ !startsWith(inputs.release_tag, '0.') }} | |
flavor: | | |
latest=false | |
- name: Install cosign | |
uses: sigstore/cosign-installer@c56c2d3e59e4281cc41dea2217323ba5694b171e # v3.8.0 | |
if: ${{ inputs.container_image_sign }} | |
- name: Install Syft | |
uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0 | |
if: ${{ inputs.container_image_sign }} | |
- name: Create and push multi-platform image index | |
env: | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
working-directory: ${{ steps.download.outputs.download-path }} | |
run: | | |
while read -r CONTAINER_REGISTRY_IMAGE_NAME; do | |
readarray -t ANNOTATIONS < <(jq -r '.annotations[] | ("--annotation", .)' <<< "${DOCKER_METADATA_OUTPUT_JSON}") | |
readarray -t TAGS < <(jq -r --arg CONTAINER_REGISTRY_IMAGE_NAME "${CONTAINER_REGISTRY_IMAGE_NAME}" '.tags[] | select(contains($CONTAINER_REGISTRY_IMAGE_NAME)) | ("--tag", .)' <<< "${DOCKER_METADATA_OUTPUT_JSON}") | |
readarray -t SOURCES < <(printf "${CONTAINER_REGISTRY_IMAGE_NAME}@sha256:%s\n" *) | |
docker buildx imagetools create "${ANNOTATIONS[@]}" "${TAGS[@]}" "${SOURCES[@]}" | |
done <<< "$(yq '.[].image_name | downcase' <<< "${CONTAINER_REGISTRIES}")" | |
- name: Sign + create SBOM | |
if: ${{ inputs.container_image_sign }} | |
id: sign-sbom | |
env: | |
DOCKER_METADATA_TAG_0: ${{ fromJSON(steps.docker_meta.outputs.json).tags[0] }} | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
run: | | |
# Get the digest of the image from fist container registry (due to: https://github.com/docker/buildx/issues/2407) | |
CONTAINER_IMAGE_DIGEST="$(docker buildx imagetools inspect "${DOCKER_METADATA_TAG_0}" --format "{{json .Manifest.Digest}}" | jq -r)" | |
echo "CONTAINER_IMAGE_DIGEST=${CONTAINER_IMAGE_DIGEST}" | tee -a "${GITHUB_OUTPUT}" | |
while read -r CONTAINER_REGISTRY_IMAGE_NAME; do | |
cosign sign --recursive --yes "${CONTAINER_REGISTRY_IMAGE_NAME}@${CONTAINER_IMAGE_DIGEST}" | |
# Syft doesn't support recursive attestation yet - this is only for index manifest | |
syft attest --output cyclonedx-json "${CONTAINER_REGISTRY_IMAGE_NAME}@${CONTAINER_IMAGE_DIGEST}" | |
done <<< "$(yq '.[].image_name | downcase' <<< "${CONTAINER_REGISTRIES}")" | |
- name: Remove docker credentials | |
if: ${{ always() }} | |
env: | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
run: | | |
while read -r CONTAINER_REGISTRY; do | |
docker logout "${CONTAINER_REGISTRY}" | |
done <<< "$(yq '.[].registry' <<< "${CONTAINER_REGISTRIES}")" | |
provenance: | |
name: ${{ matrix.registry }} - provenance | |
needs: container-build-push-merge | |
if: ${{ inputs.container_image_sign || inputs.release_tag != '' }} | |
permissions: | |
actions: read # for detecting the Github Actions environment. | |
id-token: write # for creating OIDC tokens for signing. | |
packages: write # for uploading attestations. | |
strategy: | |
# NOTE: These are the reasons why I need to repeat the whole container_registries section again :-( | |
# GH env variables can not be used in matrix: https://stackoverflow.com/questions/74072206/github-actions-use-variables-in-matrix-definition | |
# Secrets can not be used in matrix: https://github.com/orgs/community/discussions/26302 | |
# GH Reusable Workflow can not be run as step: https://stackoverflow.com/questions/75733616/github-actions-how-to-call-a-reusable-workflow-as-a-step | |
matrix: | |
include: | |
# keep-sorted start | |
- registry: docker.io | |
image_name: docker.io/peru/${{ github.event.repository.name }} | |
username: peru | |
password: dockerhub_container_registry_password | |
- registry: ghcr.io | |
image_name: ghcr.io/${{ github.repository }} | |
username: ${{ github.actor }} | |
password: GITHUB_TOKEN | |
- registry: quay.io | |
image_name: quay.io/petr_ruzicka/${{ github.event.repository.name }} | |
username: petr_ruzicka+github_actions_access | |
password: quay_container_registry_password | |
# keep-sorted end | |
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected] | |
with: | |
image: ${{ matrix.image_name }} | |
digest: ${{ needs.container-build-push-merge.outputs.container_image_digest }} | |
registry-username: ${{ matrix.username }} | |
secrets: | |
registry-password: ${{ secrets[matrix.password] }} | |
verify-signatures-provenance-sbom: | |
name: verify signatures and provenance | |
needs: [container-build-push-merge, provenance] | |
runs-on: ubuntu-latest | |
if: ${{ inputs.container_image_sign || inputs.release_tag != '' }} | |
steps: | |
- name: Verify signatures, provenance and SBOM | |
env: | |
CONTAINER_IMAGE_PLATFORMS: ${{ env.container_image_platforms }} | |
CONTAINER_REGISTRIES: ${{ env.container_registries }} | |
COSIGN_CERTIFICATE_IDENTITY_REGEXP: ${{ github.event.repository.html_url }}/.github/workflows | |
COSIGN_CERTIFICATE_OIDC_ISSUER: https://token.actions.githubusercontent.com | |
COSIGN_ATTESTATION_TYPE: https://cyclonedx.org/bom | |
TRIVY_DISABLE_VEX_NOTICE: true | |
CONTAINER_IMAGE_DIGEST: ${{ needs.container-build-push-merge.outputs.container_image_digest }} | |
run: | | |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" | |
brew install cosign grype regclient slsa-verifier | |
while read -r CONTAINER_REGISTRY_IMAGE_NAME; do | |
CONTAINER_REGISTRY_IMAGE="${CONTAINER_REGISTRY_IMAGE_NAME}@${CONTAINER_IMAGE_DIGEST}" | |
echo "*** ${CONTAINER_REGISTRY_IMAGE_NAME}" | |
# # Example from Chainguard | |
# # https://images.chainguard.dev/directory/image/go/provenance | |
# CONTAINER_REGISTRY_IMAGE_NAME="cgr.dev/chainguard/go" | |
# CONTAINER_IMAGE_DIGEST=$(regctl image digest "cgr.dev/chainguard/go:latest") | |
# CONTAINER_REGISTRY_IMAGE="cgr.dev/chainguard/go@${CONTAINER_IMAGE_DIGEST}" | |
# COSIGN_CERTIFICATE_IDENTITY_REGEXP="https://github.com/chainguard-images/images/" | |
# COSIGN_CERTIFICATE_OIDC_ISSUER="https://token.actions.githubusercontent.com" | |
# COSIGN_ATTESTATION_TYPE="https://spdx.dev/Document" | |
# CONTAINER_IMAGE_PLATFORMS="linux/amd64,linux/arm64" | |
# CONTAINER_REGISTRY_IMAGE_NAME="c8n.io/ruzickap-github/malware-cryptominer-container-test" | |
# CONTAINER_IMAGE_DIGEST=$(regctl image digest "c8n.io/ruzickap-github/malware-cryptominer-container-test:latest") | |
# CONTAINER_REGISTRY_IMAGE="c8n.io/ruzickap-github/malware-cryptominer-container-test@${CONTAINER_IMAGE_DIGEST}" | |
# COSIGN_CERTIFICATE_IDENTITY_REGEXP="https://github.com/ruzickap/gha-test/.github/workflows" | |
# COSIGN_CERTIFICATE_OIDC_ISSUER="https://token.actions.githubusercontent.com" | |
# COSIGN_ATTESTATION_TYPE="https://cyclonedx.org/bom" | |
# CONTAINER_IMAGE_PLATFORMS="linux/amd64,linux/arm64" | |
##################################### | |
# Cosign | |
##################################### | |
# Verify the manifest list is signed | |
cosign verify \ | |
--certificate-identity-regexp="${COSIGN_CERTIFICATE_IDENTITY_REGEXP}" \ | |
--certificate-oidc-issuer="${COSIGN_CERTIFICATE_OIDC_ISSUER}" \ | |
"${CONTAINER_REGISTRY_IMAGE}" | jq --color-output | |
# Verify if every platfrom image manifest is signed | |
while read -r MANIFEST_DIGESTS; do | |
cosign verify \ | |
--certificate-identity-regexp="${COSIGN_CERTIFICATE_IDENTITY_REGEXP}" \ | |
--certificate-oidc-issuer="${COSIGN_CERTIFICATE_OIDC_ISSUER}" \ | |
"${CONTAINER_REGISTRY_IMAGE_NAME}@${MANIFEST_DIGESTS}" | jq --color-output | |
done <<< "$(regctl manifest get "${CONTAINER_REGISTRY_IMAGE}" --format '{{jsonPretty .}}' | jq -r '.manifests[].digest')" | |
##################################### | |
# SBOM | |
##################################### | |
cosign verify-attestation --type="${COSIGN_ATTESTATION_TYPE}" \ | |
--certificate-oidc-issuer="${COSIGN_CERTIFICATE_OIDC_ISSUER}" \ | |
--certificate-identity-regexp="${COSIGN_CERTIFICATE_IDENTITY_REGEXP}" \ | |
"${CONTAINER_REGISTRY_IMAGE}" | jq --color-output '.payload |= .[:2000] + "...<rest_is_removed>..."' --color-output | |
cosign verify-attestation --type="${COSIGN_ATTESTATION_TYPE}" \ | |
--certificate-oidc-issuer="${COSIGN_CERTIFICATE_OIDC_ISSUER}" \ | |
--certificate-identity-regexp="${COSIGN_CERTIFICATE_IDENTITY_REGEXP}" \ | |
"${CONTAINER_REGISTRY_IMAGE}" | jq '.payload | @base64d | fromjson | .predicate' | grype | |
for PLATFORM in ${CONTAINER_IMAGE_PLATFORMS//,/ }; do | |
cosign download attestation --platform="${PLATFORM}" --predicate-type="${COSIGN_ATTESTATION_TYPE}" \ | |
"${CONTAINER_REGISTRY_IMAGE}" | jq -r .payload | base64 -d | jq .predicate | grype --add-cpes-if-none | |
done | |
##################################### | |
# SLSA Provenance | |
##################################### | |
cosign verify-attestation --type="slsaprovenance" \ | |
--certificate-oidc-issuer="${COSIGN_CERTIFICATE_OIDC_ISSUER}" \ | |
--certificate-identity-regexp='^https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@refs/tags/v[0-9]+.[0-9]+.[0-9]+$' \ | |
"${CONTAINER_REGISTRY_IMAGE}" | jq --color-output | |
slsa-verifier verify-image --print-provenance --source-uri "github.com/${GITHUB_REPOSITORY}" \ | |
"${CONTAINER_REGISTRY_IMAGE}" | jq --color-output | |
##################################### | |
# Container Registry Manifests | |
##################################### | |
cosign tree "${CONTAINER_REGISTRY_IMAGE}" | |
cosign download attestation "${CONTAINER_REGISTRY_IMAGE}" | jq --color-output '.payload |= .[:2000] + "...<rest_is_removed>..."' | |
# Note: "regctl manifest get" should contain "Annotations" | |
regctl manifest get "${CONTAINER_REGISTRY_IMAGE}" | |
regctl manifest get "${CONTAINER_REGISTRY_IMAGE/@*/}:${CONTAINER_IMAGE_DIGEST/:/-}.att" | |
for PLATFORM in ${CONTAINER_IMAGE_PLATFORMS//,/ }; do | |
regctl image config --platform="${PLATFORM}" "${CONTAINER_REGISTRY_IMAGE}" | jq --color-output | |
done | |
done <<< "$(yq '.[].image_name | downcase' <<< "${CONTAINER_REGISTRIES}")" |