diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 493805d..2754a7d 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -43,9 +43,9 @@ on: description: "Enable Hadolint" default: true type: boolean - platforms: - description: "Build platforms" - default: "linux/amd64,linux/arm64" + platform: + description: "Build platform" + default: "linux/amd64" type: string secrets: username: @@ -56,7 +56,35 @@ on: jobs: build: runs-on: ubuntu-latest + + # avoid shell injection through string interpolation + env: + PLATFORM: ${{ inputs.platform }} + OCI_IMAGE: ${{ inputs.image-name }}:${{ inputs.image-tag }} + REPORT_MODE: ${{ inputs.security-report }} + steps: + - name: Validate platform input + run: | + if [ "$PLATFORM" != "linux/amd64" ] && [ "$PLATFORM" != "linux/arm64" ] ; then + echo "❌ Error: Invalid platform: $PLATFORM" + echo "Valid platforms are 'linux/amd64' or 'linux/arm64'" + exit 1 + fi + + echo "✅ Platform validation passed: $PLATFORM" + + - name: Validate security report mode input + if: ${{ inputs.security-scan }} + run: | + if [ "$REPORT_MODE" != "comment" ] && [ "$REPORT_MODE" != "sarif" ] ; then + echo "❌ Error: Invalid security report mode: $REPORT_MODE" + echo "Valid modes are 'comment' or 'sarif'" + exit 1 + fi + + echo "✅ Security report mode validation passed: $REPORT_MODE" + - name: Checkout Repository uses: actions/checkout@v4 @@ -74,40 +102,25 @@ jobs: username: ${{ secrets.username }} password: ${{ secrets.password }} - - name: Build Docker Image + - name: Build Docker image uses: docker/build-push-action@v6 with: build-args: ${{ inputs.build-args }} context: ${{ inputs.context }} file: ${{ inputs.dockerfile }} - platforms: ${{ inputs.platforms }} - push: ${{ inputs.push }} + platforms: ${{ inputs.platform }} + load: true # Make the image available on runner + push: false # Don't push yet, wait for security checks tags: ${{ inputs.image-name }}:${{ inputs.image-tag }} - - name: Build Docker Image as Tarball - if: ${{ inputs.security-scan }} - run: | - BUILD_ARGS="" - if [ -n "${{ inputs.build-args }}" ]; then - while IFS= read -r line; do - if [ -n "$line" ]; then - BUILD_ARGS="$BUILD_ARGS --build-arg $line" - fi - done <<< "${{ inputs.build-args }}" - fi - docker build $BUILD_ARGS -t ${{ inputs.image-name }}:${{ inputs.image-tag }} -f ${{ inputs.dockerfile }} ${{ inputs.context }} - docker save -o vuln-image.tar ${{ inputs.image-name }}:${{ inputs.image-tag }} - - name: Run Trivy vulnerability scanner id: trivy if: ${{ inputs.security-scan }} - uses: aquasecurity/trivy-action@0.29.0 + uses: aquasecurity/trivy-action@0.33.1 with: - input: vuln-image.tar + image-ref: ${{ inputs.image-name }}:${{ inputs.image-tag }} format: ${{ (inputs.security-report == 'sarif' && 'sarif') || 'table' }} - ignore-unfixed: true vuln-type: "os,library" - severity: "CRITICAL,HIGH" hide-progress: true output: ${{ (inputs.security-report == 'sarif' && 'trivy-results.sarif') || 'trivy.txt' }} @@ -126,7 +139,7 @@ jobs: with: issue-number: ${{ github.event.pull_request.number }} comment-author: "github-actions[bot]" - body-includes: "Trivy Security Scan Results" + body-includes: "Trivy Security ${{ inputs.platform }} Scan Results" - name: Create or update Trivy comment if: github.event_name == 'pull_request' && inputs.security-scan && inputs.security-report == 'comment' @@ -138,7 +151,7 @@ jobs: edit-mode: replace body: | - ### 🔒 Trivy Security Scan Results + ### 🔒 Trivy Security ${{ inputs.platform }} Scan Results
Click to expand detailed results ```bash @@ -196,3 +209,26 @@ jobs: ${{ steps.read_hadolint.outputs.report }} ```
+ + - name: Fail build on CRITICAL or HIGH vulnerabilities + if: ${{ inputs.security-scan }} + uses: aquasecurity/trivy-action@0.33.1 + with: + image-ref: ${{ inputs.image-name }}:${{ inputs.image-tag }} + format: table + ignore-unfixed: true + vuln-type: "os,library" + severity: "CRITICAL,HIGH" + hide-progress: true + skip-setup-trivy: true + exit-code: 1 + + - name: Push Docker image + if: ${{ inputs.push }} + run: docker push "$OCI_IMAGE" + + - name: Cleanup files + if: always() + run: | + rm -f trivy.txt trivy-results.sarif + docker image rm -f "$OCI_IMAGE" diff --git a/docker-build/README.md b/docker-build/README.md index 4605f3f..8d47eb6 100644 --- a/docker-build/README.md +++ b/docker-build/README.md @@ -2,34 +2,40 @@ ## 🔍 Overview -This reusable GitHub Actions workflow automates the process of building and pushing Docker images to Docker Hub. It simplifies the Docker build process in your CI/CD pipeline by handling authentication, building, and tagging in a standardized way. Perfect for teams looking to streamline their containerization workflow with minimal configuration. +This reusable GitHub Actions workflow automates the process of building and pushing Docker images to a Docker Registry. +It simplifies the Docker build process in your CI/CD pipeline by handling authentication, building, and tagging in a standardized way. +Perfect for teams looking to streamline their containerization workflow with minimal configuration. ## ✨ Features -- 🔐 Securely authenticates with Docker Hub using best practices +- 🔐 Securely authenticates with a Docker Registry using best practices - 🏗️ Builds optimized Docker images from a specified Dockerfile -- 🏷️ Intelligently tags and pushes images to Docker Hub -- 🔎 Scan for vulnerabilities +- 🏷️ Intelligently tags and pushes images to a Docker Registry +- 🔎 Scan for vulnerabilities with Trivy - 👍 Lint dockerfile - 🛡️ Handles authentication securely using GitHub Secrets - 🚀 Optimizes build performance with layer caching -- 📦 Supports multi-platform builds (AMD64, ARM64) +- 📦 Supports AMD64 and ARM64 platforms (one per workflow run) + +> [!IMPORTANT] +> Due to a limitation on Trivy analysis, the workflow targets a single platform. +> A workflow instance should be configured for each intended targeted platform. ## ⚙️ Inputs -| Name | Description | Required | Default | -| ----------------- | ---------------------------------------------------------------------------------- | -------- | --------------------------- | -| `build-args` | Docker build arguments (multiline format: `KEY1=value1\nKEY2=value2`) | No | `""` | -| `context` | Path to Docker Build Context | No | `"."` | -| `dockerfile` | Path to the Dockerfile to build (e.g., './Dockerfile', './docker/Dockerfile') | No | `"Dockerfile"` | -| `hadolint` | Enable Hadolint | No | `true` | -| `image-name` | Name of Docker Image (e.g., 'myimage', 'myorg/myimage') | true | - | -| `image-tag` | Tag to apply to the built image (e.g., 'latest', 'v1.2.3') | No | `"latest"` | -| `platforms` | Indicates which platforms the image should be built for | No | `"linux/amd64,linux/arm64"` | -| `push` | Push Docker Image to Registry | No | `false` | -| `registry` | Docker Registry | No | `"docker.io"` | -| `security-report` | Security Report Mode (`"sarif"` \| `"comment"`; ignored if `security-scan: false`) | No | `"sarif"` | -| `security-scan` | Enable Trivy Security Scan | No | `true` | +| Name | Description | Required | Default | +| ----------------- | ---------------------------------------------------------------------------------- | -------- | --------------- | +| `build-args` | Docker build arguments (multiline format: `KEY1=value1\nKEY2=value2`) | No | `""` | +| `context` | Path to Docker Build Context | No | `"."` | +| `dockerfile` | Path to the Dockerfile to build (e.g., './Dockerfile', './docker/Dockerfile') | No | `"Dockerfile"` | +| `hadolint` | Enable Hadolint | No | `true` | +| `image-name` | Name of Docker Image (e.g., 'myimage', 'myorg/myimage') | true | - | +| `image-tag` | Tag to apply to the built image (e.g., 'latest', 'v1.2.3') | No | `"latest"` | +| `platform` | Indicates which platform the image should be built for | No | `"linux/amd64"` | +| `push` | Push Docker Image to Registry | No | `false` | +| `registry` | Docker Registry | No | `"docker.io"` | +| `security-report` | Security Report Mode (`"sarif"` \| `"comment"`; ignored if `security-scan: false`) | No | `"sarif"` | +| `security-scan` | Enable Trivy Security Scan | No | `true` | ## 🔐 Secrets @@ -73,7 +79,7 @@ jobs: ## 📝 Notes -- 🔒 Ensure your Docker Hub credentials are stored securely as GitHub Secrets +- 🔒 Ensure your Docker Registry credentials are stored securely as GitHub Secrets - 🔄 The workflow will automatically handle the Docker build and push process - 🏷️ You can specify any valid Docker tag format in the `tag` input - 📅 Consider using dynamic tags based on git tags, commit SHAs, or dates @@ -81,7 +87,7 @@ jobs: ## 🛠️ Troubleshooting -- If you encounter authentication issues, verify your Docker Hub credentials are correct and have appropriate permissions +- If you encounter authentication issues, verify your Docker Registry credentials are correct and have appropriate permissions - For build failures, check your Dockerfile syntax and ensure all referenced files exist - Large images may take longer to push - consider optimizing your Dockerfile with multi-stage builds - If you need to debug the build process, you can add the `ACTIONS_STEP_DEBUG` secret set to `true` in your repository