What is DevSecOps?
DevSecOps integrates security practices into every phase of the software development lifecycle — from code commit through build, test, deploy, and runtime. Instead of security being a final gate before production (the traditional "throw it over the wall" approach), DevSecOps makes security a shared responsibility embedded in every pipeline stage.
Think of it like building safety into a car's design from day one — crumple zones, airbags, ABS — rather than adding crash padding as an afterthought. The car is safer because safety was part of the engineering process, not bolted on at the end.
The Shift-Left Philosophy
flowchart LR
Code["Code
SAST, Linting"] --> Build["Build
Dependency Scan"]
Build --> Test["Test
DAST, Fuzzing"]
Test --> Deploy["Deploy
Image Scan, Sign"]
Deploy --> Runtime["Runtime
Monitoring, WAF"]
style Code fill:#e8f4f4,stroke:#3B9797,color:#132440
style Build fill:#e8f4f4,stroke:#3B9797,color:#132440
style Test fill:#f0f4f8,stroke:#16476A,color:#132440
style Deploy fill:#f0f4f8,stroke:#16476A,color:#132440
style Runtime fill:#fff5f5,stroke:#BF092F,color:#132440
The cost of fixing a security vulnerability increases exponentially the later it's found. A misconfigured Dockerfile caught in a PR review costs minutes to fix. The same vulnerability discovered in production during a penetration test costs days of incident response, patching, and auditing.
Software Supply Chain Security
Supply chain attacks target the tools, libraries, and processes used to build software rather than the software itself. The SolarWinds attack (2020) and Log4Shell vulnerability (2021) demonstrated that a single compromised dependency can affect thousands of organisations. DevSecOps addresses this through provenance tracking, artifact signing, and SBOM generation.
Image Signing with Cosign & Sigstore
# Install Cosign — keyless container image signing
# Part of the Sigstore project (Linux Foundation)
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# Sign a container image (keyless — uses OIDC identity)
cosign sign myregistry/web-app:v1.2.0
# Opens browser for OIDC authentication
# Signature stored in the registry alongside the image
# Verify an image signature before deployment
cosign verify myregistry/web-app:v1.2.0 \
--certificate-identity=ci@example.com \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
# Verification for myregistry/web-app:v1.2.0 --
# The following checks were performed:
# - The cosign claims were validated
# - The signatures were verified against the specified public key
echo "Image signed and verified successfully"
SBOM Generation (Software Bill of Materials)
# Generate SBOM with Syft (Anchore)
# Scans container images and produces CycloneDX or SPDX output
syft myregistry/web-app:v1.2.0 -o cyclonedx-json > sbom.json
# Attach SBOM to the container image in the registry
cosign attach sbom --sbom sbom.json myregistry/web-app:v1.2.0
# Generate SBOM from a Dockerfile / project directory
syft dir:./src -o spdx-json > project-sbom.spdx.json
# Scan the SBOM for known vulnerabilities
grype sbom:./sbom.json
# NAME INSTALLED VULNERABILITY SEVERITY
# openssl 3.0.8 CVE-2023-xxxx High
# libcurl 7.88.1 CVE-2023-yyyy Medium
echo "SBOM generated and scanned"
Container Image Scanning
Trivy — Comprehensive Vulnerability Scanner
# Trivy scans container images, filesystems, Git repos, and K8s clusters
# Install Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# Scan a container image for vulnerabilities
trivy image myregistry/web-app:v1.2.0
# web-app:v1.2.0 (debian 12.4)
# Total: 23 (UNKNOWN: 0, LOW: 10, MEDIUM: 8, HIGH: 4, CRITICAL: 1)
# Scan with severity filter (fail on HIGH or CRITICAL)
trivy image --severity HIGH,CRITICAL --exit-code 1 myregistry/web-app:v1.2.0
# Scan Kubernetes manifest files for misconfigurations
trivy config ./k8s-manifests/
# Checks: 52 (UNKNOWN: 0, LOW: 5, MEDIUM: 12, HIGH: 3, CRITICAL: 0)
# Scan a running Kubernetes cluster
trivy k8s --report summary cluster
echo "Trivy scan complete"
CI Pipeline Integration
# .github/workflows/security-scan.yaml
name: Security Scan Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Step 1: Secret scanning (prevent leaked credentials)
- name: Scan for secrets
uses: trufflesecurity/trufflehog@main
with:
extra_args: --only-verified
# Step 2: SAST — Static Application Security Testing
- name: Run Semgrep SAST
uses: semgrep/semgrep-action@v1
with:
config: >-
p/owasp-top-ten
p/javascript
p/typescript
# Step 3: Dependency vulnerability scan
- name: Dependency scan
run: trivy fs --severity HIGH,CRITICAL --exit-code 1 .
# Step 4: Build and scan container image
- name: Build Docker image
run: docker build -t myregistry/web-app:${{ github.sha }} .
- name: Scan container image
uses: aquasecurity/trivy-action@master
with:
image-ref: myregistry/web-app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: 1
# Step 5: Upload results to GitHub Security tab
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
# Step 6: Generate and attach SBOM
- name: Generate SBOM
run: |
syft myregistry/web-app:${{ github.sha }} -o cyclonedx-json > sbom.json
cosign attach sbom --sbom sbom.json myregistry/web-app:${{ github.sha }}
# Step 7: Sign the image
- name: Sign image
run: cosign sign myregistry/web-app:${{ github.sha }}
env:
COSIGN_EXPERIMENTAL: 1
Policy-as-Code
Policy-as-code encodes security and compliance rules as declarative configurations that are automatically enforced. Instead of relying on documentation and manual reviews, policies are evaluated by admission controllers in the Kubernetes API server — blocking non-compliant resources before they're created.
Kyverno — Kubernetes-Native Policy Engine
# kyverno-require-labels.yaml — Enforce mandatory labels
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-labels
annotations:
policies.kyverno.io/title: Require Labels
policies.kyverno.io/severity: medium
spec:
validationFailureAction: Enforce
background: true
rules:
- name: require-team-label
match:
any:
- resources:
kinds: ["Deployment", "StatefulSet", "DaemonSet"]
validate:
message: "The label 'team' is required on all workloads."
pattern:
metadata:
labels:
team: "?*"
# kyverno-disallow-privileged.yaml — Block privileged containers
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privileged-containers
spec:
validationFailureAction: Enforce
background: true
rules:
- name: deny-privileged
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Privileged containers are not allowed."
pattern:
spec:
containers:
- securityContext:
privileged: "false"
- name: require-non-root
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Containers must run as non-root."
pattern:
spec:
containers:
- securityContext:
runAsNonRoot: true
# kyverno-verify-images.yaml — Only allow signed images
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signatures
spec:
validationFailureAction: Enforce
webhookTimeoutSeconds: 30
rules:
- name: verify-cosign-signature
match:
any:
- resources:
kinds: ["Pod"]
verifyImages:
- imageReferences:
- "myregistry/*"
attestors:
- entries:
- keyless:
subject: "ci@example.com"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev
OPA Gatekeeper
# OPA Gatekeeper constraint template — Resource limits required
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredresources
spec:
crd:
spec:
names:
kind: K8sRequiredResources
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredresources
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits.cpu
msg := sprintf("Container '%v' must have CPU limits", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.resources.limits.memory
msg := sprintf("Container '%v' must have memory limits", [container.name])
}
Shopify's Policy-as-Code at Scale
Shopify runs over 400,000 Kubernetes pods across multiple clusters. Their DevSecOps framework uses Kyverno with 85 cluster policies covering security (no privileged containers, image signing verification), compliance (resource limits, labels), and operational standards (pod disruption budgets, topology spread). Policies are stored in Git, version-controlled, and deployed via Argo CD — making security policy changes go through the same PR review process as application code. Non-compliant resources are blocked before creation, with clear error messages guiding developers toward the correct configuration.
Secrets & Runtime Security
# TruffleHog — scan Git history for leaked secrets
trufflehog git file://. --only-verified --json
# Gitleaks — fast secret scanner for CI pipelines
gitleaks detect --source . --verbose
# Finding: AWS Access Key ID
# Secret: AKIA...
# File: config/settings.py
# Line: 42
# Commit: a3f7c2d
# Pre-commit hook to prevent secret commits
# .pre-commit-config.yaml
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
EOF
echo "Secret scanning configured"
Runtime Security with Falco
# Falco rule — detect shell in container at runtime
- rule: Terminal shell in container
desc: Detect a shell process spawned in a container
condition: >
spawned_process and container and
proc.name in (bash, sh, zsh, dash) and
not proc.pname in (cron, containerd-shim)
output: >
Shell spawned in container
(user=%user.name container=%container.name
shell=%proc.name parent=%proc.pname
image=%container.image.repository)
priority: WARNING
tags: [container, shell, mitre_execution]
- rule: Sensitive file access
desc: Detect read of sensitive files like /etc/shadow
condition: >
open_read and container and
fd.name in (/etc/shadow, /etc/passwd, /root/.ssh)
output: >
Sensitive file opened for reading
(file=%fd.name user=%user.name container=%container.name)
priority: CRITICAL
tags: [container, filesystem, mitre_credential_access]
Building a Secure CI/CD Pipeline
End-to-End Security Pipeline Architecture
flowchart TD
PR["PR Created"] --> Secrets["Secret Scan
(Gitleaks)"]
Secrets --> SAST["SAST
(Semgrep)"]
SAST --> Deps["Dependency Scan
(Trivy FS)"]
Deps --> Build["Docker Build"]
Build --> ImgScan["Image Scan
(Trivy Image)"]
ImgScan --> SBOM["Generate SBOM
(Syft)"]
SBOM --> Sign["Sign Image
(Cosign)"]
Sign --> Deploy["Deploy to K8s"]
Deploy --> Admission["Admission Control
(Kyverno)"]
Admission --> Runtime["Runtime Monitor
(Falco)"]
style PR fill:#f0f4f8,stroke:#16476A,color:#132440
style Secrets fill:#e8f4f4,stroke:#3B9797,color:#132440
style SAST fill:#e8f4f4,stroke:#3B9797,color:#132440
style Deps fill:#e8f4f4,stroke:#3B9797,color:#132440
style Build fill:#f0f4f8,stroke:#16476A,color:#132440
style ImgScan fill:#e8f4f4,stroke:#3B9797,color:#132440
style SBOM fill:#e8f4f4,stroke:#3B9797,color:#132440
style Sign fill:#e8f4f4,stroke:#3B9797,color:#132440
style Deploy fill:#f0f4f8,stroke:#16476A,color:#132440
style Admission fill:#fff5f5,stroke:#BF092F,color:#132440
style Runtime fill:#fff5f5,stroke:#BF092F,color:#132440
Conclusion & Next Steps
DevSecOps transforms security from a bottleneck into an accelerator. By embedding security scanning, policy enforcement, artifact signing, and runtime monitoring into every pipeline stage, teams ship faster because they don't need to wait for manual security reviews — the pipeline handles it automatically.
- Shift left — Find vulnerabilities at code commit, not in production. SAST, dependency scanning, and secret detection belong in every PR.
- Sign everything — Container images, SBOMs, and attestations should be cryptographically signed. Verify signatures before deployment.
- Enforce with admission control — Kyverno and OPA Gatekeeper prevent non-compliant workloads from running. Policy-as-code is auditable and version-controlled.
- Monitor at runtime — Falco detects anomalous behaviour (shell access, sensitive file reads) in running containers.
- Automate compliance — Git history + SBOM + signed images + policy reports = continuous compliance evidence.
Next in the Series
In Part 14: FinOps & Cloud Economics, we'll explore cloud cost optimisation — resource right-sizing, showback and chargeback models, Kubernetes cost attribution, spot instances, and building a FinOps practice.