DevSecOps & CI/CD Security
DevSecOps & BαΊ£o mαΊt Pipeline CI/CD
DevSecOps: Security as Code
DevSecOps integrates security into every stage of the DevOps pipeline β making security checks automatic, fast, and developer-friendly. The goal: make the secure path the easy path. If developers have to think about security, they will sometimes forget it. If security is automated in the pipeline, it is always enforced.
Write code, create PRs, run unit tests β security checks happen automatically via IDE plugins and pre-commit hooks
Define security policies as code β SAST rules, SCA policies, container scanning thresholds, secret detection patterns
Enforce gates in CI/CD pipeline β ArgoCD only deploys approved Git SHAs; Trivy blocks Critical CVEs in production
CI/CD Security Pipeline β Platform C (Go 1.23 + GitHub Actions)
| Stage | Tool | What it Catches | Gate? |
|---|---|---|---|
| Pre-commit | TruffleHog / Gitleaks |
Secrets in code β API keys, passwords, private keys committed to Git | Block commit |
| Build (SAST) | gosec + Semgrep (OWASP rules) |
Insecure Go patterns β SQL injection, weak crypto, hardcoded secrets, OWASP Top 10 patterns | Block PR merge |
| Test (SCA) | govulncheck + Dependabot |
CVEs in Go module dependencies β known vulnerabilities in third-party packages | Block PR (Critical) |
| Container (SCA) | Trivy |
CVEs in Docker base image packages, OS-level vulnerabilities, misconfigurations | Block deploy (Critical) |
| Container Signing | cosign |
Unsigned images β ensures image was built by trusted CI, not modified after build | Block deploy |
| Staging (DAST) | OWASP ZAP |
Runtime vulnerabilities β auth bypass, injection, insecure headers, SSL/TLS issues | Alert (review) |
| Deploy | ArgoCD |
Ensures ONLY Git-approved SHA is deployed β no manual Docker push to production | Enforced (GitOps) |
Security Gate Philosophy
A gate that warns but doesn't block is not a security control β it's a notification. Every Critical/High finding must exit with code 1 to prevent PR merge or deployment. If developers can bypass the warning and ship anyway, the gate provides no security guarantee.
Platform C GitHub Actions Security Pipeline (Actual YAML)
name: Security Gates on: [push, pull_request] jobs: sast: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: {fetch-depth: 0} # Full history for TruffleHog - run: go install github.com/securego/gosec/v2/cmd/gosec@latest && gosec ./... - uses: returntocorp/semgrep-action@v1 with: {config: 'p/owasp-top-ten p/golang'} sca: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: go install golang.org/x/vuln/cmd/govulncheck@latest && govulncheck ./... secrets: runs-on: ubuntu-latest steps: - uses: trufflesecurity/trufflehog@main with: {path: ./, base: main, head: HEAD} container: runs-on: ubuntu-latest steps: - uses: aquasecurity/trivy-action@master with: image-ref: 'aula:${{github.sha}}' severity: 'CRITICAL,HIGH' exit-code: '1' # β BLOCK if Critical/High found
If any job exits with code 1, the PR cannot be merged. Branch protection rules enforce required status checks.
Container Image Security Best Practices
AVOID β Mutable tag
FROM golang:1.23-alpine
Tag 1.23-alpine can be changed by the registry owner β supply chain risk
USE β Immutable digest
FROM golang:1.23@sha256:abc123...
SHA-256 digest is cryptographically bound to exact image content β cannot be changed
Run as non-root
USER nonroot:nonroot
Container compromise β attacker only has nonroot privileges, not host root
Container Security Checklist
- β Use distroless or alpine base image (minimal attack surface)
- β Pin base image by digest (SHA-256), not tag
- β Run as non-root user (
USER nonroot) - β No secrets baked into image layers (use Vault Agent)
- β Sign image with cosign after build
- β Scan with Trivy before push to registry
- β Set read-only root filesystem in K8s manifest
Secret Scanning & IaC Security
Why TruffleHog Scans History
A secret committed and then deleted is still in Git history β every commit hash is permanent. TruffleHog scans the entire commit history, not just the current HEAD. If a secret is found in history, it must be considered compromised and rotated immediately, then removed using git-filter-repo.
Secret found in history = ROTATE IMMEDIATELY
Even if the commit was made 2 years ago and deleted next commit, the secret is in the Git object store and visible to anyone with repo access (including historical clones).
IaC Security Scanning
Infrastructure as Code (Terraform, Kubernetes manifests, Helm charts) can contain security misconfigurations. Scan with Checkov or kube-bench before apply.
- βΈPrivileged containers (
privileged: true) - βΈPermissive RBAC (ClusterAdmin role for app pods)
- βΈNo resource limits (enables DoS via resource exhaustion)
- βΈExposed ports in service definitions
- βΈSecrets in environment variables (use Vault + K8s secrets)
Key Terms
Integration of security into DevOps practices β security automated in every CI/CD stage
Static Application Security Testing that blocks PR merge on Critical/High findings (exit code 1)
Software Composition Analysis β scans third-party dependencies for known CVEs; blocks on Critical
Signs container images with cryptographic signatures β ensures image integrity and provenance from trusted CI
Using SHA-256 hash instead of tag β digest is immutable, tag can be overwritten by registry owner
Scanning Terraform, K8s manifests, Helm charts for misconfigurations before infrastructure is provisioned
- 1. Security gates should BLOCK (exit code 1) β a gate that warns but doesn't prevent deployment is a notification, not a control. CISSP may phrase this as "which is a preventive control?" β only blocking gates qualify.
- 2. TruffleHog scans Git HISTORY β even deleted secrets can be found and must be rotated. Never think "it's safe because I deleted the commit."
- 3. Container images: pin by digest (immutable, cryptographically bound) not tag (mutable β can be changed by registry). This is a supply chain security principle.
- 4. IaC security: scan Terraform/K8s manifests for misconfigurations before apply β the configuration is infrastructure; misconfigurations are vulnerabilities.
- 5. DevSecOps goal: make the secure path the easy path β automate so developers get fast feedback and don't have to manually think about each security check.
Current state assessment: Platform C has GitHub Actions + ArgoCD. Verify which security jobs have exit-code: '1' configured β advisory-only jobs are not security controls.
Priority 1 β Secret scanning: Enable TruffleHog with fetch-depth: 0 (full history). Run once manually first β any findings in historical commits require immediate secret rotation, even if the committer thought they deleted it.
Priority 2 β govulncheck vs Dependabot: govulncheck is Go-native and checks for vulnerabilities in the Go module graph that are actually reachable β fewer false positives than Dependabot alone. Both should run.
Priority 3 β Trivy with exit-code 1: Confirm that Critical CVEs in base images block deployment, not just generate reports. Check current Trivy action configuration in all Platform C service repos.
Action this week: audit all GitHub Actions workflows β confirm every security job has exit-code: '1' or equivalent blocking behavior.
Practice Questions
Q1: A security scanning job in GitHub Actions outputs a warning about a Critical CVE but the PR can still be merged. Is this a security control?
A: No β a gate that warns but does not block is a notification, not a preventive security control. Settingexit-code: '1' on Critical findings converts it from advisory to a true blocking gate.
Q2: A developer committed an API key to Git by mistake, realized it, and deleted it in the next commit. TruffleHog finds it. Does the secret need to be rotated?
A: Yes β immediately. Git history is permanent. The secret exists in the repository's object store and is visible to anyone with read access to the repo, including historical clones. The only secure response is to rotate the secret, then optionally rewrite history with git-filter-repo.Q3: A Dockerfile uses FROM golang:1.23-alpine. What security risk does this tag present compared to a digest pin?
FROM golang:1.23@sha256:abc...) uses a cryptographic hash that is immutable β if the image content changes, the hash changes, causing the build to fail with a mismatch error.
Q4: What does IaC security scanning check in Kubernetes deployment manifests?
A: Privileged containers, permissive RBAC (ClusterAdmin for app pods), missing resource limits, exposed ports, secrets in environment variables, missing security contexts (runAsNonRoot, readOnlyRootFilesystem, drop capabilities).privileged: true) is as dangerous as vulnerable application code. Tools like Checkov, kube-bench, and Polaris scan these automatically in CI.Q5: What is the primary goal of DevSecOps shift-left in a CI/CD pipeline?
A: To provide developers with fast, automated security feedback as early as possible in the development process β ideally in the IDE (real-time) and pre-commit (before push) β so vulnerabilities are fixed at minimal cost before reaching production.