GitLab CI Integration
GitLab CI Integration
GitLab provides robust CI/CD capabilities with built-in security features. Here's a comprehensive pipeline configuration:
# .gitlab-ci.yml
stages:
- build
- test
- scan
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
TRIVY_NO_PROGRESS: "true"
TRIVY_CACHE_DIR: ".trivycache/"
.docker_login: &docker_login
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin
build:
stage: build
image: docker:24.0
services:
- docker:24.0-dind
before_script:
- *docker_login
script:
# Build with security best practices
- docker build
--build-arg BUILDKIT_INLINE_CACHE=1
--cache-from $CI_REGISTRY_IMAGE:latest
--tag $IMAGE_TAG
--tag $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
--file Dockerfile
.
# Save image for subsequent stages
- docker save $IMAGE_TAG -o image.tar
artifacts:
paths:
- image.tar
expire_in: 1 hour
container_scanning_trivy:
stage: scan
image: aquasec/trivy:latest
needs: ["build"]
cache:
paths:
- .trivycache/
script:
# Load the built image
- docker load -i image.tar
# Run Trivy scan with different outputs
- trivy image --format table --severity HIGH,CRITICAL $IMAGE_TAG
# Generate JSON report for parsing
- trivy image --format json --output trivy-report.json $IMAGE_TAG
# Generate GitLab-compatible report
- trivy image --format gitlab --output gl-container-scanning-report.json $IMAGE_TAG
# Check for specific vulnerability thresholds
- |
CRITICAL_COUNT=$(cat trivy-report.json | jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="CRITICAL")] | length')
HIGH_COUNT=$(cat trivy-report.json | jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="HIGH")] | length')
echo "Found $CRITICAL_COUNT critical and $HIGH_COUNT high vulnerabilities"
if [ "$CRITICAL_COUNT" -gt "0" ]; then
echo "Pipeline failed due to critical vulnerabilities"
exit 1
fi
if [ "$HIGH_COUNT" -gt "10" ]; then
echo "Pipeline failed due to too many high vulnerabilities"
exit 1
fi
artifacts:
reports:
container_scanning: gl-container-scanning-report.json
paths:
- trivy-report.json
expire_in: 1 week
container_scanning_snyk:
stage: scan
image: snyk/snyk:docker
needs: ["build"]
before_script:
- *docker_login
script:
# Load image
- docker load -i image.tar
# Authenticate with Snyk
- snyk auth $SNYK_TOKEN
# Run Snyk test
- snyk container test $IMAGE_TAG --severity-threshold=high --json > snyk-report.json || true
# Monitor image (send to Snyk dashboard)
- snyk container monitor $IMAGE_TAG --project-name="$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME"
# Parse results for GitLab
- |
cat snyk-report.json | jq -r '
.vulnerabilities[] |
"[\(.severity | ascii_upcase)] \(.packageName)@\(.version) - \(.title)"
'
artifacts:
paths:
- snyk-report.json
expire_in: 1 week
license_scanning:
stage: scan
image: aquasec/trivy:latest
needs: ["build"]
script:
- docker load -i image.tar
- trivy image --license-full --format json --output licenses.json $IMAGE_TAG
- |
# Check for problematic licenses
COPYLEFT=$(cat licenses.json | jq '[.Results[]?.Licenses[]? | select(.Category=="Copyleft")] | length')
if [ "$COPYLEFT" -gt "0" ]; then
echo "⚠️ Found $COPYLEFT copyleft licenses"
cat licenses.json | jq '.Results[]?.Licenses[]? | select(.Category=="Copyleft")'
fi
security_report:
stage: scan
image: alpine:latest
needs: ["container_scanning_trivy", "container_scanning_snyk"]
before_script:
- apk add --no-cache jq
script:
- |
# Combine reports
echo "# Security Scan Summary" > security-summary.md
echo "**Image:** $IMAGE_TAG" >> security-summary.md
echo "**Date:** $(date)" >> security-summary.md
echo "" >> security-summary.md
# Trivy summary
echo "## Trivy Results" >> security-summary.md
cat trivy-report.json | jq -r '
.Results[] |
"- **\(.Target)**: \(.Vulnerabilities | length) vulnerabilities"
' >> security-summary.md
# Snyk summary
echo "## Snyk Results" >> security-summary.md
cat snyk-report.json | jq -r '
"- Total vulnerabilities: \(.vulnerabilities | length)"
' >> security-summary.md
artifacts:
paths:
- security-summary.md
expose_as: 'Security Report'
push_secure_image:
stage: deploy
image: docker:24.0
services:
- docker:24.0-dind
needs: ["container_scanning_trivy", "container_scanning_snyk"]
before_script:
- *docker_login
script:
- docker load -i image.tar
- docker push $IMAGE_TAG
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
# Tag as latest only if on main branch
- |
if [ "$CI_COMMIT_BRANCH" == "main" ]; then
docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
docker push $CI_REGISTRY_IMAGE:latest
fi
only:
- main
- develop