DevSecOpsGitHub ActionsSecurity Jun 2024 10 min read

Building a DevSecOps Pipeline: Security That Doesn't Slow Teams Down

How to integrate SAST, SCA, container scanning, and secret detection into your CI/CD pipeline in a way that actually gets used — not bypassed.

Security belongs in the pipeline, not after it

The traditional security model — developers build, security team reviews before release — doesn't scale. By the time security finds an issue, the code has been through code review, QA, and staging. Fixing it means unwinding all of that. DevSecOps moves security checks left into the pipeline, where a failed SAST scan is as routine as a failed unit test. Here's how to build a pipeline that's secure by default, based on what I've deployed across regulated production environments.

The four security layers in a pipeline

Complete GitHub Actions DevSecOps pipeline

name: DevSecOps Pipeline

on:
  pull_request:
    branches: [main]

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:

    - uses: actions/checkout@v4
      with:
        fetch-depth: 0   # full history for secret detection

    # Secret detection — runs first, fast fail
    - name: Scan for secrets
      uses: gitleaks/gitleaks-action@v2
      env:
        GITHUB_TOKEN: ${{{{ secrets.GITHUB_TOKEN }}}}

    # SAST with SonarQube
    - name: SonarQube scan
      uses: SonarSource/sonarqube-scan-action@master
      env:
        SONAR_TOKEN: ${{{{ secrets.SONAR_TOKEN }}}}
        SONAR_HOST_URL: ${{{{ secrets.SONAR_HOST_URL }}}}

    # Container image scan with Trivy
    - name: Build image
      run: docker build -t app:${{{{ github.sha }}}} .

    - name: Trivy vulnerability scan
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: app:${{{{ github.sha }}}}
        format: sarif
        output: trivy-results.sarif
        severity: CRITICAL,HIGH
        exit-code: '1'   # fail on CRITICAL or HIGH

    - name: Upload Trivy results to GitHub Security
      uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: trivy-results.sarif

SonarQube quality gates

A SonarQube quality gate is a pass/fail threshold that blocks deployment if code doesn't meet your security and quality standards. The gate I configure for regulated environments:

# sonar-project.properties
sonar.projectKey=your-project
sonar.qualitygate.wait=true   # block pipeline until gate completes

# Quality gate conditions (set in SonarQube UI)
# Security Rating: A (no vulnerabilities)
# Reliability Rating: A (no bugs)
# Coverage on new code: >= 80%
# Duplicated lines on new code: <= 3%

IAM least-privilege enforcement

Container security doesn't stop at the image. Your pods should run with minimal AWS permissions, enforced via IRSA (IAM Roles for Service Accounts):

# Terraform: create IAM role for a specific service account
resource "aws_iam_role" "app_role" {
  name = "app-service-role"

  assume_role_policy = jsonencode({{
    Statement = [{{
      Effect = "Allow"
      Principal = {{
        Federated = aws_iam_openid_connect_provider.eks.arn
      }}
      Action = "sts:AssumeRoleWithWebIdentity"
      Condition = {{
        StringEquals = {{
          "${{oidc_provider}}:sub" = "system:serviceaccount:production:app-service"
        }}
      }}
    }}]
  }})
}
The principle: Each microservice gets its own IAM role with only the permissions it actually needs. An API service that reads from one S3 bucket should not have s3:* on all buckets. This limits blast radius when a service is compromised.

Handling security findings without blocking every PR

The biggest DevSecOps mistake is configuring security tools to block every finding immediately. Engineers start ignoring the pipeline, adding bypass flags, or working around it. The approach that actually works:

  1. CRITICAL severity — hard block. PR cannot merge. No exceptions.
  2. HIGH severity — soft block with a 7-day SLA. Creates a ticket automatically, PR merges but issue is tracked.
  3. MEDIUM and below — informational. Reported in the PR comment, not a gate.

This keeps the pipeline moving while ensuring critical issues are never deployed. Teams stop fighting the security tooling and start treating it as part of normal development flow — which is the whole point.

← Back to all articles