From bd08e71c8c57010cee8aada7b717becbd30bc31a Mon Sep 17 00:00:00 2001 From: Mathew Payne <2772944+GeekMasher@users.noreply.github.com> Date: Mon, 18 Nov 2024 11:03:13 +0000 Subject: [PATCH] feat: Update container build, scanning, and publishing --- .github/workflows/container-publish.yml | 124 +++++++++++++++ .github/workflows/container-security.yml | 72 +++++++++ .github/workflows/container.yml | 185 ++++++++--------------- 3 files changed, 256 insertions(+), 125 deletions(-) create mode 100644 .github/workflows/container-publish.yml create mode 100644 .github/workflows/container-security.yml diff --git a/.github/workflows/container-publish.yml b/.github/workflows/container-publish.yml new file mode 100644 index 0000000..5b970eb --- /dev/null +++ b/.github/workflows/container-publish.yml @@ -0,0 +1,124 @@ +name: Container Build and Release + +on: + workflow_call: + inputs: + version: + description: "Semantic version of the image" + required: true + type: string + + container-file: + description: "Path to the Dockerfile" + type: string + default: "Dockerfile" + + container-name: + description: "Name of the container" + type: string + default: "${{ github.repository }}" + + sbom: + description: "Generate and upload SBOM" + type: string + default: "true" + + signing: + description: "Sign the image" + type: string + default: "false" + +env: + REGISTRY: ghcr.io + +jobs: + publish-image: + runs-on: ubuntu-latest + + permissions: + # to upload SBOM + id-token: write + contents: write + # to upload Docker image + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 + + - name: Log in to the Container registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set Container Metadata + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 + with: + images: ${{ env.REGISTRY }}/${{ inputs.container-name }} + tags: | + # latest / main + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} + # SemVer + type=semver,pattern={{version}},value=${{ inputs.version }} + type=semver,pattern=v{{version}},value=${{ inputs.version }} + type=semver,pattern=v{{major}},value=${{ inputs.version }} + type=semver,pattern=v{{major}}.{{minor}},value=${{ inputs.version }} + + - name: Build & Publish Container ${{ inputs.container-name }} + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 + if: ${{ steps.set-version.outputs.release == 'true' }} + id: build + with: + file: "${{ inputs.container-file }}" + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # SBOM Settings + sbom: true + + # Upload Software Bill of Materials (SBOM) to GitHub + - name: Upload SBOM + uses: advanced-security/spdx-dependency-submission-action@5530bab9ee4bbe66420ce8280624036c77f89746 # v0.1.1 + if: ${{ inputs.sbom == 'true' }} + with: + filePath: '.' + filePattern: '*.spdx.json' + + sign-image: + runs-on: ubuntu-latest + needs: publish-image + # Sign the image only if it is being published + if: ${{ inputs.signing == 'true' && inputs.publish == 'true' }} + + permissions: + # read the image from GitHub Container Registry + packages: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0 + with: + cosign-release: 'v2.4.1' + + - name: Log in to the Container registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Sign the published container + # This step uses the identity token to provision an ephemeral certificate against + # the sigstore community Fulcio instance. + run: | + cosign sign --yes \ + ${{ env.IMAGE_NAME }}@${{ needs.build-publish-image.outputs.digest }} + diff --git a/.github/workflows/container-security.yml b/.github/workflows/container-security.yml new file mode 100644 index 0000000..fb8cfe8 --- /dev/null +++ b/.github/workflows/container-security.yml @@ -0,0 +1,72 @@ +name: Container Security Scanning + +on: + workflow_call: + inputs: + version: + description: "Semantic version of the image" + type: string + + container-file: + description: "Path to the Dockerfile" + type: string + default: "Dockerfile" + + container-name: + description: "Name of the container" + type: string + default: "${{ github.repository }}" + + scanning-block: + description: "Block the build if vulnerabilities are found" + type: string + default: "false" + +env: + REGISTRY: ghcr.io + +jobs: + scan-image: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 + + - name: Log in to the Container registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Initial Container ${{ inputs.container-name }} + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 + id: build + with: + file: "${{ inputs.container-file }}" + context: . + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + # Scan the image for vulnerabilities + - name: Run the Anchore / Grype scan action + if: ${{ inputs.scanning == 'true' }} + uses: anchore/scan-action@f2ba85e044c8f5e5014c9a539328a9c78d3bfa49 # v5.2.1 + id: scan + with: + image: "${{ env.REGISTRY }}/${{ inputs.container-name }}:${{ steps.build.outputs.digest }}" + only-fixed: true + fail-build: ${{ inputs.scanning-block }} + + - name: Upload vulnerability report + if: ${{ inputs.scanning == 'true' }} + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: ${{ steps.scan.outputs.sarif }} diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 42411c4..3577e8c 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -1,4 +1,4 @@ -name: Conatiner Build and Release +name: Container Build and Release on: workflow_call: @@ -6,17 +6,21 @@ on: version: description: "Semantic version of the image" type: string - required: true container-file: description: "Path to the Dockerfile" type: string default: "Dockerfile" - + + container-name: + description: "Name of the container" + type: string + default: "${{ github.repository }}" + signing: description: "Sign the image" type: string - default: "true" + default: "false" publish: description: "Publish the image to the registry" @@ -45,132 +49,63 @@ on: env: REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} jobs: - build-publish-image: + set-version: runs-on: ubuntu-latest - outputs: - digest: ${{ steps.build.outputs.digest }} - - permissions: - # to upload SBOM - id-token: write - contents: write - # to upload Docker image - packages: write + release: ${{ steps.check_release.outputs.release }} + version: ${{ steps.set-version.outputs.version }} steps: - - name: Checkout repository + - name: "Checkout" uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to the Container registry - uses: docker/login-action@v3.2.0 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5.5.1 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - # latest / main - type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} - # SemVer - type=semver,pattern={{version}},value=${{ inputs.version }} - # SemVer, major only - type=semver,pattern=v{{major}},value=${{ inputs.version }} - - - name: Build Docker image - uses: docker/build-push-action@v6.2.0 - id: build - with: - file: "${{ inputs.container-file }}" - context: . - push: ${{ inputs.publish }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - # SBOM Settings - sbom: true - - # Upload Software Bill of Materials (SBOM) to GitHub - - name: Upload SBOM - uses: advanced-security/spdx-dependency-submission-action@v0.1.1 - if: ${{ inputs.sbom == 'true' }} - with: - filePath: '.' - filePattern: '*.spdx.json' - - scanning: - runs-on: ubuntu-latest - needs: build-publish-image - # Scan the image only if it is being published - if: ${{ inputs.scanning == 'true' && inputs.publish == 'true' }} - - permissions: - contents: read - # read the image from GitHub Container Registry - packages: read - # to scan the Docker image - security-events: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Log in to the Container registry - uses: docker/login-action@v3.2.0 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # Scan the image for vulnerabilities - - name: Run the Anchore Grype scan action - uses: anchore/scan-action@3343887d815d7b07465f6fdcd395bd66508d486a - id: scan - with: - image: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.version }}@${{ needs.build-publish-image.outputs.digest }}" - only-fixed: true - fail-build: ${{ inputs.scanning-block }} - - - name: Upload vulnerability report - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: ${{ steps.scan.outputs.sarif }} - - signing: - runs-on: ubuntu-latest - needs: build-publish-image - # Sign the image only if it is being published - if: ${{ inputs.signing == 'true' && inputs.publish == 'true' }} - - permissions: - # read the image from GitHub Container Registry - packages: read - - steps: - - uses: sigstore/cosign-installer@v3.5.0 - with: - cosign-release: 'v2.2.2' - - - name: Log in to the Container registry - uses: docker/login-action@v3.2.0 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Sign the published container - # This step uses the identity token to provision an ephemeral certificate against the sigstore community Fulcio instance. + - name: "Get and Set version" + id: set-version run: | - cosign sign --yes \ - ${{ env.IMAGE_NAME }}@${{ needs.build-publish-image.outputs.digest }} - \ No newline at end of file + set -e + + if [[ -f .release.yml ]]; then + pip install yq + current_version=$(yq -r ".version" .release.yml) + echo "Current Version :: $current_version" + + echo "version=$current_version" >> $GITHUB_OUTPUT + else + echo "Failed to find version..." + exit 1 + fi + + released_version=$(gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/:owner/:repo/releases/latest | jq -r ".tag_name") + + if [[ "$current_version" == "NA" || "$current_version" == "$released_version" ]]; then + echo "No new release found" + echo "release=false" >> "$GITHUB_OUTPUT" + else + echo "New release found" + echo "version=$current_version" >> "$GITHUB_OUTPUT" + echo "release=true" >> "$GITHUB_OUTPUT" + fi + + # Scan the container + scan-image: + uses: advanced-security/reusable-workflows/.github/workflows/container-security.yml@main + needs: set-version + secrets: inherit + with: + version: ${{ needs.set-version.outputs.version }} + container-file: ${{ inputs.container-file }} + container-name: ${{ inputs.container-name }} + scanning-block: ${{ inputs.scanning-block }} + + publish-image: + uses: advanced-security/reusable-workflows/.github/workflows/container-publish.yml@main + needs: [ scan-image, set-version ] + if: ${{ needs.set-version.outputs.release == 'true' }} + secrets: inherit + with: + version: ${{ needs.set-version.outputs.version }} + container-file: ${{ inputs.container-file }} + container-name: ${{ inputs.container-name }} + sbom: ${{ inputs.sbom }} + signing: ${{ inputs.signing }}