Containerising your application doesn't make it secure. Containers add isolation but introduce a new attack surface — image registries, container runtimes, orchestrators, supply chains. The 2026 threat landscape includes supply-chain attacks (the SolarWinds template), runtime escapes, misconfigured K8s exposures, and credential theft through container metadata services.
This page is the working defence in depth.
| Layer | Threat | Defence |
|---|---|---|
| Image | Vulnerable dependencies; embedded secrets | Image scanning; minimal base images; secret hygiene |
| Registry | Tampered images; unauthorised pulls | Signed images; private registry; access controls |
| Build pipeline | Compromised CI; malicious dependencies | SBOM; provenance attestations; pipeline isolation |
| Runtime (container) | Escape; privilege escalation; lateral movement | Non-root users; read-only filesystem; seccomp/AppArmor |
| Runtime (orchestrator) | Misconfigured RBAC; pod-to-pod attacks | Network policies; PSA; admission controllers |
| Network | Lateral movement; data exfiltration | mTLS; egress controls; service mesh policies |
Most teams do half of these well and neglect the other half. Each gap is exploited regularly.
Smaller images = fewer vulnerabilities + fewer tools for attackers.
A Python app on python:3.11-slim (~80MB) has dozens of CVEs at any time. The same app on python:3.11-slim-distroless has fewer because there are fewer packages.
Scan images for known vulnerabilities at build time and on registry push.
Tools:
Scan on every build. Block deploys with critical CVEs in production-bound images.
A surprising number of images ship with API keys, database passwords, SSH keys baked into layers. Tools (detect-secrets, gitleaks, trufflehog) scan for this.
The fix: secrets at runtime via environment variables, mounted volumes, or secret managers (AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets). Never in the image.
A list of every component in the image. Enables vulnerability tracking, license compliance, supply-chain analysis.
Standards: SPDX, CycloneDX. Tools: Syft, ScribeSecurity, Microsoft SBOM Tool.
Generate at build; attach to the image; consumed by scanners and policy engines.
The 2020-2024 wave of supply-chain attacks (SolarWinds, npm packages, PyPI typosquatting, GitHub Actions compromise) made supply-chain security a first-class concern.
Don't pip install requests in your Dockerfile (latest version, could change anytime). Pin: pip install requests==2.32.4. Use lockfiles (requirements.txt, package-lock.json, go.sum, Cargo.lock).
Tools that examine new dependencies for known issues:
Review dependencies before adding. The left-pad style "we depend on this 12-line library" is also a supply-chain risk.
Sign images at build with Sigstore (Cosign), Notary, or registry-native signing. Verify at deploy.
# Build and sign
cosign sign --key cosign.key registry.example/app:v1.2.3
# Verify before deploy
cosign verify --key cosign.pub registry.example/app:v1.2.3
Kubernetes admission controllers (Kyverno, OPA Gatekeeper) can enforce: only deploy signed images.
SLSA (Supply-chain Levels for Software Artifacts) framework. Attestations prove "this image was built by this CI on this commit." Verifiable; tamper-evident.
In 2026, SLSA Level 3 is achievable with mainstream CI (GitHub Actions, GitLab CI). Adopt for production-bound builds.
Don't run containers as root. Even if the container escapes, root inside means root outside (in some configurations).
FROM alpine
RUN adduser -D appuser
USER appuser
Most images run as root by default. Audit; fix.
Containers don't need to write to most paths. Mark them read-only:
securityContext:
readOnlyRootFilesystem: true
Limits an attacker's ability to drop persistence. Mount specific writable volumes for legitimate writes.
Linux capabilities give fine-grained privileges. Containers usually don't need most of them. Drop all and add back only what's required:
securityContext:
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
Kernel-level syscall filtering. Restricts what the container can ask the kernel to do.
securityContext.seccompProfile.type: RuntimeDefault is a sane default; tighten further if possible.--security-opt seccomp=profile.json.Most workloads tolerate the default seccomp profile; few break things.
Kubernetes' built-in policy enforcer. Three modes:
privileged — no restrictions (legacy).baseline — common-sense restrictions.restricted — strict; root prevented; capabilities dropped.Set restricted on application namespaces; relax only for specific exemptions.
Linux primitives that isolate containers. Standard; mostly invisible. Misconfigurations (sharing PID namespace, mounting host filesystems) defeat isolation.
By default, every pod can talk to every other pod. This is an attack-graph nightmare.
Kubernetes NetworkPolicy resources restrict pod-to-pod and pod-to-external traffic.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-frontend-only
spec:
podSelector:
matchLabels:
app: api
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
Default-deny + explicit allow is the right shape. Without these, lateral movement is easy.
Istio, Linkerd, Cilium service mesh. Provide:
For mature security postures, mTLS-by-default + service-mesh policy is the operational shape.
Prevent compromised pods from exfiltrating data or beaconing to C2 servers. Egress proxies (Cilium, ASM, Tailscale-style ZTNA) restrict outbound traffic to allowlisted destinations.
Most teams don't do this. The pods that compromised do it for you.
Detect attacks in progress:
Look for: unexpected processes spawning, network connections to unusual destinations, modifications to /etc, container escapes.
For mid-size and larger teams, runtime threat detection is increasingly table stakes.
kubectl describe.For high-stakes secrets, mount via short-lived token from the manager, not as long-lived env vars.
A defensible pipeline:
Code commit
↓
CI builds image (SLSA-attested)
↓
Image scanned for CVEs and secrets
↓
Image signed with Cosign
↓
Image pushed to private registry
↓
Admission controller verifies signature on deploy
↓
Pod runs with non-root, restricted PSA, network policies, runtime detection
Each step adds a layer. None are individually expensive; the cumulative defence is strong.
Container images become stale. Even a patched application has unpatched base images.
Rebuild and redeploy on a regular cadence — weekly is typical for production. Automated tools (Renovate, Dependabot) for dependency updates; rebuild your base images when upstream releases security updates.
A container image deployed 2 years ago and never updated is a pile of unpatched CVEs.
/var/run/docker.sock mounted into a container = Docker daemon takeover.For a Kubernetes deployment running in production:
A few weeks of work; defends against the bulk of the threat surface.