Docker Security: Dissecting Namespaces, cgroups, and the Art of Misconfiguration
6 min read
May 25, 2025

Table of contents
TL;DR
- Understand how Docker containers isolate processes with namespaces, cgroups, and OverlayFS.
- See why isolation ≠ security, and where common misconfigurations open real attack paths.
- Meet Valeris, a Rust CLI that audits running containers for dangerous defaults.
- Walk away with a checklist to keep your next deploy out of the breach headlines.
Welcome to the Series
If you’ve followed my work, you know I love application security testing. Lately, more clients rely on Docker and Kubernetes, yet I hadn’t explored the internals deeply until now.
To make this journey practical (and fun) I’m building Valeris, a Rust‑based CLI that scans running containers for misconfigurations. It’s part learning project, part real‑world utility and you get to watch (or help!) while I build.
In this first chapter:
- You’ll learn what Docker is and how It works.
- I’ll introduce Valeris and show you how it works.
- And we’ll look at some common misconfigurations it can already detect.
Let’s go.
What Is Docker?
Docker is a platform that lets you package an app, its dependencies, and its environment into a container: a lightweight “virtual box” that shares the host’s kernel but runs in its own isolated space.
But what does that really mean?
When you run a Docker container, you’re not launching a full operating system like you would with a virtual machine. Instead, you're starting a regular process on the host... but with some clever isolation tricks applied. These tricks are built into the Linux kernel and revolve around three key mechanisms:
- Namespaces – what a process can see
- Control groups (cgroups) – what a process can use
- Overlay (Union) File Systems – what a process can write
Let’s break them down.
Namespaces – The illusion of being alone
Namespaces are like tinted glasses for processes. They limit what a process can see or interact with in the system.
Think of them as "personal illusions" applied to a process:
it thinks it's alone, but really it’s just been put in a carefully crafted sandbox.
Each namespace type controls a different part of that illusion:
Namespace | What it isolates | Analogy |
---|---|---|
pid |
Process IDs (PIDs) | The process thinks it’s PID 1 (like init) |
net |
Network interfaces, IPs, routes | Gets its own “private” network |
mnt |
Mount points / filesystems | Sees only its own mounted directories |
uts |
Hostname and domain name | Can call itself vuln-container.local |
ipc |
Shared memory (semaphores, etc.) | Can’t talk to other processes via shared mem |
user |
UID/GID mappings | Can map its own root user differently | cgroup |
cgroup membership | Hides host resource limits |
Without namespaces:
- A process sees all other running processes.
- It shares the same network as the host.
- It can access the same filesystem mounts.
With namespaces:
- A process only sees its own little world.
- It thinks it's PID 1 (like a fresh OS).
- Its network is isolated, its mounts are separated, and its hostname can be totally fake.
And how does Docker apply this?
Docker uses a Linux syscalls called clone(2)
and unshare(2)
under the hood to create new namespaces when launching a container.
So when you type:
docker run debian:stable-slim
Docker is actually doing something like:
"Hey kernel, create a new process, but isolate it using these namespaces: PID, NET, MNT, UTS, USER… and maybe don’t tell it it’s just a guest."
That’s how each container ends up with its own environment, while still running on the same kernel as everything else.
Control Groups (cgroups) – Resource management for grown-ups
While namespaces handle what a process can see, control groups (cgroups) control what a process can use.
Think of cgroups as the resource police:
“You get two CPU cores, 512MB of RAM, and that’s it. No exceptions.”
In simple terms, cgroups limit, measure, and isolate resource usage of processes or groups of processes.
Here’s what you can control:
Resource | Example usage |
---|---|
CPU | Limit a container to 1 core, or 50% CPU time |
Memory | Cap RAM usage (and trigger OOM when exceeded) |
I/O | Limit disk read/write speeds |
PIDs | Limit how many processes it can spawn |
Network | Limit bandwidth (with more advanced setups) |
Each cgroup is like a sandbox with a budget: it doesn’t care what the app is doing, but it ensures the app can’t exceed what it's allowed.
OverlayFS - Layer cake for containers
While namespaces isolate what a process sees, and cgroups limit what a process uses,
the union file system makes sure containers don’t accidentally trash your disk (or each other’s).
That’s right Docker containers don’t get a blank hard drive every time they start. Instead, Docker uses OverlayFS (a type of Union FS) to simulate a complete filesystem by stacking multiple layers.
Building a Docker container is like assembling a cake:
- 🎂 At the bottom: a read-only base image (e.g.
debian:stable
). - 🧁 Next layer: your installed packages (
nginx
,python
, etc.). - 🍒 Then: your app files and configurations.
- ✍️ At the top: a writable layer that's unique to this running container.
When your container runs:
- It reads files by scanning top-down through the layers.
- It writes by copying files into the top writable layer only.
This technique is called copy-on-write, and it means:
- Base layers remain untouched,
- Changes are container-specific,
- And teardown is fast, just discard the top layer.
This is useful because thanks to this Docker can:
- Reuse the same base images efficiently.
- Build fast, thanks to cached and layered image construction.
- Stay isolated even if 10 containers use
debian
, they don’t overwrite each other. - Can be destroyed and rebuilt instantly, because only the writable layer is ephemeral.
Without this, each container would be a complete copy, slow, heavy, and repetitive.
Automating the Hunt for Misconfigurations
Now that we’ve covered the basics of how Docker works, let’s talk about the real reason we're here: misconfigurations.
As you might imagine, there are plenty of ways to configure a container poorly, making it vulnerable to privilege escalation, data leaks, or even host compromise. Some classic examples include:
- Running the container as root (no user restrictions at all),
- Using the
--privileged
flag (which disables most security protections), - Mounting sensitive directories like
/proc
or/var/run/docker.sock
, - Leaking secrets through environment variables like
API_KEY
,DB_PASSWORD
, or AWS credentials.
So yeah Docker’s isolation isn’t magic. And misconfigurations are surprisingly common, especially in fast-paced environments where security isn't always a priority.
That’s why I wanted to create a tool that could automate the process of identifying these weak configurations. Not just to save time, but to help me learn the ins and outs of container security along the way.
Yes, I know there are excellent tools out there already like Trivy, Dockle, and docker-bench-security and they’re incredibly powerful. But I strongly believe that building something from scratch gives you a deeper understanding of how things really work. And honestly? It’s more fun that way.
What is Valeris?
Valeris is a CLI tool I’m building in Rust to audit Docker (and soon Kubernetes) containers. It scans running containers for misconfigurations that could lead to real security issues. Things like root containers, excessive privileges, dangerous mounts, and exposed secrets.
The best part? I’m building it from scratch as I learn, and documenting the process in this blog series. It’s both a learning journey and (hopefully) a tool that can be genuinely useful.
Valeris is designed with a plugin-based architecture, which means:
- It’s easy to extend as I learn more.
- Each plugin can focus on a specific risk (e.g., user privileges, mounts, networking).
- It makes it easier to explain concepts clearly in the blog, one layer at a time.
What Valeris Can Already Do
Right now, Valeris can help with a few key checks:
- Detect if a container is running as the root user.
- Check if it’s mounting sensitive folders like
/proc
or/sys
. - Report network exposure, such as open ports or host networking.
- Find environment variables that contain common secret patterns.
It’s still early-stage, but even these basic checks already help streamline container audits.
Why build this in Rust?
Glad you asked. I chose Rust because:
- I wanted to learn it in a real-world context.
- It gives me a tool that’s fast, memory-safe, and future-proof.
- It’s widely used in Web3 and infrastructure tooling, which aligns with my interests.
- And honestly? Rust makes you write better code even if it makes you suffer a little at first.
Also: building a tool while learning is painful… but extremely rewarding.
Wrapping Up
So, Docker is powerful but also dangerously easy to misconfigure. In this first chapter, we’ve seen what containers really are, why isolation isn't bulletproof, and how tools like Valeris can help flag the things that most people forget (or ignore) when deploying.
But this is just the beginning.
What’s Next?
In the next chapter:
- We'll create a vulnerable on purpose container setup.
- We’ll scan it with Valeris and walk through the results.
- And we’ll dig into how the plugin system works under the hood how each security check is actually performed.
Useful links
- 🔗 GitHub Repo
- 📝 Blog + upcoming chapters
- ⭐ Give it a star if you like the project
Resources
Docker. “Docker Engine Security Documentation.” Available at: https://docs.docker.com/engine/security/
man7.org. “namespaces(7) – Linux Manual Page.” Available at: https://man7.org/linux/man-pages/man7/namespaces.7.html
The Linux Kernel Documentation. “Control Group v2.” Available at: https://docs.kernel.org/admin-guide/cgroup-v2.html
The Linux Kernel Documentation. “Overlay Filesystem.” Available at: https://docs.kernel.org/filesystems/overlayfs.html
Aqua Security. “Trivy” Available at: https://trivy.dev/latest/
Good with Tech. “Dockle: Container Image Linter for Security.” Available at: https://github.com/goodwithtech/dockle
Docker. “Docker Bench for Security.” Available at: https://github.com/docker/docker-bench-security