LIVE: New phishing campaigns targeting mobile users —View latest threats →

Back to Tutorials
Advanced 17 min read

Docker & Kubernetes: Container Security Guide

Containers changed how we deploy software — but many teams ship them with root access, outdated images, and exposed secrets. Here's the full hardening guide.

25 February 2026

The Container Security Misconception

A common misconception is that containers provide strong isolation analogous to virtual machines. They do not. Containers share the host kernel — a container escape vulnerability that achieves code execution in the host kernel grants access to every container on that host, plus the host itself. Container security is about reducing the blast radius of a compromised container, not about assuming the boundary is impenetrable.

The attack surface spans the container image, the runtime configuration, the orchestration platform, the network, and the secrets management layer. Hardening must address all of them.

Docker Image Hardening

Use Minimal Base Images

Every package in a base image is a potential vulnerability. Prefer distroless images (Google's images with no shell, package manager, or OS tools) or Alpine Linux (musl libc, apk, ~5MB):

# Instead of ubuntu:22.04
FROM gcr.io/distroless/java21-debian12

Distroless images cannot be shelled into interactively — this limits attacker options significantly if the container is compromised.

Multi-Stage Builds

Build dependencies (compilers, build tools, test frameworks) should never be in the production image:

FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o server .

FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]

The final image contains only the compiled binary and its runtime dependencies.

Never Run as Root

By default, processes in containers run as root (UID 0). If a container escape occurs, the attacker has root on the host. Always create a non-root user:

RUN useradd -u 1001 -r appuser
USER appuser

Combine with --read-only filesystem and --tmpfs /tmp for write-needed locations.

Scan Images for Vulnerabilities

Integrate image scanning into your CI/CD pipeline:

# Trivy — fast, comprehensive vulnerability scanner
trivy image myapp:latest

# Grype — alternative with SBOM support
grype myapp:latest

Fail the build if high or critical CVEs are found. Rebuild and update base images on a regular schedule — not just when you change application code.

Docker Runtime Security

Drop Capabilities

Linux capabilities break root's all-or-nothing privilege model into discrete units. Drop all capabilities and add back only what the application needs:

docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp

Seccomp and AppArmor

Docker's default seccomp profile blocks ~44 dangerous syscalls. Use it explicitly and consider a custom profile for sensitive workloads. AppArmor or SELinux profiles add mandatory access control on top of the kernel:

docker run --security-opt seccomp=profile.json myapp
docker run --security-opt apparmor=docker-default myapp

Never Use --privileged

The --privileged flag gives the container nearly full host access, disabling all security isolation. It is occasionally required for specific use cases (like running Docker-in-Docker) but should be avoided entirely in production and treated as an immediate red flag during security reviews.

Kubernetes Security Hardening

Pod Security Standards

Kubernetes enforces security policies via Pod Security Standards (replacing the deprecated PodSecurityPolicy):

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest

The restricted profile enforces: no privileged containers, no root user, no privilege escalation, required seccomp profile, dropped all capabilities.

RBAC: Least Privilege for Service Accounts

Every pod runs with a service account. By default, that service account can reach the Kubernetes API. Apply least privilege:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]

Audit with kubectl auth can-i --list --as=system:serviceaccount:default:myapp. Disable automounting of service account tokens when not needed: automountServiceAccountToken: false.

Secrets Management

Kubernetes Secrets are Base64-encoded, not encrypted, by default. Use:

  • Encryption at rest: enable EncryptionConfiguration in the API server to encrypt secrets in etcd
  • External secrets managers: AWS Secrets Manager, HashiCorp Vault, or GCP Secret Manager via the External Secrets Operator
  • Never put secrets in ConfigMaps, environment variables in Deployment specs (visible via kubectl describe), or container images

Network Policies

By default, all pods in a Kubernetes cluster can communicate with each other. Implement NetworkPolicy to enforce micro-segmentation:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
spec:
  podSelector: {}
  policyTypes: [Ingress]

Then add explicit allow rules for necessary communication paths only. Use a CNI plugin that enforces NetworkPolicy (Calico, Cilium, Weave).

Continuous Security Posture

  • Use Falco (CNCF runtime security tool) to detect suspicious behavior at runtime: unexpected shell spawns, outbound connections to new IPs, privilege escalation attempts
  • Scan cluster configurations with kube-bench (CIS Kubernetes Benchmark) and Trivy operator
  • Enable audit logging on the Kubernetes API server and ship logs to a SIEM
  • Regularly rotate image digests and automate dependency updates with Renovate or Dependabot
#Docker#Kubernetes#container security#DevSecOps#cloud