DevOps
DevSecOps Fundamentals
Understand DevSecOps principles: shift-left security, secure coding practices, and integrating security into CI/CD pipelines.
By TechCoder TeamLast updated: 2026-06-02
In a Nutshell
Understand DevSecOps principles: shift-left security, secure coding practices, and integrating security into CI/CD pipelines. This hands-on tutorial focuses on practical implementation of devsecops fundamentals concepts.
DevSecOps Fundamentals
DevSecOps integrates security practices into the DevOps workflow—shifting security left and making it everyone's responsibility.
What is DevSecOps?
Traditional: DevSecOps:
Dev ──> Ops ──> Sec Dev + Sec + Ops (simultaneous)
↓
┌─────────────────────┐
│ Security built-in │
│ at every stage │
└─────────────────────┘
Shift-Left Security
Requirements ──> Design ──> Code ──> Test ──> Deploy ──> Operate
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
Threat Security SAST DAST Runtime Incident
Modeling by SonarQube OWASP Security Response
Design Trivy ZAP WAF Forensics
Security considerations moved earlier in the development lifecycle.
The Three Pillars of DevSecOps
- People: Culture of shared responsibility
- Process: Automated security checks in pipeline
- Technology: Security tools integrated into toolchain
Security in CI/CD Pipeline
Pipeline Security Stages
# .github/workflows/secure-pipeline.yml
name: Secure CI/CD
on: [push, pull_request]
jobs:
# Stage 1: Secret Detection
secrets-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Detect Secrets
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: main
head: HEAD
extra_args: --debug --only-verified
# Stage 2: Software Composition Analysis (SCA)
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk
uses: snyk/actions/node@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
- name: OWASP Dependency-Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: 'my-app'
path: '.'
format: 'ALL'
# Stage 3: Static Application Security Testing (SAST)
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SonarQube Scan
uses: sonarqube-quality-gate/action@master
with:
host: ${{ secrets.SONAR_HOST }}
token: ${{ secrets.SONAR_TOKEN }}
- name: CodeQL Analysis
uses: github/codeql-action/init@v2
with:
languages: javascript
- uses: github/codeql-action/analyze@v2
# Stage 4: Build and Image Scan
build-scan:
runs-on: ubuntu-latest
needs: [secrets-scan, dependency-scan, sast]
steps:
- uses: actions/checkout@v4
- name: Build Image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan Image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload to Security Tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
# Stage 5: Dynamic Application Security Testing (DAST)
dast:
runs-on: ubuntu-latest
needs: build-scan
steps:
- name: Deploy to Staging
run: ./deploy-staging.sh
- name: OWASP ZAP Scan
uses: zaproxy/action-full-scan@v0.7.0
with:
target: 'https://staging.example.com'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a'
Secure Coding Practices
OWASP Top 10 (2021)
| Rank | Risk | Prevention |
|---|---|---|
| A01 | Broken Access Control | Least privilege, deny by default |
| A02 | Cryptographic Failures | Encrypt data, use strong algorithms |
| A03 | Injection | Parameterized queries, input validation |
| A04 | Insecure Design | Threat modeling, secure patterns |
| A05 | Security Misconfiguration | Hardening, minimal features |
| A06 | Vulnerable Components | Dependency scanning, updates |
| A07 | Auth Failures | MFA, strong passwords, session mgmt |
| A08 | Integrity Failures | Software supply chain security |
| A09 | Logging Failures | Comprehensive logging, monitoring |
| A10 | SSRF | URL validation, whitelist domains |
Input Validation
# Bad
query = f"SELECT * FROM users WHERE id = {user_id}"
cursor.execute(query)
# Good - Parameterized queries
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
# Input validation
import re
from pydantic import BaseModel, validator
class UserInput(BaseModel):
email: str
age: int
@validator('email')
def validate_email(cls, v):
if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', v):
raise ValueError('Invalid email')
return v
@validator('age')
def validate_age(cls, v):
if v < 0 or v > 150:
raise ValueError('Invalid age')
return v
Secrets Management
# Bad - Never do this!
api_key: "sk-live-1234567890abcdef"
# Good - Use environment variables
api_key: ${API_KEY}
# Better - Use secret management service
# AWS Secrets Manager
aws secretsmanager get-secret-value --secret-id prod/api/key
# HashiCorp Vault
vault kv get secret/api/key
# Kubernetes Secrets
kubectl create secret generic api-key --from-literal=key=secretvalue
# Docker Secrets (Swarm)
echo "secretvalue" | docker secret create api_key -
Container Security
Dockerfile Best Practices
# Use minimal base images
FROM alpine:latest
# Or even better - distroless
FROM gcr.io/distroless/nodejs18-debian11
# Run as non-root
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# Scan for vulnerabilities
# docker scan myimage:latest
# or trivy image myimage:latest
# Don't leak secrets
# Bad
ENV API_KEY=sk-1234567890
# Good
ENV API_KEY=${API_KEY}
ARG API_KEY
RUN echo $API_KEY > /app/config && ...
# Multi-stage to reduce attack surface
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY /app/dist ./dist
COPY /app/node_modules ./node_modules
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]
Container Security Scanning
# Trivy - comprehensive scanner
trivy image myimage:latest
trivy fs --security-checks vuln,config,secret ./
trivy k8s --report summary cluster
# Grype
grype myimage:latest
# Docker Scout
docker scout cves myimage:latest
# In CI/CD
trivy image --exit-code 1 --severity HIGH,CRITICAL myimage:latest
Infrastructure Security
Terraform Security (tfsec)
# Install tfsec
brew install tfsec
# Scan Terraform
tfsec .
tfsec . --format sarif --out tfsec.sarif
# Fix issues automatically
tfsec . --fix
# Bad - Unencrypted S3 bucket
resource "aws_s3_bucket" "bad" {
bucket = "my-bucket"
}
# Good - Encrypted with private ACL
resource "aws_s3_bucket" "good" {
bucket = "my-bucket"
}
resource "aws_s3_bucket_server_side_encryption_configuration" "good" {
bucket = aws_s3_bucket.good.bucket
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.mykey.arn
}
}
}
resource "aws_s3_bucket_public_access_block" "good" {
bucket = aws_s3_bucket.good.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Kubernetes Security
# Pod Security Standards
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
memory: "256Mi"
cpu: "500m"
requests:
memory: "128Mi"
cpu: "250m"
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
# kube-bench - CIS benchmark scanning
docker run --pid=host aquasec/kube-bench:latest
# kube-hunter - penetration testing
kube-hunter --remote some.node.com
# kubectl audit
kubectl get pods --all-namespaces -o json | kubesec scan -
Compliance as Code
Policy as Code (Open Policy Agent)
# policy.rego - Deny public S3 buckets
package aws.s3
deny[msg] {
bucket := input.resource.aws_s3_bucket[name]
bucket.acl == "public-read"
msg := sprintf("S3 bucket %s is publicly readable", [name])
}
deny[msg] {
bucket := input.resource.aws_s3_bucket[name]
not bucket.server_side_encryption_configuration
msg := sprintf("S3 bucket %s is not encrypted", [name])
}
# Run OPA
opa eval --data policy.rego --input terraform.tfstate.json "data.aws.s3.deny"
Security Metrics
| Metric | Target | Tool |
|---|---|---|
| Vulnerability Scan Coverage | 100% of images | Trivy, Snyk |
| Mean Time to Patch (MTTP) | < 24 hours | Dependency bot |
| Security Test Pass Rate | > 95% | CI/CD pipeline |
| Secrets in Code | 0 | GitLeaks, TruffleHog |
| IAM Policy Violations | 0 | IAM Analyzer |
Quiz
Quiz
Question 1 of 5What does 'shift-left security' mean?
Moving security checks to production
Integrating security earlier in the development lifecycle
Using left-hand authentication
Security only for legacy systems
Next Steps
Now let's explore security tools and secrets management in detail.