PIPELINE SECURITY

CI/CD Security

Securing your CI/CD pipeline - secrets management, security gates, secure deployment, and integrating security scanners into your release process.

Why is CI/CD Security Important?

Your CI/CD pipeline has access to source code, credentials, production environments, and artifact repositories. A compromised pipeline can be used to deploy malware, steal secrets, or gain access to production. Pipeline security is a critical component of your DevSecOps strategy.

DevSecOps Pipeline Architecture

Security Gates in CI/CD

1. Pre-commit

Checks before code is committed

  • Pre-commit hooks (secrets detection)
  • Linting and formatting
  • Local SAST scan

2. Build Stage

Checks during build

  • SAST - static code analysis
  • SCA - dependency analysis
  • Secret scanning (GitLeaks)
  • License compliance

3. Test Stage

Security tests

  • Unit tests with security assertions
  • Container image scanning (Trivy)
  • IaC scanning (tfsec, Checkov)

4. Deploy Stage

Pre-deployment checks

  • DAST - dynamic testing
  • Approval gates for production
  • Deployment verification

5. Post-deploy

Continuous monitoring

  • Runtime security monitoring
  • Vulnerability alerts
  • Compliance checks

Secrets Management

Proper handling of secrets (API keys, passwords, certificates) is one of the most important aspects of CI/CD security.

NEVER do this

  • Hardcoded secrets in code or pipeline configurations
  • Secrets in environment variables without encryption
  • Sharing secrets between environments (dev/prod)
  • Long-lived static access keys
  • Secrets in Docker images

Proper Secrets Handling

# GitLab CI - using protected variables variables: # Reference to secret stored in GitLab CI/CD settings AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY # Better - using OIDC (no static keys) deploy: id_tokens: GITLAB_OIDC_TOKEN: aud: https://gitlab.com script: - | export $(aws sts assume-role-with-web-identity \ --role-arn $AWS_ROLE_ARN \ --role-session-name "gitlab-$CI_JOB_ID" \ --web-identity-token $GITLAB_OIDC_TOKEN \ --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \ --output text | xargs -n1 echo) # Best - using external secrets manager deploy: before_script: - | # Retrieve secrets from AWS Secrets Manager export DB_PASSWORD=$(aws secretsmanager get-secret-value \ --secret-id prod/db/password \ --query SecretString --output text)

Example: Secure DevSecOps Pipeline

# .gitlab-ci.yml - DevSecOps Pipeline stages: - test - build - scan - deploy variables: DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA # ===== TEST STAGE ===== # Secret detection gitleaks: stage: test image: zricethezav/gitleaks:latest script: - gitleaks detect --source . --report-path gitleaks-report.json artifacts: paths: [gitleaks-report.json] when: always allow_failure: true # SAST - static analysis semgrep: stage: test image: returntocorp/semgrep script: - semgrep ci --json --output semgrep-results.json artifacts: paths: [semgrep-results.json] when: always allow_failure: true # SCA - dependency scanning dependency_check: stage: test image: owasp/dependency-check:latest script: - /usr/share/dependency-check/bin/dependency-check.sh \ --project "MyApp" \ --scan . \ --format JSON \ --out dependency-check-report.json artifacts: paths: [dependency-check-report.json] when: always allow_failure: true # ===== BUILD STAGE ===== build_image: stage: build image: docker:24 services: - docker:24-dind script: - docker build -t $DOCKER_IMAGE . - docker push $DOCKER_IMAGE only: - main # ===== SCAN STAGE ===== # Container image scanning trivy: stage: scan image: aquasec/trivy:latest script: - trivy image --exit-code 0 --severity HIGH,CRITICAL \ --format json --output trivy-results.json $DOCKER_IMAGE artifacts: paths: [trivy-results.json] when: always dependencies: - build_image # ===== DEPLOY STAGE ===== deploy_staging: stage: deploy environment: name: staging script: - echo "Deploying to staging..." only: - main deploy_production: stage: deploy environment: name: production script: - echo "Deploying to production..." when: manual # Requires manual approval only: - main dependencies: - deploy_staging

CI/CD Security Best Practices

Pipeline Configuration

  • Pipeline as Code in Git repository
  • Protected branches - no direct pushes to main
  • Required code reviews
  • Signed commits
  • Pinned versions of tools and images

Secrets & Access

  • OIDC/Workload Identity instead of static keys
  • External secrets manager (Vault, AWS Secrets Manager)
  • Secrets rotation
  • Least privilege for pipeline service accounts
  • Audit logging of all access

Security Gates

  • Fail pipeline on critical findings
  • Manual approval for production deploys
  • Security review for infrastructure changes
  • Rollback mechanisms
  • Deployment windows

Security Risks in CI/CD

  • Poisoned Pipeline Execution (PPE) - pipeline configuration manipulation
  • Dependency Confusion - package manager exploitation
  • Credential theft from pipeline logs
  • Supply chain attacks via build tools
  • Privilege escalation through self-hosted runners