Key Takeaways

  • Never run as root inside containers
  • Scan images for vulnerabilities in CI/CD
  • Use minimal base images (distroless, alpine)
  • Container escapes are real and dangerous

1. Container Security Fundamentals

Containers share the host kernel, making them less isolated than VMs. Security requires attention at every layer: image, runtime, orchestration, and infrastructure.

Container vs VM Isolation

2. Secure Image Building

# Dockerfile security best practices
FROM alpine:3.18  # Use minimal base image

# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Copy only necessary files
COPY --chown=appuser:appgroup app /app

# Set non-root user
USER appuser

# Use specific versions, not 'latest'
# Don't store secrets in image
# Use multi-stage builds to minimize final image

# Multi-stage build example
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]

3. Image Vulnerability Scanning

# Trivy - comprehensive scanner
trivy image myapp:latest
trivy image --severity HIGH,CRITICAL myapp:latest

# Grype
grype myapp:latest

# Snyk
snyk container test myapp:latest

# Docker Scout (built into Docker)
docker scout cves myapp:latest

# CI/CD integration
# Fail build if HIGH/CRITICAL vulns found
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest

4. Runtime Security

# Docker security options
docker run --read-only \
           --security-opt=no-new-privileges \
           --cap-drop=ALL \
           --cap-add=NET_BIND_SERVICE \
           --user 1000:1000 \
           myapp:latest

# Seccomp profile (limit syscalls)
docker run --security-opt seccomp=/path/to/profile.json myapp

# AppArmor profile
docker run --security-opt apparmor=docker-default myapp

# Drop all capabilities, add only needed
# CAP_NET_BIND_SERVICE: bind to ports <1024
# CAP_CHOWN, CAP_SETUID: usually not needed

5. Container Escape Attacks

Container Escape Vectors
  • Privileged containers: Full host access
  • Docker socket mount: Create privileged containers
  • Kernel exploits: Shared kernel vulnerabilities
  • Sensitive host mounts: /etc, /root, /var/run/docker.sock
# DANGEROUS: Never do this in production
docker run --privileged -v /:/host myapp
# Attacker can: chroot /host && access everything

# Docker socket escape
docker run -v /var/run/docker.sock:/var/run/docker.sock myapp
# Inside container:
docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh
# Now running as root on host!

6. Kubernetes Security

# Pod Security Standards (restrictive)
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL

# Network Policies (deny by default)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

7. Container Security Tools

8. Hardening Checklist

Container Security Checklist
  • ✅ Use minimal base images (distroless, alpine)
  • ✅ Run as non-root user
  • ✅ Scan images in CI/CD pipeline
  • ✅ Drop all capabilities, add only needed
  • ✅ Use read-only root filesystem
  • ✅ Never run privileged containers
  • ✅ Never mount Docker socket
  • ✅ Sign and verify images
  • ✅ Implement network policies
  • ✅ Enable audit logging

FAQ

Are containers secure by default?
No. Default Docker configurations are optimized for convenience, not security. You must actively harden containers with non-root users, dropped capabilities, and proper image hygiene.

Kubernetes Security Cloud Security API Security