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