Code

View on GitHub

Host example #

make demo
./demo --sleep

demo.c

ps aux | grep demo

Display process namespace association:

ls -l /proc/$(pidof demo)/ns

Compare namespaces with your shell:

ls -l /proc/$$/ns

Build docker image #

docker build -t demo .
docker image list demo
docker run demo
docker run -it --name demo_sleep --rm demo --sleep

Note PID = 1 here. However, from host perspective there is a regular process demo with a high PID:

ps aux | grep demo

docker top lists processes within a single container:

docker top demo_sleep

docker ps lists containers:

docker ps -a

Inspect containerized process namespace association:

HOST_PID=$(docker inspect -f '{{.State.Pid}}' demo_sleep)
echo $HOST_PID
sudo ls -l /proc/$HOST_PID/ns/

Note the differences from what we’ve seen before.

PID Namespace #

Containerized processes see only other processes within the same container.

Try running forking process and observe PIDs:

docker run -it --rm demo --fork

Try running it multiple times in parallel.

Containers cannot access host PIDs.

HOST_PID=$$
echo $HOST_PID
docker run -it --rm demo --kill $HOST_PID

DIY PID namespace #

make pidns

pidns.c

sudo ./pidns

Note that child PID from perspective of parent is different from what child sees.

NET namespace #

./demo --if

Try listing container interfaces:

docker run -it --rm demo --if

Now bind port 8080 within host and then within the container:

nc -u -l 8080
docker run -it --rm --name=demo_udp demo --udp

Send datagram to host:

echo "Hello Host" | nc -u -w1 127.0.0.1 8080

And to the container:

CONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' demo_udp)
echo "Container IP: $CONTAINER_IP"
echo "Hello Container" | nc -u -w1 $CONTAINER_IP 8080

See interfaces from the container:

docker exec demo_udp ip a

Note network namespaces created by docker live in a different directory compared to the standard ones created by ip netns tool. You can find them with:

SANDBOX_KEY=$(docker inspect -f '{{.NetworkSettings.SandboxKey}}' demo_udp)
echo $SANDBOX_KEY

Port exporting #

docker run -it --rm --name=demo_cpu -p 80:8080/udp --cpus="0.1" demo --udp

Show iptables rules:

sudo iptables -t nat -L DOCKER -n

Show auxiliary process:

ps aux | grep docker-proxy

Send something:

echo "Hello!" | nc -u -w1 localhost 80

MNT Namespace #

docker run -it --name demo_fs demo --write
ls -l /tmp/container_secret.txt
HOST_PID=$(docker inspect -f '{{.State.Pid}}' demo_fs)
sudo cat /proc/$HOST_PID/mountinfo | grep '/ / '
HOST_PID=$(docker inspect -f '{{.State.Pid}}' demo_fs)
UPPER_DIR=$(sudo cat /proc/$HOST_PID/mountinfo | grep '/ / ' | grep -o 'upperdir=[^,]*' | cut -d= -f2)
LOWER_DIR=$(sudo cat /proc/$HOST_PID/mountinfo | grep '/ / ' | grep -o 'lowerdir=[^,]*' | cut -d= -f2)
echo $UPPER_DIR
echo $LOWER_DIR
HOST_PID=$(docker inspect -f '{{.State.Pid}}' demo_fs)
UPPER_DIR=$(sudo cat /proc/$HOST_PID/mountinfo | grep '/ / ' | grep -o 'upperdir=[^,]*' | cut -d= -f2)
sudo tree $UPPER_DIR
docker container rm demo_fs

DIY virtual root #

make mntns
sudo ./mntns ./root

Bind mounts #

docker run -it --name demo_bind -v ./data:/data demo --write /data/trwaly_sekret.txt
HOST_PID=$(docker inspect -f '{{.State.Pid}}' demo_bind)
sudo cat /proc/$HOST_PID/mountinfo | grep '/data'
stat ./data/trwaly_sekret.txt
cat ./data/trwaly_sekret.txt
sudo rm -Rf ./data
docker rm -f demo_bind

Control Groups #

docker run -it --rm -m 50m demo --mem
CONTAINER_ID=$(docker inspect -f '{{.Id}}' demo_sleep)
echo $CONTAINER_ID
ls /sys/fs/cgroup/system.slice/docker-$CONTAINER_ID.scope/
docker run -it --rm --name=demo_cpu --cpus="0.1" demo --cpu
HOST_PID=$(docker inspect -f '{{.State.Pid}}' demo_cpu)
htop -p $HOST_PID
CONTAINER_ID=$(docker inspect -f '{{.Id}}' demo_cpu)
echo $CONTAINER_ID
cat /sys/fs/cgroup/system.slice/docker-$CONTAINER_ID.scope/cpu.max

Cleanup #

Ensure there are no leftovers:

docker container ps

Stop if anything is still running with docker kill.

docker container prune