Container Security
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.
The threat layers
| 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.
Image security
Minimal base images
Smaller images = fewer vulnerabilities + fewer tools for attackers.
- **Distroless** (Google) — base image with only the runtime your app needs. No shell, no package manager.
- **Alpine** — small Linux distribution; popular base.
- **Scratch** — for static binaries (Go, Rust) — image is just your binary.
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.
Image scanning
Scan images for known vulnerabilities at build time and on registry push.
Tools:
- **Trivy** — open source; widely used; fast.
- **Grype** — Anchore's scanner; integrates with SBOM.
- **Snyk Container** — commercial; deeper analysis.
- **AWS Inspector / Google Container Analysis** — managed cloud scanners.
Scan on every build. Block deploys with critical CVEs in production-bound images.
No embedded secrets
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.
SBOM (Software Bill of Materials)
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.
Supply-chain security
The 2020-2024 wave of supply-chain attacks (SolarWinds, npm packages, PyPI typosquatting, GitHub Actions compromise) made supply-chain security a first-class concern.
Pin dependencies
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`).
Dependency review
Tools that examine new dependencies for known issues:
- **GitHub Dependency Review action** — flags risky deps in PRs.
- **Snyk Open Source** — commercial.
- **Socket.dev** — analyses package behaviour, not just CVEs.
Review dependencies before adding. The `left-pad` style "we depend on this 12-line library" is also a supply-chain risk.
Image signing and verification
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.
Provenance attestations
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.
Runtime security
Non-root user
Don't run containers as root. Even if the container escapes, root inside means root outside (in some configurations).
```dockerfile
FROM alpine
RUN adduser -D appuser
USER appuser
```
Most images run as root by default. Audit; fix.
Read-only root filesystem
Containers don't need to write to most paths. Mark them read-only:
```yaml
securityContext:
readOnlyRootFilesystem: true
```
Limits an attacker's ability to drop persistence. Mount specific writable volumes for legitimate writes.
Drop capabilities
Linux capabilities give fine-grained privileges. Containers usually don't need most of them. Drop all and add back only what's required:
```yaml
securityContext:
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
```
Seccomp / AppArmor / SELinux
Kernel-level syscall filtering. Restricts what the container can ask the kernel to do.
- Kubernetes: `securityContext.seccompProfile.type: RuntimeDefault` is a sane default; tighten further if possible.
- Docker: `--security-opt seccomp=profile.json`.
Most workloads tolerate the default seccomp profile; few break things.
Pod Security Admission (PSA)
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.
Namespaces and cgroups
Linux primitives that isolate containers. Standard; mostly invisible. Misconfigurations (sharing PID namespace, mounting host filesystems) defeat isolation.
Network security in Kubernetes
By default, every pod can talk to every other pod. This is an attack-graph nightmare.
Network Policies
Kubernetes NetworkPolicy resources restrict pod-to-pod and pod-to-external traffic.
```yaml
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.
Service mesh
Istio, Linkerd, Cilium service mesh. Provide:
- Automatic mTLS between pods.
- Authorisation policies at the call level.
- Observability of pod-to-pod traffic.
- Egress controls.
For mature security postures, mTLS-by-default + service-mesh policy is the operational shape.
Egress filtering
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.
Runtime threat detection
Detect attacks in progress:
- **Falco** — runtime behaviour rules; fires on suspicious syscalls (e.g., container spawning a shell).
- **Sysdig, Aqua, Tetragon** — broader runtime detection; commercial / open-source variants.
- **Cloud-native equivalents** — AWS GuardDuty, Google Security Command Center.
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.
Secret management
- **Don't bake secrets into images.** (Said before; saying again.)
- **Don't put secrets in environment variables in plain ConfigMaps.** They appear in `kubectl describe`.
- **Use Kubernetes Secrets** (encrypted at rest if you've configured KMS encryption) for basic cases.
- **Use external secret managers** (Vault, AWS Secrets Manager, GCP Secret Manager) for production. Tools like External Secrets Operator sync them.
- **Rotate regularly.** Stale secrets are a smell.
For high-stakes secrets, mount via short-lived token from the manager, not as long-lived env vars.
Container image lifecycle
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.
Patching cadence
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.
Common failure modes
- **Privileged containers in production.** "It worked in dev so we shipped it." Privileged means root on the host. Audit; remove.
- **HostPath mounts of sensitive directories.** `/var/run/docker.sock` mounted into a container = Docker daemon takeover.
- **Default-allow network policies.** Or no network policies at all. Lateral movement ready.
- **Stale base images.** Never updated; CVE backlog grows.
- **Public registry by default.** "Pull from Docker Hub anonymously." Replace with private registry or proxy with caching.
- **No image-signature enforcement.** "We sign images but don't verify on deploy." Sign and verify; otherwise the signing was theatre.
A pragmatic baseline
For a Kubernetes deployment running in production:
1. **Distroless or slim base images** for application containers.
2. **Image scanning** in CI; block deploys on critical CVEs.
3. **No embedded secrets**; use Kubernetes Secrets + KMS encryption.
4. **Non-root, read-only root FS, drop capabilities** in pod security context.
5. **Restricted PSA** on application namespaces.
6. **Network policies** with default-deny and explicit allows.
7. **Image signing + admission verification.**
8. **Falco or equivalent** for runtime detection.
9. **Weekly base-image rebuilds.**
10. **Service mesh with mTLS** for production at scale.
A few weeks of work; defends against the bulk of the threat surface.
Further reading
- [ContainerOrchestration]() — Kubernetes mechanics
- [ApplicationSecurityFundamentals]() — broader app-sec context
- [ThreatModeling]() — anticipating threats
- [ZeroTrustArchitecture]() — broader zero-trust posture