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