Container Security Guide
A comprehensive approach to securing containers from build to runtime.
Datanest Digital — datanest.dev
Table of Contents
- Image Hardening
- Vulnerability Scanning Pipeline
- Policy Enforcement
- Runtime Security
- CI/CD Integration
- Compliance Mapping
- Incident Response
Image Hardening
Principle: Minimal, Immutable, Non-Root
Every container image should follow three principles:
- Minimal: Include only what the application needs to run. No shells, no package managers, no debugging tools in production images.
- Immutable: Never patch running containers. Rebuild and redeploy.
- Non-root: Never run as UID 0.
Multi-Stage Builds
Multi-stage builds are the foundation of hardened images. Build tools, compilers, and development dependencies never reach the final image:
# Build stage — has gcc, make, pip, etc.
FROM python:3.12-slim AS builder
COPY requirements.txt .
RUN pip install --prefix=/install -r requirements.txt
# Production stage — only runtime dependencies
FROM python:3.12-slim
COPY --from=builder /install /usr/local
COPY --chown=10001:10001 src/ ./src/
USER 10001
Base Image Selection
| Language | Recommended Base | Size | Notes |
|---|---|---|---|
| Python | python:X.Y-slim |
~150MB | Debian slim, includes glibc |
| Node.js | node:X-alpine |
~50MB | Musl libc, smallest option |
| Go |
scratch or gcr.io/distroless/static
|
~2-10MB | No OS at all |
| Java |
eclipse-temurin:X-jre + debian:slim
|
~100MB | JRE only, no JDK |
Checklist
- [ ] Pin base image version (never use
:latest) - [ ] Use multi-stage build
- [ ] Run as non-root user with explicit UID
- [ ] Set
HEALTHCHECKinstruction - [ ] Use
COPYinstead ofADD - [ ] No secrets in
ENVorARG - [ ] Add OCI labels for traceability
- [ ] Use
.dockerignoreto exclude.git,node_modules, etc.
Vulnerability Scanning Pipeline
Defense in Depth: Multiple Scanners
No single scanner catches everything. Use at least two:
| Scanner | Strength | Weakness |
|---|---|---|
| Trivy | Fast, comprehensive, misconfigs | Fewer language-specific advisories |
| Grype | Strong language ecosystem coverage | No misconfig scanning |
| Hadolint | Dockerfile best practices | Only lints Dockerfiles |
Scanning Strategy
Developer Workstation CI/CD Pipeline Registry
│ │ │
hadolint lint trivy image scan periodic rescan
(pre-commit) grype image scan (new CVE DB)
│ conftest policy │
│ │ │
└──── Push ──── Build ── Scan ── Push ── Monitor
Handling Findings
- CRITICAL: Block deployment. Fix immediately.
- HIGH: Block deployment. Fix within 7 days.
- MEDIUM: Allow deployment. Fix within 30 days.
- LOW: Track in backlog.
False Positives
Create a .trivyignore file for accepted risks:
# CVE-2024-12345: Not exploitable in our configuration
# Reviewed by: security-team, Date: 2026-01-15
CVE-2024-12345
Always document the justification and review date.
Policy Enforcement
OPA (Open Policy Agent)
OPA policies enforce rules at build time using conftest:
# Test Dockerfiles against policies
conftest test Dockerfile -p policies/opa/
# Test Kubernetes manifests
conftest test deployment.yaml -p policies/opa/
Key policies included:
- dockerfile-policy.rego: No root, no latest, no ADD, no secrets in ENV
- k8s-pod-security.rego: Restricted PSS profile enforcement
Kyverno
Kyverno policies enforce rules at deploy time as a Kubernetes admission controller:
# Install Kyverno
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
# Apply policies
kubectl apply -f policies/kyverno/
Key policies:
- require-non-root: Containers must run as non-root
- require-resource-limits: CPU/memory limits mandatory
-
require-image-digest: Images must use
@sha256:references
Policy Modes
| Mode | Behavior | Use Case |
|---|---|---|
Enforce |
Block non-compliant resources | Production |
Audit |
Log violations but allow | Migration period |
Start in Audit mode, monitor violations, then switch to Enforce.
Runtime Security
Seccomp Profiles
Seccomp restricts which syscalls a container can make. The included profile blocks:
-
ptrace— Prevents debugging/container escape -
mount/umount— Prevents filesystem manipulation -
unshare/setns— Prevents namespace escape -
kexec_load— Prevents kernel replacement -
init_module— Prevents kernel module loading
Apply in Kubernetes:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profiles/seccomp-profile.json
AppArmor Profiles
AppArmor provides mandatory access control. The included profile:
- Denies raw network sockets
- Denies mount operations
- Restricts file system access
- Blocks execution from
/tmp(common attack vector) - Denies ptrace
# Load profile on each node
sudo apparmor_parser -r /etc/apparmor.d/container-restricted
# Apply to pod
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/myapp: localhost/container-restricted
Security Context Best Practices
Every production pod should have:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]
CI/CD Integration
GitHub Actions Pipeline
The included workflow (container-security.yml) implements:
- Lint — Hadolint checks Dockerfile best practices
- Build — Docker Buildx with layer caching and SBOM generation
- Scan — Trivy + Grype vulnerability scanning with SARIF upload
- Policy — conftest OPA policy validation
- Gate — Security summary with pass/fail gate
Pre-commit Hooks
# .pre-commit-config.yaml
repos:
- repo: https://github.com/hadolint/hadolint
rev: v2.12.0
hooks:
- id: hadolint-docker
Scheduled Rescanning
New CVEs are published daily. Rescan deployed images on a schedule:
on:
schedule:
- cron: '0 6 * * *' # Daily at 6 AM UTC
Compliance Mapping
| Security Control | CIS Docker 1.6 | CIS Kubernetes 1.8 | NIST 800-190 |
|---|---|---|---|
| Non-root user | 4.1 | 5.2.6 | CM-7 |
| Read-only rootfs | 5.12 | 5.2.4 | AC-6 |
| Resource limits | 5.10 | 5.2.7 | SC-6 |
| Image scanning | 4.4 | — | RA-5 |
| Seccomp profiles | 5.2 | 5.2.2 | SC-39 |
| No privileged | 5.4 | 5.2.1 | AC-6(1) |
| Image signing | 4.5 | — | SI-7 |
| Network policies | — | 5.3.2 | SC-7 |
Incident Response
Container Compromise Playbook
- Isolate: Apply a deny-all NetworkPolicy to the compromised pod
- Capture: Export container filesystem for forensics
kubectl cp <pod>:/app ./forensics/app-snapshot
docker export <container> > forensics/container.tar
- Investigate: Check logs, network connections, process list
- Remediate: Rebuild from clean base image, rotate secrets
- Harden: Add policies to prevent recurrence
Useful Forensics Commands
# Check running processes in container
kubectl exec <pod> -- ps aux
# Check network connections
kubectl exec <pod> -- netstat -tlnp
# Check for unexpected SUID binaries
kubectl exec <pod> -- find / -perm -4000 -type f 2>/dev/null
# Check container events
kubectl describe pod <pod> | grep -A 20 Events
Part of the Container Security Toolkit by Datanest Digital.
For support: hello@datanest.dev
This is 1 of 6 resources in the DevOps Toolkit Pro toolkit. Get the complete [Container Security Toolkit] with all files, templates, and documentation for $XX.
Or grab the entire DevOps Toolkit Pro bundle (6 products) for $178 — save 30%.
Top comments (0)