406 lines
10 KiB
YAML
406 lines
10 KiB
YAML
# Grype CI/CD Pipeline Configuration Examples
|
|
#
|
|
# This file provides example configurations for integrating Grype vulnerability
|
|
# scanning into various CI/CD platforms.
|
|
|
|
# =============================================================================
|
|
# GitHub Actions
|
|
# =============================================================================
|
|
|
|
name: Container Security Scan
|
|
|
|
on:
|
|
push:
|
|
branches: [main, develop]
|
|
pull_request:
|
|
branches: [main]
|
|
schedule:
|
|
# Scan daily for new vulnerabilities
|
|
- cron: '0 6 * * *'
|
|
|
|
jobs:
|
|
grype-scan:
|
|
name: Grype Vulnerability Scan
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Build Docker image
|
|
run: |
|
|
docker build -t ${{ github.repository }}:${{ github.sha }} .
|
|
|
|
- name: Install Grype
|
|
uses: anchore/scan-action@v3
|
|
id: grype
|
|
with:
|
|
image: ${{ github.repository }}:${{ github.sha }}
|
|
fail-build: true
|
|
severity-cutoff: high
|
|
output-format: sarif
|
|
|
|
- name: Upload SARIF results to GitHub Security
|
|
uses: github/codeql-action/upload-sarif@v3
|
|
if: always()
|
|
with:
|
|
sarif_file: ${{ steps.grype.outputs.sarif }}
|
|
|
|
- name: Generate human-readable report
|
|
if: always()
|
|
run: |
|
|
grype ${{ github.repository }}:${{ github.sha }} -o table > grype-report.txt
|
|
|
|
- name: Upload scan report
|
|
uses: actions/upload-artifact@v4
|
|
if: always()
|
|
with:
|
|
name: grype-scan-report
|
|
path: grype-report.txt
|
|
retention-days: 30
|
|
|
|
# =============================================================================
|
|
# GitLab CI
|
|
# =============================================================================
|
|
|
|
# .gitlab-ci.yml
|
|
|
|
stages:
|
|
- build
|
|
- scan
|
|
- deploy
|
|
|
|
variables:
|
|
IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
|
GRYPE_VERSION: "latest"
|
|
|
|
build:
|
|
stage: build
|
|
image: docker:24-dind
|
|
services:
|
|
- docker:24-dind
|
|
script:
|
|
- docker build -t $IMAGE_NAME .
|
|
- docker push $IMAGE_NAME
|
|
only:
|
|
- branches
|
|
|
|
grype-scan:
|
|
stage: scan
|
|
image: anchore/grype:$GRYPE_VERSION
|
|
script:
|
|
- grype $IMAGE_NAME --fail-on high -o json > grype-results.json
|
|
- grype $IMAGE_NAME -o table
|
|
artifacts:
|
|
reports:
|
|
container_scanning: grype-results.json
|
|
paths:
|
|
- grype-results.json
|
|
expire_in: 30 days
|
|
allow_failure: false
|
|
only:
|
|
- branches
|
|
|
|
deploy:
|
|
stage: deploy
|
|
script:
|
|
- echo "Deploying $IMAGE_NAME"
|
|
only:
|
|
- main
|
|
when: on_success
|
|
|
|
# =============================================================================
|
|
# Jenkins Pipeline
|
|
# =============================================================================
|
|
|
|
# Jenkinsfile
|
|
|
|
pipeline {
|
|
agent any
|
|
|
|
environment {
|
|
IMAGE_NAME = "myapp"
|
|
IMAGE_TAG = "${env.BUILD_NUMBER}"
|
|
GRYPE_VERSION = "latest"
|
|
}
|
|
|
|
stages {
|
|
stage('Build') {
|
|
steps {
|
|
script {
|
|
docker.build("${IMAGE_NAME}:${IMAGE_TAG}")
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Grype Scan') {
|
|
agent {
|
|
docker {
|
|
image "anchore/grype:${GRYPE_VERSION}"
|
|
args '-v /var/run/docker.sock:/var/run/docker.sock'
|
|
}
|
|
}
|
|
steps {
|
|
sh """
|
|
# Run scan with high severity threshold
|
|
grype ${IMAGE_NAME}:${IMAGE_TAG} \
|
|
--fail-on high \
|
|
-o json > grype-results.json
|
|
|
|
# Generate human-readable report
|
|
grype ${IMAGE_NAME}:${IMAGE_TAG} \
|
|
-o table > grype-report.txt
|
|
"""
|
|
}
|
|
post {
|
|
always {
|
|
archiveArtifacts artifacts: 'grype-*.json,grype-*.txt',
|
|
allowEmptyArchive: true
|
|
}
|
|
failure {
|
|
echo 'Grype scan found vulnerabilities above threshold'
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Deploy') {
|
|
when {
|
|
branch 'main'
|
|
}
|
|
steps {
|
|
echo "Deploying ${IMAGE_NAME}:${IMAGE_TAG}"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# =============================================================================
|
|
# CircleCI
|
|
# =============================================================================
|
|
|
|
# .circleci/config.yml
|
|
|
|
version: 2.1
|
|
|
|
orbs:
|
|
docker: circleci/docker@2.2.0
|
|
|
|
jobs:
|
|
build-and-scan:
|
|
docker:
|
|
- image: cimg/base:2024.01
|
|
steps:
|
|
- checkout
|
|
- setup_remote_docker:
|
|
docker_layer_caching: true
|
|
|
|
- run:
|
|
name: Build Docker Image
|
|
command: |
|
|
docker build -t myapp:${CIRCLE_SHA1} .
|
|
|
|
- run:
|
|
name: Install Grype
|
|
command: |
|
|
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
|
|
|
|
- run:
|
|
name: Scan with Grype
|
|
command: |
|
|
grype myapp:${CIRCLE_SHA1} --fail-on critical -o json > grype-results.json
|
|
grype myapp:${CIRCLE_SHA1} -o table | tee grype-report.txt
|
|
|
|
- store_artifacts:
|
|
path: grype-results.json
|
|
destination: scan-results
|
|
|
|
- store_artifacts:
|
|
path: grype-report.txt
|
|
destination: scan-results
|
|
|
|
workflows:
|
|
build-scan-deploy:
|
|
jobs:
|
|
- build-and-scan:
|
|
filters:
|
|
branches:
|
|
only:
|
|
- main
|
|
- develop
|
|
|
|
# =============================================================================
|
|
# Azure Pipelines
|
|
# =============================================================================
|
|
|
|
# azure-pipelines.yml
|
|
|
|
trigger:
|
|
branches:
|
|
include:
|
|
- main
|
|
- develop
|
|
|
|
pool:
|
|
vmImage: 'ubuntu-latest'
|
|
|
|
variables:
|
|
imageName: 'myapp'
|
|
imageTag: '$(Build.BuildId)'
|
|
|
|
stages:
|
|
- stage: Build
|
|
jobs:
|
|
- job: BuildImage
|
|
steps:
|
|
- task: Docker@2
|
|
displayName: Build Docker image
|
|
inputs:
|
|
command: build
|
|
dockerfile: Dockerfile
|
|
tags: $(imageTag)
|
|
|
|
- stage: Scan
|
|
dependsOn: Build
|
|
jobs:
|
|
- job: GrypeScan
|
|
steps:
|
|
- script: |
|
|
# Install Grype
|
|
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
|
|
|
|
# Run scan
|
|
grype $(imageName):$(imageTag) \
|
|
--fail-on high \
|
|
-o json > $(Build.ArtifactStagingDirectory)/grype-results.json
|
|
|
|
grype $(imageName):$(imageTag) \
|
|
-o table > $(Build.ArtifactStagingDirectory)/grype-report.txt
|
|
displayName: 'Run Grype Scan'
|
|
|
|
- task: PublishBuildArtifacts@1
|
|
displayName: 'Publish Scan Results'
|
|
inputs:
|
|
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
|
ArtifactName: 'grype-scan-results'
|
|
condition: always()
|
|
|
|
- stage: Deploy
|
|
dependsOn: Scan
|
|
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
|
|
jobs:
|
|
- job: DeployProduction
|
|
steps:
|
|
- script: echo "Deploying to production"
|
|
displayName: 'Deploy'
|
|
|
|
# =============================================================================
|
|
# Tekton Pipeline
|
|
# =============================================================================
|
|
|
|
# tekton-pipeline.yaml
|
|
|
|
apiVersion: tekton.dev/v1beta1
|
|
kind: Pipeline
|
|
metadata:
|
|
name: grype-scan-pipeline
|
|
spec:
|
|
params:
|
|
- name: image-name
|
|
type: string
|
|
description: Name of the image to scan
|
|
- name: image-tag
|
|
type: string
|
|
description: Tag of the image to scan
|
|
default: latest
|
|
|
|
workspaces:
|
|
- name: shared-workspace
|
|
|
|
tasks:
|
|
- name: build-image
|
|
taskRef:
|
|
name: buildah
|
|
workspaces:
|
|
- name: source
|
|
workspace: shared-workspace
|
|
params:
|
|
- name: IMAGE
|
|
value: $(params.image-name):$(params.image-tag)
|
|
|
|
- name: grype-scan
|
|
runAfter:
|
|
- build-image
|
|
taskRef:
|
|
name: grype-scan
|
|
params:
|
|
- name: IMAGE
|
|
value: $(params.image-name):$(params.image-tag)
|
|
- name: SEVERITY_THRESHOLD
|
|
value: high
|
|
|
|
- name: deploy
|
|
runAfter:
|
|
- grype-scan
|
|
taskRef:
|
|
name: kubectl-deploy
|
|
params:
|
|
- name: IMAGE
|
|
value: $(params.image-name):$(params.image-tag)
|
|
|
|
---
|
|
|
|
apiVersion: tekton.dev/v1beta1
|
|
kind: Task
|
|
metadata:
|
|
name: grype-scan
|
|
spec:
|
|
params:
|
|
- name: IMAGE
|
|
description: Image to scan
|
|
- name: SEVERITY_THRESHOLD
|
|
description: Fail on this severity or higher
|
|
default: high
|
|
|
|
steps:
|
|
- name: scan
|
|
image: anchore/grype:latest
|
|
script: |
|
|
#!/bin/sh
|
|
grype $(params.IMAGE) \
|
|
--fail-on $(params.SEVERITY_THRESHOLD) \
|
|
-o json > /workspace/grype-results.json
|
|
|
|
grype $(params.IMAGE) -o table | tee /workspace/grype-report.txt
|
|
|
|
workspaces:
|
|
- name: scan-results
|
|
|
|
# =============================================================================
|
|
# Best Practices
|
|
# =============================================================================
|
|
|
|
# 1. Update vulnerability database regularly
|
|
# - Run grype db update before scans
|
|
# - Cache database between pipeline runs
|
|
# - Update database at least daily
|
|
|
|
# 2. Set appropriate fail thresholds
|
|
# - Production: --fail-on critical or high
|
|
# - Development: --fail-on high (may allow critical temporarily)
|
|
# - Monitor-only: No fail threshold, just report
|
|
|
|
# 3. Archive scan results
|
|
# - Store JSON for trend analysis
|
|
# - Keep reports for compliance audits
|
|
# - Retention: 30-90 days minimum
|
|
|
|
# 4. Integrate with security dashboards
|
|
# - Upload SARIF to GitHub Security
|
|
# - Send metrics to monitoring systems
|
|
# - Alert security team on critical findings
|
|
|
|
# 5. Scheduled scanning
|
|
# - Scan production images daily for new CVEs
|
|
# - Re-scan after vulnerability database updates
|
|
# - Track vulnerability trends over time
|