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

LayerThreatDefence
ImageVulnerable dependencies; embedded secretsImage scanning; minimal base images; secret hygiene
RegistryTampered images; unauthorised pullsSigned images; private registry; access controls
Build pipelineCompromised CI; malicious dependenciesSBOM; provenance attestations; pipeline isolation
Runtime (container)Escape; privilege escalation; lateral movementNon-root users; read-only filesystem; seccomp/AppArmor
Runtime (orchestrator)Misconfigured RBAC; pod-to-pod attacksNetwork policies; PSA; admission controllers
NetworkLateral movement; data exfiltrationmTLS; 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.

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:

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:

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).

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:

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:

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.

Most workloads tolerate the default seccomp profile; few break things.

Pod Security Admission (PSA)

Kubernetes' built-in policy enforcer. Three modes:

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.

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:

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:

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

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

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