From 03325f57841956f7d829058e374161199c4e7496 Mon Sep 17 00:00:00 2001 From: Greg Hendrickson Date: Sun, 8 Feb 2026 18:02:02 +0000 Subject: [PATCH] feat(docker): add Docker CI/CD reusable workflow - OIDC authentication to GHCR (keyless, no secrets required) - Multi-platform builds (linux/amd64, linux/arm64) - SBOM generation and attestation - Build provenance attestation - Trivy vulnerability scanning with SARIF upload - GitHub Actions cache for layer reuse - Semantic version tagging from git refs - All actions pinned to SHA for supply chain security Follows 2026 GitHub Actions security best practices: - Pin dependencies by SHA - Generate software attestations - Scan for vulnerabilities before push --- .github/workflows/docker-ci.yml | 206 ++++++++++++++++++++++++++++++++ README.md | 75 ++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 .github/workflows/docker-ci.yml diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml new file mode 100644 index 0000000..ba4d7aa --- /dev/null +++ b/.github/workflows/docker-ci.yml @@ -0,0 +1,206 @@ +# Docker CI/CD Reusable Workflow +# Builds, scans, and pushes container images with attestations +# +# Features: +# - OIDC authentication (no secrets required for GHCR) +# - Multi-platform builds (linux/amd64, linux/arm64) +# - SBOM generation and attestation +# - Trivy vulnerability scanning +# - Build provenance attestation +# - Smart caching with GitHub Actions cache +# +# Usage: +# jobs: +# docker: +# uses: ghndrx/github-actions-library/.github/workflows/docker-ci.yml@main +# with: +# image-name: my-app +# push: ${{ github.event_name != 'pull_request' }} +# permissions: +# contents: read +# packages: write +# id-token: write +# attestations: write + +name: Docker CI + +on: + workflow_call: + inputs: + image-name: + description: 'Image name (without registry prefix)' + required: true + type: string + context: + description: 'Docker build context path' + required: false + type: string + default: '.' + dockerfile: + description: 'Path to Dockerfile' + required: false + type: string + default: 'Dockerfile' + push: + description: 'Push image to registry' + required: false + type: boolean + default: false + platforms: + description: 'Target platforms (comma-separated)' + required: false + type: string + default: 'linux/amd64,linux/arm64' + build-args: + description: 'Build arguments (newline-separated KEY=value)' + required: false + type: string + default: '' + target: + description: 'Docker build target stage' + required: false + type: string + default: '' + scan-severity: + description: 'Trivy scan severity threshold (CRITICAL,HIGH,MEDIUM,LOW)' + required: false + type: string + default: 'CRITICAL,HIGH' + fail-on-vuln: + description: 'Fail workflow if vulnerabilities found' + required: false + type: boolean + default: false + generate-sbom: + description: 'Generate SBOM attestation' + required: false + type: boolean + default: true + generate-provenance: + description: 'Generate build provenance attestation' + required: false + type: boolean + default: true + outputs: + image-digest: + description: 'Image digest (sha256:...)' + value: ${{ jobs.build.outputs.digest }} + image-tags: + description: 'Generated image tags (JSON array)' + value: ${{ jobs.build.outputs.tags }} + sbom-attestation-id: + description: 'SBOM attestation bundle ID' + value: ${{ jobs.build.outputs.sbom-id }} + +jobs: + build: + name: Build & Push + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + attestations: write + outputs: + digest: ${{ steps.build.outputs.digest }} + tags: ${{ steps.meta.outputs.tags }} + sbom-id: ${{ steps.sbom-attest.outputs.bundle-id }} + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 + + - name: Log in to GitHub Container Registry + if: inputs.push + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 + with: + images: ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }} + tags: | + # Branch name + type=ref,event=branch + # PR number + type=ref,event=pr + # Semantic version tags + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }} + # Short SHA + type=sha,prefix=sha-,format=short + # Latest on default branch + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + + - name: Build and push + id: build + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 + with: + context: ${{ inputs.context }} + file: ${{ inputs.context }}/${{ inputs.dockerfile }} + platforms: ${{ inputs.platforms }} + push: ${{ inputs.push }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: ${{ inputs.build-args }} + target: ${{ inputs.target }} + cache-from: type=gha + cache-to: type=gha,mode=max + provenance: ${{ inputs.generate-provenance }} + sbom: ${{ inputs.generate-sbom }} + + - name: Generate SBOM attestation + if: inputs.push && inputs.generate-sbom + id: sbom-attest + uses: actions/attest-sbom@115c3be05ff3974bcbd596578934b3f9ce39bf68 # v2.1.0 + with: + subject-name: ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }} + subject-digest: ${{ steps.build.outputs.digest }} + sbom-path: ${{ runner.temp }}/sbom.spdx.json + continue-on-error: true + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # v0.29.0 + with: + image-ref: ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }}@${{ steps.build.outputs.digest }} + format: 'sarif' + output: 'trivy-results.sarif' + severity: ${{ inputs.scan-severity }} + exit-code: ${{ inputs.fail-on-vuln && '1' || '0' }} + if: inputs.push + + - name: Upload Trivy scan results + uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 + with: + sarif_file: 'trivy-results.sarif' + if: inputs.push && always() + + - name: Output summary + if: always() + run: | + echo "## 🐳 Docker Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY + echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Image | \`ghcr.io/${{ github.repository_owner }}/${{ inputs.image-name }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Digest | \`${{ steps.build.outputs.digest }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Platforms | ${{ inputs.platforms }} |" >> $GITHUB_STEP_SUMMARY + echo "| Pushed | ${{ inputs.push }} |" >> $GITHUB_STEP_SUMMARY + echo "| Provenance | ${{ inputs.generate-provenance }} |" >> $GITHUB_STEP_SUMMARY + echo "| SBOM | ${{ inputs.generate-sbom }} |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Tags" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY diff --git a/README.md b/README.md index 90c5df7..324cbcb 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Reusable GitHub Actions workflows and composite actions for CI/CD pipelines. | Workflow | Description | |----------|-------------| | [`python-ci.yml`](.github/workflows/python-ci.yml) | Python CI with UV (lint, type-check, test, security) | +| [`docker-ci.yml`](.github/workflows/docker-ci.yml) | Docker CI/CD with OIDC, attestations, and security scanning | ## Composite Actions @@ -106,6 +107,80 @@ pythonVersion = "3.12" typeCheckingMode = "standard" ``` +## Docker CI Workflow Features + +The `docker-ci.yml` reusable workflow provides production-ready container builds: + +- **OIDC authentication** - Keyless auth to GHCR (no secrets needed) +- **Multi-platform builds** - linux/amd64 + linux/arm64 by default +- **SBOM generation** - Software Bill of Materials attestation +- **Build provenance** - Cryptographic proof of build origin +- **Trivy scanning** - Vulnerability detection with SARIF upload +- **Smart caching** - GitHub Actions cache for layer reuse +- **Semantic tagging** - Auto-tags from git refs and versions + +### Quick Start + +```yaml +# .github/workflows/docker.yml +name: Docker + +on: + push: + branches: [main] + tags: ['v*'] + pull_request: + +jobs: + build: + uses: ghndrx/github-actions-library/.github/workflows/docker-ci.yml@main + with: + image-name: my-app + push: ${{ github.event_name != 'pull_request' }} + permissions: + contents: read + packages: write + id-token: write + attestations: write +``` + +### Inputs + +| Input | Type | Default | Description | +|-------|------|---------|-------------| +| `image-name` | string | *required* | Image name (without registry) | +| `context` | string | `.` | Docker build context path | +| `dockerfile` | string | `Dockerfile` | Dockerfile path relative to context | +| `push` | boolean | `false` | Push image to GHCR | +| `platforms` | string | `linux/amd64,linux/arm64` | Target platforms | +| `build-args` | string | `''` | Build args (newline-separated) | +| `target` | string | `''` | Multi-stage build target | +| `scan-severity` | string | `CRITICAL,HIGH` | Trivy severity threshold | +| `fail-on-vuln` | boolean | `false` | Fail on vulnerabilities | +| `generate-sbom` | boolean | `true` | Generate SBOM attestation | +| `generate-provenance` | boolean | `true` | Generate provenance attestation | + +### Outputs + +| Output | Description | +|--------|-------------| +| `image-digest` | Image digest (sha256:...) | +| `image-tags` | Generated tags (JSON array) | +| `sbom-attestation-id` | SBOM attestation bundle ID | + +### Security Features + +All actions are **pinned to SHA** for supply chain security: + +```yaml +uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 +``` + +Images pushed to GHCR include: +- **SBOM attestation** - Full dependency manifest +- **Build provenance** - Verifiable build metadata +- **Vulnerability scan results** - Uploaded as SARIF to Security tab + ## License MIT