Container security is not an afterthought—it must be integrated into every stage of your application lifecycle. This guide covers essential DevSecOps practices for securing containerized applications.
Introduction
As containerization becomes more prevalent, security threats evolve. From vulnerable base images to runtime exploits, containers present unique security challenges. DevSecOps integrates security into development and operations workflows, making security a shared responsibility.
1. Image Security
Use Minimal Base Images
Start with minimal base images to reduce the attack surface:
# Bad: Uses full OS image
FROM ubuntu:latest
RUN apt-get update && apt-get install -y python3 pip
RUN pip install flask
# Good: Uses minimal Python image
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
Scan for Vulnerabilities
Use tools like Trivy to scan images:
trivy image myapp:latest
trivy image --severity HIGH,CRITICAL myapp:latest
Non-Root User
Always run containers as non-root:
RUN useradd -m -u 1000 appuser
USER appuser
2. Build Security
Multi-Stage Builds
Use multi-stage builds to minimize image size and exposure:
# Build stage
FROM golang:1.21 as builder
WORKDIR /build
COPY . .
RUN CGO_ENABLED=0 go build -o app .
# Runtime stage
FROM scratch
COPY --from=builder /build/app /app
ENTRYPOINT ["/app"]
Supply Chain Security
Implement signed commits and signed container images:
# Sign container image
cosign sign myregistry.azurecr.io/myapp:latest
# Verify signature
cosign verify myregistry.azurecr.io/myapp:latest
3. Registry Security
Private Registries
Use private container registries with proper authentication:
apiVersion: v1
kind: Secret
metadata:
name: regcred
type: kubernetes.io/dockercfg
data:
.dockercfg: <base64-encoded-config>
Image Signing and Attestation
Implement image signing for supply chain security:
# Sign image with cosign
cosign sign --key cosign.key myregistry.azurecr.io/myapp:latest
# Verify with public key
cosign verify --key cosign.pub myregistry.azurecr.io/myapp:latest
4. Runtime Security
Pod Security Standards
Enforce security standards at the pod level:
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
spec:
privileged: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'MustRunAs'
fsGroup:
rule: 'MustRunAs'
readOnlyRootFilesystem: true
Network Policies
Restrict traffic with network policies:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-network-policy
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: production
- podSelector:
matchLabels:
app: frontend
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: TCP
port: 53
5. CI/CD Pipeline Security
Container Scanning in Pipeline
Integrate scanning into your CI/CD:
stages:
- build
- scan
- deploy
build:
stage: build
script:
- docker build -t myapp:$CI_COMMIT_SHA .
scan:
stage: scan
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:$CI_COMMIT_SHA
allow_failure: false
deploy:
stage: deploy
script:
- kubectl apply -f k8s/
Secret Management
Never store secrets in code or container images:
# Use external secret management
# HashiCorp Vault
vault kv get secret/database/credentials
# AWS Secrets Manager
aws secretsmanager get-secret-value --secret-id db/prod/password
# Kubernetes Secrets
kubectl create secret generic db-credentials \
--from-literal=password=$DB_PASSWORD
6. Compliance and Auditing
Logging and Monitoring
Implement comprehensive logging:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
verbs: ["create", "update", "patch", "delete"]
omitStages:
- RequestReceived
Regular Audits
- Automated vulnerability scanning
- Penetration testing
- Compliance checks against standards (CIS, PCI-DSS)
- Code review and security reviews
7. Incident Response
Prepare for Incidents
- Document security incidents response procedures
- Have rollback plans ready
- Maintain backups
- Test disaster recovery procedures
Best Practices Summary
- Start Secure: Begin with secure base images and minimal dependencies
- Scan Everything: Scan images, dependencies, and code regularly
- Least Privilege: Run containers as non-root with minimal permissions
- Network Segmentation: Use network policies to restrict traffic
- Secret Management: Never hardcode secrets
- Audit and Monitor: Log all activities and monitor for anomalies
- Continuous Learning: Stay updated with security advisories and best practices
Conclusion
Container security requires a holistic approach spanning development, deployment, and operations. By implementing these DevSecOps practices, you can significantly reduce the attack surface and build more resilient systems.
Remember: security is a journey, not a destination. Continuously improve your practices, stay informed about emerging threats, and make security a core value in your organization.
Have thoughts or want to discuss?
I'd love to hear your thoughts on this article or discuss related topics.
Send Me a Message