Containers and orchestration
Container security
A container is a convenience boundary, not a strong security boundary. Defend it in layers, with minimal images, non-root users, read-only filesystems, dropped capabilities and no secrets in layers.
Containers share the host kernel, so a kernel exploit can reach the host. That makes a container a convenience boundary, not a strong one. You defend it in layers. Each layer limits what an attacker can do after they get in.
Concept
Linux capabilities split root's powers into about 40 separate permissions, such as CAP_NET_ADMIN. A seccomp profile is a filter that blocks risky system calls. Docker applies both by default, and you can tighten them.
Walking through a hardened container
This command starts an image with most defences turned on.
docker run --read-only --tmpfs /tmp \
--cap-drop=ALL --security-opt no-new-privileges \
--user 10001 --memory 512m --cpus 0.5 \
myorg/api@sha256:9f86d081884c7d65...Each flag closes one door.
--read-onlywith--tmpfs /tmp: the root filesystem is read-only, and only/tmpis writable. An attacker cannot drop a binary, and an accidental write fails loudly.--cap-drop=ALL: removes every capability. Most web services need none.--security-opt no-new-privileges: a setuid binary cannot gain extra rights.--user 10001: the process is not root.--memoryand--cpus: the limits from the cgroups page apply.- The image is pinned by digest, so the bytes are the ones you tested.
Layers of defence
- Minimal images. Every package is a potential CVE. A distroless image has no shell, so an attacker cannot simply run
curl | sh. - Image scanning and signing. Tools such as Trivy, Grype and Docker Scout compare packages against vulnerability databases. Fail CI on fixable critical CVEs, and sign images with cosign. The CI page covers the supply chain.
- Non-root user. Ideally user namespaces map container root to an unprivileged host UID.
- Read-only root filesystem. Mount a
tmpfsonly for paths that need writes. - Dropped capabilities. Add back only what the service needs.
- No secrets in images. Inject runtime secrets from a manager, as covered in secrets and encryption. Use BuildKit secret mounts for build-time credentials.
- Never mount the Docker socket. Anyone who can reach it effectively has root on the host.
Common mistakes
- Saying "it's in a container, so it's safe." Containers share the kernel. Do not run untrusted code on that assumption.
- Putting a credential in a
RUNstep. Anyone who can pull the image can read the layer that holds it, even if a later step deleted it. - Mounting
/var/run/docker.sockinto an app. The app can start a privileged container on the host.
Check yourself
- Why is deleting a secret in a later layer not enough? Answer: The earlier layer still contains the secret, and anyone who pulls the image can read it.
- What does
--cap-drop=ALLchange? Answer: It removes all Linux capabilities, so even root inside the container has few privileges to use.
Key takeaways
- Treat a container as a convenience boundary and defend it in layers.
- Minimal images, scanning and signing shrink what can be attacked.
- Non-root users, read-only filesystems and dropped capabilities limit what an attacker gets.
- Secrets never belong in image layers, and the Docker socket never belongs in a container.
Further learning
The next page applies these ideas to a real image and shows how far a Dockerfile can shrink it: shrinking an image. For secrets handling, read secrets and encryption.
Related
Namespaces and cgroups
A container is a Linux process with two kernel features applied. Namespaces control what it can see, and cgroups control what it can use.
Secrets management and encryption
Keeping credentials out of code and data safe at rest and in transit with KMS, Vault and envelope encryption — plus how to catch leaks before they become incidents.
Continuous integration and the software supply chain
Fast, trustworthy pipelines - build, test, artifact, scan and sign - plus DORA metrics and supply-chain controls that keep trunk green.