Intermediate System & Host

Container Security — Docker, Kubernetes, images, capabilities, and network policies

Containers are not VMs. They share the host kernel. A compromised container can impact the host or other containers if security settings are not correctly applied.

Docker image security

The image is the starting point. An insecure image contaminates every derived container:

# Bad: runs as root, large generic base image
FROM ubuntu:latest
RUN apt-get install -y python3
COPY app.py /app/app.py
CMD ["python3", "/app/app.py"]

# Better: minimal image, non-root user, no unnecessary packages
FROM python:3.12-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
CMD ["python3", "app.py"]

Scan images before deploying:

# Trivy — image vulnerability scanner
trivy image python:3.12-alpine
trivy image --severity HIGH,CRITICAL my-app:latest

Linux capabilities — minimize them

Containers inherit kernel capabilities. Drop everything and add only what is required:

# docker-compose.yml
services:
  app:
    image: my-app:latest
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE   # only if a port < 1024 is needed
    security_opt:
      - no-new-privileges:true
    read_only: true

Never run containers with --privileged in production — it is equivalent to giving root access to the host.

Seccomp and AppArmor

Seccomp filters which syscalls the container is allowed to make:

# Docker already applies a default seccomp profile
# Inspect the current profile:
docker inspect --format='{{.HostConfig.SecurityOpt}}' my_container

# Apply a restrictive custom profile:
docker run --security-opt seccomp=/path/to/profile.json my-app

Kubernetes — security configuration

Pod Security Standards

# SecurityContext on the pod
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: my-app:1.2.3
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: ["ALL"]

Network Policies — network segmentation

By default, all pods communicate with each other. Network Policies implement least privilege at the network layer:

# Allow only traffic from frontend to backend on port 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

RBAC — access control

# ServiceAccount with minimal permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-reader
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: app-reader-binding
subjects:
- kind: ServiceAccount
  name: my-app-sa
roleRef:
  kind: Role
  name: app-reader
  apiGroup: rbac.authorization.k8s.io

Security checklist

Docker:
  - Minimal base image (alpine, distroless)
  - Non-root user in Dockerfile
  - Vulnerability scan in CI/CD (Trivy, Grype)
  - No secrets in environment variables — use a secrets manager
  - No --privileged, no /var/run/docker.sock mount

Kubernetes:
  - Pod Security Standards enabled (enforce: restricted)
  - Network Policies defined for all namespaces
  - RBAC with dedicated ServiceAccounts per application
  - Fixed version tag on images (never :latest in production)
  - Admission controllers: OPA/Gatekeeper or Kyverno for policy enforcement