Skip to content

Implement Basic Trivy Scanning Workflow #12

Implement Basic Trivy Scanning Workflow

Implement Basic Trivy Scanning Workflow #12

name: Docker Security Scan
on:
push:
branches:
- main
- develop
paths:
- "docker/**"
- "templates/docker-compose/**"
- ".github/workflows/docker-security-scan.yml"
pull_request:
paths:
- "docker/**"
- "templates/docker-compose/**"
- ".github/workflows/docker-security-scan.yml"
schedule:
- cron: "0 6 * * *" # Daily at 6 AM UTC
workflow_dispatch: # Allow manual triggering
jobs:
scan-project-images:
name: Scan Project-Built Docker Images
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
strategy:
fail-fast: false
matrix:
image:
- dockerfile: docker/provisioned-instance/Dockerfile
context: docker/provisioned-instance
name: provisioned-instance
- dockerfile: docker/ssh-server/Dockerfile
context: docker/ssh-server
name: ssh-server
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t torrust-tracker-deployer/${{ matrix.image.name }}:latest \
-f ${{ matrix.image.dockerfile }} \
.
- name: Display vulnerabilities (table format)
uses: aquasecurity/[email protected]
with:
image-ref: torrust-tracker-deployer/${{ matrix.image.name }}:latest
format: "table"
severity: "HIGH,CRITICAL"
exit-code: "0" # Don't fail here, just display
- name: Run Trivy vulnerability scanner
uses: aquasecurity/[email protected]
continue-on-error: true
id: trivy-scan
with:
image-ref: torrust-tracker-deployer/${{ matrix.image.name }}:latest
format: "sarif"
output: "trivy-results-${{ matrix.image.name }}.sarif"
severity: "HIGH,CRITICAL"
exit-code: "1"
scanners: "vuln" # Only vulnerabilities, skip secrets (test containers have legitimate SSH keys)
- name: Upload SARIF artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: sarif-project-${{ matrix.image.name }}-${{ github.run_id }}
path: "trivy-results-${{ matrix.image.name }}.sarif"
retention-days: 30
scan-third-party-images:
name: Scan Third-Party Docker Images
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
strategy:
fail-fast: false
matrix:
# NOTE: These images must match the ones used in templates/docker-compose/docker-compose.yml.tera
# TODO: Automate image detection from docker-compose templates - see https://github.com/torrust/torrust-tracker-deployer/issues/252
image:
- torrust/tracker:develop
- mysql:8.0
- grafana/grafana:11.4.0
- prom/prometheus:v3.0.1
steps:
- name: Display vulnerabilities (table format)
uses: aquasecurity/[email protected]
with:
image-ref: ${{ matrix.image }}
format: "table"
severity: "HIGH,CRITICAL"
exit-code: "0" # Don't fail here, just display
- name: Run Trivy vulnerability scanner
uses: aquasecurity/[email protected]
continue-on-error: true
id: trivy-scan
with:
image-ref: ${{ matrix.image }}
format: "sarif"
output: "trivy-results.sarif"
severity: "HIGH,CRITICAL"
exit-code: "1"
scanners: "vuln" # Focus on CVEs, not secrets
- name: Sanitize image name for artifact
id: sanitize
run: echo "name=$(echo '${{ matrix.image }}' | tr '/:' '-')" >> $GITHUB_OUTPUT
- name: Upload SARIF artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: sarif-third-party-${{ steps.sanitize.outputs.name }}-${{ github.run_id }}
path: "trivy-results.sarif"
retention-days: 30
upload-sarif-results:
name: Upload SARIF Results to GitHub Security
runs-on: ubuntu-latest
needs: [scan-project-images, scan-third-party-images]
if: always()
permissions:
security-events: write
steps:
- name: Download all SARIF artifacts
uses: actions/download-artifact@v4
with:
pattern: sarif-*-${{ github.run_id }}
merge-multiple: false
- name: Upload SARIF files to GitHub Security
run: |
# Upload each SARIF file with a unique category
find . -name "*.sarif" -type f | while read -r sarif_file; do
# Extract image name from directory path for category
category=$(basename $(dirname "$sarif_file") | sed 's/^sarif-//' | sed 's/-[0-9]*$//')
echo "Uploading $sarif_file with category: docker-$category"
# Use gh CLI to upload SARIF (simpler than action in loop)
cat "$sarif_file" | gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${{ github.repository }}/code-scanning/sarifs \
-f sarif=@- \
-f ref="${{ github.ref }}" \
-f commit_sha="${{ github.sha }}" \
-f checkout_uri="${{ github.server_url }}/${{ github.repository }}" \
-f category="docker-$category" || echo "Failed to upload $sarif_file"
done
env:
GH_TOKEN: ${{ github.token }}