Back
2/4

Security & Best Practices

+20 XP on completion

#Security & Best Practices

After this lesson you'll know:

  • why running as root in a container is dangerous
  • how to run containers with minimal privileges
  • what secrets are and how to use them safely
  • how resource limits prevent DoS from within a container

#Don't run as root

⚠️ Container root is dangerous

Containers run as root by default. An attacker who breaks into the container has root privileges on the host.

# Better: Create your own user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

# Everything now runs as appuser

#Reduce capabilities

💡 Principle of least privilege

Linux capabilities are permissions for specific system calls. Docker gives containers many by default. You don't need most — and every open capability is an attack vector.

# Only allow the capability you actually need: binding to ports under 1024
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
💡 How to test

Even with --cap-drop ALL most containers run fine. If something breaks (e.g. ping), add specific capabilities instead of allowing everything.

#No secrets in the image!

🚫 Secrets in images are a security risk

Once baked into the image, anyone with registry access can extract the secret — docker history shows it in plain text.

# WRONG — API key is baked into the image forever
ENV API_KEY=sk-12345

# RIGHT — pass at runtime
docker run -e API_KEY=sk-12345 my-app
# Or with Docker Secrets (recommended for Swarm)
docker run --secret id=api-key my-app

#Read-Only Root Filesystem

💡 Read-only = more secure

A read-only filesystem prevents attackers from persisting malware. Temporary data goes to RAM (--tmpfs).

docker run --read-only --tmpfs /tmp nginx
# The container can't write to the filesystem (except /tmp)

#Resource Limits as a Security Measure

⚠️ Containers without limits = a risk to the whole host

A bug or attack that eats 100% CPU — without limits, every other container suffers. This is a denial of service from within.

# Limit memory (most important DoS protection)
docker run --memory=256m nginx

# Limit CPU
docker run --cpus=1.5 nginx

# Auto-restart on crashes
docker run --restart=unless-stopped nginx

# All together — secure setup
docker run -d \
  --name web \
  --memory=512m \
  --cpus=1 \
  --restart=unless-stopped \
  nginx

#✋ Try it out

  • Add USER to your Dockerfile and rebuild. Start the container and check with whoami
  • Start a container with --cap-drop ALL and try ping google.com — why doesn't it work?
  • Start a container with --read-only --tmpfs /tmp and try writing a file to / vs /tmp
  • docker run --memory=64m alpine sh -c "dd if=/dev/zero of=/dev/null bs=1M" — start with 64 MB limit. Check with docker inspect --format='{{.State.OOMKilled}}' if the container was killed

#📌 Summary

  • Always set USER in Dockerfile — root in a container is a security risk
  • Minimize capabilities with --cap-drop ALL
  • Secrets don't belong in images, use environment variables or --secret
  • Resource limits (--memory, --cpus) prevent DoS from individual containers
← → to navigate