Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 62 additions & 26 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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

Expand All @@ -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' }}

Expand All @@ -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'
Expand All @@ -138,7 +151,7 @@ jobs:
edit-mode: replace
body: |
<!-- trivy-scan -->
### 🔒 Trivy Security Scan Results
### 🔒 Trivy Security ${{ inputs.platform }} Scan Results
<details><summary>Click to expand detailed results</summary>

```bash
Expand Down Expand Up @@ -196,3 +209,26 @@ jobs:
${{ steps.read_hadolint.outputs.report }}
```
</details>

- 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"
46 changes: 26 additions & 20 deletions docker-build/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -73,15 +79,15 @@ 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
- 🧪 For testing purposes, you can use the `--dry-run` flag in your own implementation

## 🛠️ 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