Listen to this Post

Introduction:
Containers have become the backbone of modern cloud-native applications, yet their inner workings often remain a black box to many developers and security professionals. By re-implementing a basic container runtime from scratch, one engineer discovered that Docker is not magic—it is a sophisticated composition of decades-old Linux kernel primitives like namespaces, cgroups, and layered filesystems. Understanding these core components is essential for effective container security, threat modeling, and infrastructure hardening.
Learning Objectives:
- Understand the Linux kernel features (namespaces, cgroups, overlayfs) that enable containerization.
- Learn how to manually configure and interact with these primitives using command-line tools.
- Gain insight into container networking fundamentals using virtual Ethernet pairs and bridges.
- Identify security boundaries and potential misconfigurations in container runtimes.
- Apply hands-on commands to inspect, limit, and debug containerized environments.
You Should Know:
1. Linux Namespaces: The Foundation of Isolation
At the heart of every container are Linux namespaces, which isolate global system resources so that processes inside the container see their own view of the system. When you run a container, the runtime creates new namespaces for the process.
Step‑by‑step guide explaining what this does and how to use it:
To see this in action without Docker, you can manually launch a process in a new namespace using the `unshare` command.
Run a bash shell in a new mount, PID, and network namespace sudo unshare --mount --pid --net --fork bash Inside the new namespace, check the processes ps aux You will notice only a few processes, as the PID namespace is isolated. Check the network interfaces ip link You will see only a loopback interface (lo), isolated from the host.
To see all namespaces on your system and which processes belong to them:
lsns This lists all current namespaces and their types (net, mnt, pid, etc.)
You can also enter an existing container’s namespace from the host for debugging:
Find the PID of a container process (e.g., from <code>docker inspect</code>) Then enter its network namespace nsenter -t <PID> -n ip addr
This shows that a container is simply a group of processes running within a specific set of namespaces. From a security perspective, misconfigured namespace sharing (e.g., sharing the host’s PID or network namespace) can severely weaken isolation.
2. Control Groups (cgroups v2): Enforcing Resource Limits
Namespaces provide isolation, but they do not limit resource usage. That is the job of cgroups. Cgroups allow you to restrict CPU, memory, and I/O for a group of processes.
Step‑by‑step guide explaining what this does and how to use it:
On a system with cgroups v2 (check with mount | grep cgroup), you can create a child cgroup and set limits.
Create a new cgroup for a demo container sudo mkdir /sys/fs/cgroup/container-demo Check the default settings cat /sys/fs/cgroup/container-demo/cpu.max cat /sys/fs/cgroup/container-demo/memory.max Set a memory limit of 256MB (256 1024 1024 = 268435456 bytes) echo 268435456 | sudo tee /sys/fs/cgroup/container-demo/memory.max Set a CPU limit (e.g., 50% of one core: 50000 out of 100000) echo "50000 100000" | sudo tee /sys/fs/cgroup/container-demo/cpu.max Now, run a process and add its PID to the cgroup.procs file sudo bash -c "echo $$ >> /sys/fs/cgroup/container-demo/cgroup.procs" Any child processes will also be subject to these limits.
You can then run a stress test (e.g., stress --vm-bytes 300M --vm-keep -m 1) to see the kernel enforce the limit. In production, misconfigured or missing cgroup limits can lead to resource exhaustion attacks (DoS) from a compromised container.
3. OverlayFS: Writable Container Layers
Docker images are built from read-only layers. When you run a container, OverlayFS merges these layers and adds a thin writable layer on top. This is why changes inside a container do not affect the base image.
Step‑by‑step guide explaining what this does and how to use it:
You can manually create an overlay mount to understand the mechanism.
Create directories for the lower (base), upper (changes), work, and merged layers
mkdir -p ~/overlay-demo/{lower,upper,work,merged}
Populate the lower layer with a sample file
echo "Hello from base image" > ~/overlay-demo/lower/base.txt
Mount the overlay
sudo mount -t overlay overlay -o lowerdir=~/overlay-demo/lower,upperdir=~/overlay-demo/upper,workdir=~/overlay-demo/work ~/overlay-demo/merged
Check the merged view
cat ~/overlay-demo/merged/base.txt
Create a new file in the merged directory
echo "New file from container" > ~/overlay-demo/merged/new.txt
Check the upper layer - the changes are stored here
ls ~/overlay-demo/upper/
The lower layer (base image) remains untouched.
When a file is deleted from the merged view, a “whiteout” file is created in the upper layer to hide it. Understanding this helps in forensic analysis of compromised containers and in designing secure image build processes.
4. Bridge Networking: Connecting Containers
Containers on the same host communicate via a virtual network bridge. This is implemented using a Linux bridge and virtual Ethernet (veth) pairs.
Step‑by‑step guide explaining what this does and how to use it:
Here’s how to manually create a bridge and connect a network namespace to it.
Create a bridge sudo ip link add name br0 type bridge sudo ip link set br0 up sudo ip addr add 172.18.0.1/24 dev br0 Create a network namespace (simulating a container) sudo ip netns add container1 Create a veth pair sudo ip link add veth0 type veth peer name veth1 Attach one end (veth1) to the bridge sudo ip link set veth1 master br0 sudo ip link set veth1 up Move the other end (veth0) into the container's namespace sudo ip link set veth0 netns container1 Inside the container namespace, configure the interface sudo ip netns exec container1 ip link set lo up sudo ip netns exec container1 ip link set veth0 up sudo ip netns exec container1 ip addr add 172.18.0.10/24 dev veth0 sudo ip netns exec container1 ip route add default via 172.18.0.1 From the host, you can now ping the container ping -c 2 172.18.0.10
This demonstrates that container networking is simply standard Linux bridging. Security implications include the potential for ARP spoofing or MAC flooding on the bridge if not properly secured (e.g., using `ebtables` or `iptables` to restrict traffic).
5. Interactive Mode and Pseudo-Terminals (PTY)
The `-it` flag in Docker does more than just keep STDIN open. It allocates a pseudo-terminal (PTY) for the container, which is necessary for interactive applications like a shell to handle signals and job control correctly.
Step‑by‑step guide explaining what this does and how to use it:
You can replicate this behavior using `socat` or by manually creating a PTY.
Create a new PTY and attach a shell to it sudo socat PTY,link=/tmp/docker-pty,raw,echo=0 EXEC:/bin/bash In another terminal, connect to that PTY sudo screen /tmp/docker-pty
Inside a running container, you can inspect the PTY device:
Find a container's PID and list its open file descriptors
docker inspect --format '{{.State.Pid}}' <container-id>
sudo ls -l /proc/<PID>/fd/
You will see a symlink to /dev/pts/ for the TTY.
From a security standpoint, if a container is run with a TTY allocated and is compromised, an attacker might have a more stable interactive shell, but the main risk lies in escape vulnerabilities that leverage TTY driver bugs.
6. Putting It All Together: The Container Runtime
The original post’s project, Xocker, combines all these primitives: unshare for namespaces, cgroupfs for limits, pivot_root for filesystem isolation, and veth pairs for networking.
Step‑by‑step guide explaining what this does and how to use it:
To clone and test a similar minimal runtime:
git clone https://github.com/truongnhatanh7/xocker.git cd xocker make sudo ./xocker run -it --memory 256 --cpus 0.5 alpine sh
This command will:
- Create new namespaces (using `unshare` or `clone` with flags).
- Set up cgroups for the process.
- Prepare the root filesystem using OverlayFS.
- Configure networking in the container’s namespace.
- Execute the shell with a PTY.
Examining the code (written in Go) reveals how each syscall is invoked, providing a clear map of the attack surface for container escapes and privilege escalation.
What Undercode Say:
- The perceived complexity of Docker is an illusion; it is a masterful orchestration of fundamental Linux components. For defenders and attackers alike, this means that vulnerabilities in containers are often vulnerabilities in the Linux kernel itself, misapplied.
- Security in containerized environments hinges on correctly configuring these underlying primitives. A simple misstep, such as running a container with `–privileged` or sharing the host’s namespaces, dismantles the isolation that namespaces and cgroups provide, directly exposing the host.
Key Takeaway 1: Master the primitives to master the platform. You cannot secure what you do not understand. Knowing how to manually create namespaces, set cgroup limits, and build bridges demystifies the stack and reveals the true boundaries of trust.
Key Takeaway 2: The container runtime is a security-critical component. Any bug in the runtime’s handling of these syscalls (like the CVE-2022-0492 cgroup escape) can lead to host compromise. Therefore, keeping the container runtime (runc, containerd) updated is as important as patching the kernel.
Prediction:
As infrastructure continues to evolve, we will see a shift towards “microVMs” (like Firecracker) and “confidential containers” that add a hardware-enforced isolation layer beyond traditional namespaces. This is a direct response to the realization that while Linux namespaces are powerful, the shared kernel model remains a single point of failure for multi-tenant environments. The future of container security lies in minimizing the kernel’s attack surface and moving isolation left, back to the hardware.
▶️ Related Video (84% Match):
🎯Let’s Practice For Free:
IT/Security Reporter URL:
Reported By: Anh Truong – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅


