Today’s Lesson
Security for Legal SaaS — Episode 45: Docker Security and Container Hardening
Module 10 — Infrastructure and Deployment
Welcome to Module 10. For the next several episodes, we shift from application-level security to the infrastructure your application runs on — the containers, pipelines, dependencies, configuration, and cloud services that make up the deployment environment. A perfectly secure application deployed on poorly configured infrastructure is still vulnerable. We start with containers.
What Is a Container, and Why Should You Care?
A container is a lightweight, portable package that bundles your application with everything it needs to run — the code, the runtime, the libraries, the configuration files. Think of it as a sealed shipping container for software: everything required is packed inside, isolated from whatever else is running on the same server. Docker is the most widely used container platform, though the underlying technology is built on Linux kernel features — namespaces for isolation and cgroups for resource limits.
Containers solve a real problem for legal SaaS teams. "It works on my machine" becomes irrelevant when the container is identical everywhere — development, testing, production. Your contract review tool runs the same way on a developer's laptop and on your production servers.
But containers are not virtual machines. They share the host operating system's kernel. This means a vulnerability in the kernel can affect every container on the host. NIST SP 800-190, the Application Container Security Guide 1, identifies this shared kernel as the fundamental security consideration: "Containers provide less isolation than virtual machines since they share the host OS kernel."
Critical mindset: A container is not a security boundary. But a well-configured container is a useful speed bump — it limits what an attacker can do if they compromise your application. The goal is defence in depth (layering multiple independent controls, as we covered in Episode 4), not relying on any single layer.
Base Image Selection — Your Foundation Matters
Every container starts from a base image — a starting template that includes the operating system and core libraries. The most common mistake is using `ubuntu:latest` or `node:latest` as your base.
The problems:
- `latest` is a moving target. Today's `latest` is different from last month's. Your build is not reproducible.
- Full OS images include hundreds of packages you don't need. Each package is an additional attack surface — software that could contain vulnerabilities.
- Unpinned images cannot be audited. You cannot prove what was in the image at deployment time.
The CIS Docker Benchmark 2 — a set of security configuration guidelines published by the Center for Internet Security — recommends:
| Practice | Why |
|---|---|
| Use minimal base images (`alpine`, `distroless`, `chainguard`) | Fewer packages = smaller attack surface |
| Pin images by digest, not tag | `node:20-alpine@sha256:abc123...` is immutable; `node:20-alpine` can change |
| Rebuild regularly | Even pinned images accumulate vulnerabilities over time |
| Scan before deployment | Catch known CVEs (Common Vulnerabilities and Exposures — standardised vulnerability identifiers) before they reach production |
For a legal SaaS platform, the difference is stark. A full `node:20` image contains over 900 packages. A `node:20-alpine` image contains around 30. Every eliminated package is one fewer thing that can go wrong.
Running as Non-Root — The Single Most Important Change
By default, processes inside a Docker container run as root — the most privileged user on a Linux system. If an attacker exploits a vulnerability in your application and escapes the container, they inherit root privileges on the host. Game over.
Docker security hardening guides 3 consistently rank running as non-root as the single most impactful security improvement:
# In your Dockerfile
RUN addgroup --system appgroup && adduser --system appuser --ingroup appgroup
USER appuser
The `USER` instruction switches the container's runtime user to a non-privileged account. If an attacker escapes the container, they land as an unprivileged user — still bad, but dramatically less catastrophic than root.
NIST SP 800-190 1 explicitly recommends: "Organizations should prohibit containers from running as root whenever possible."
Read-Only Filesystems, Dropped Capabilities, and Seccomp
Beyond running as non-root, three additional hardening layers significantly reduce what a compromised container can do:
Read-Only Filesystem
Mount the container's filesystem as read-only. If an attacker gains code execution inside the container, they cannot write malicious files, modify binaries, or plant persistence mechanisms.
docker run --read-only --tmpfs /tmp myapp
The `--tmpfs /tmp` flag provides a writable temporary directory in memory for applications that need it, while keeping the rest of the filesystem immutable.
Dropped Capabilities
Linux capabilities are fine-grained permissions that break root's all-or-nothing power into individual privileges. By default, Docker grants containers a set of capabilities that most applications never need. The CIS Docker Benchmark 2 recommends dropping all capabilities and adding back only what your application requires:
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp
This container can bind to privileged ports (below 1024) but cannot do anything else that requires elevated privileges — no raw network access, no kernel module loading, no changing file ownership.
Seccomp Profiles
Seccomp (Secure Computing Mode) restricts which system calls — low-level requests to the operating system kernel — a container can make. Docker's default seccomp profile 4 blocks approximately 44 of the 300+ available system calls, including dangerous ones like `reboot`, `mount`, and `clock_settime`. For higher security, create a custom profile that allows only the specific system calls your application uses.
Layered defence: Non-root user + read-only filesystem + dropped capabilities + seccomp profile = four independent barriers. An attacker who breaches one still faces three more. This is defence in depth applied to container configuration.
Secrets in Containers — Never Bake, Always Inject
Secrets — API keys, database passwords, encryption keys — must never be baked into container images. An image is a static artifact. Anyone with access to the image (your container registry, your CI/CD pipeline logs, your developer's laptop) can extract every secret embedded in it.
GitGuardian's research 5 found that container images and CI/CD configuration files are among the primary vectors for secret exposure, with millions of secrets discovered in public repositories annually.
The correct approach:
| Method | How It Works | Security Level |
|---|---|---|
| Environment variables | Injected at runtime via orchestrator (Kubernetes Secrets, Docker Compose) | Baseline — visible in process listings |
| Mounted secrets | Files mounted into the container at runtime from a secrets manager | Better — not visible in process listings |
| Vault integration | Application fetches secrets from HashiCorp Vault (introduced in Episode 15) at startup | Best — secrets are short-lived, rotated, audited |
For legal SaaS platforms handling privileged client data, Vault integration is the recommended approach. Secrets are never stored on disk, are automatically rotated, and every access is logged.
Container Scanning — Catching Vulnerabilities Before Deployment
Container images accumulate vulnerabilities. A base image that was clean last month may have three new critical CVEs today. Scanning tools analyse your images against vulnerability databases and report known issues before they reach production.
Docker's own security scanning 6 and third-party tools like Trivy, Grype, and Snyk Container check every layer of your image against the National Vulnerability Database and other sources. The CIS Docker Benchmark 2 recommends scanning images both in your CI/CD pipeline (before they're published to a registry) and continuously in your registry (because new vulnerabilities are discovered after deployment).
A practical scanning policy for legal SaaS:
- Block critical and high CVEs — no image with a known critical vulnerability reaches production
- Set fix deadlines for medium CVEs — 30 days to patch or document an exception
- Rebuild base images weekly — pick up security patches automatically
- Scan running containers — detect vulnerabilities discovered after deployment
Aqua Security's CIS Docker Benchmark guide 7 notes that many organizations scan images during build but forget to rescan running containers — leaving a window where newly discovered vulnerabilities go undetected.
Docker Bench for Security — Auditing Your Configuration
Docker Bench for Security 8 is an open-source script that audits your Docker installation against the CIS Docker Benchmark. It checks over 100 configuration items including:
- Host configuration (kernel parameters, audit rules)
- Docker daemon configuration (TLS, logging, user namespaces)
- Container runtime settings (capabilities, read-only root, seccomp)
- Image and build file best practices
Run it regularly. Treat failures as security bugs. For a legal SaaS platform where data protection obligations apply — ABA Model Rule 1.6(c), GDPR Article 32's requirement for "appropriate technical measures," SOC 2 infrastructure controls — a clean Docker Bench report is evidence of due diligence.
Key takeaway: Containers are not inherently secure. They are as secure as you configure them. Minimal base images, non-root users, read-only filesystems, dropped capabilities, seccomp profiles, runtime secret injection, and continuous scanning — together, these transform a container from a convenient packaging format into a meaningful security layer. Skip any one of them, and you've left a door open.
What's Next
Next episode, we turn to the pipeline that builds and deploys those containers — CI/CD pipeline security. Your CI/CD system has production credentials and runs arbitrary code. It is the most powerful system you own. We'll talk about how to treat it that way.
Sources & references
- NIST SP 800-190 — Application Container Security Guide
- CIS Docker Benchmark — Center for Internet Security
- Docker Security 2025: Hardening Containers — OnlineHashCrack
- Docker Seccomp Security Profiles — Docker Documentation
- CI/CD Secrets Management — GitGuardian
- CIS Benchmark — Docker Documentation
- Docker CIS Benchmark: Best Practices — Aqua Security
- Docker Bench for Security — GitHub
- Docker Security Hardening: Rootless, Seccomp, AppArmor — Virtua Cloud
- Scanning Docker Infrastructure Against CIS Benchmark — Wazuh
- CIS Benchmarks for Docker Security — Sathiya Shunmugasundaram