Build Container From Scratch - Linux Namespace - Episode 9

Published on 2025-04-15

« See All Lectures Contact Us
Build Container From Scratch - Linux Namespace - Episode 9

Introduction

This page contains the experiments used in the Build Containers from Scratch episode.

This episode is part of the Linux Namespace series.

The complete list of episodes can be found here:

  1. Episode 1: Introduction to Linux Namespaces
  2. Episode 2: Mount Namespace
  3. Episode 3: PID Namespace
  4. Episode 4: Network Namespace
  5. Episode 5: IPC Namespace
  6. Episode 6: UTS Namespace
  7. Episode 7: Cgroups Namespace
  8. Episode 8: User Namespace
  9. Episode 9: Build Container from Scratch (Current)

Experiment

Terminal 1 - root user

Terminal 2 - user

Terminal 3 - user

Go to Terminal 1

// Set up the bridge network for later root@evermight:~# ip link add name br0 type bridge; root@evermight:~# ip addr add 172.16.2.11/24 brd + dev br0; root@evermight:~# ip link set br0 up; root@evermight:~# iptables -A FORWARD -i br0 -j ACCEPT; root@evermight:~# sysctl -w net.ipv4.ip_forward=1 net.ipv4.ip_forward = 1 root@evermight:~# iptables -t nat -A POSTROUTING -s 172.16.2.0/24 -j MASQUERADE

Container 1

Go to Terminal 2

//// //// Download alpine for later evermight@evermight:~$ wget http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.1-x86_64.tar.gz 2025-02-14 21:38:11 (17.1 MB/s) - ‘alpine-minirootfs-3.10.1-x86_64.tar.gz’ saved [2711400/2711400] evermight@evermight:~$ mkdir alpine; evermight@evermight:~$ tar -xzf alpine-minirootfs-3.10.1-x86_64.tar.gz -C alpine; //// //// Create user namespace evermight@evermight:~$ id uid=1000(evermight) gid=1000(evermight) groups=1000(evermight),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd) evermight@evermight:~$ unshare -U -r /bin/bash nobody@evermight:~$ id uid=0(root) gid=0(root) //// //// Create uts namespace to change hostname nobody@evermight:~$ unshare --uts /bin/bash root@evermight:~# hostname container1 //// //// Create remaining namespaces root@evermight:~# unshare --pid --fork --mount-proc --net --ipc --cgroup /bin/bash root@container1:~# sleep 20

Go to Terminal 1

root@evermight:~# ps auxf USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 939 0.0 0.1 17180 10788 ? Ss 21:34 0:00 \_ sshd: evermight [priv] evermig+ 1045 0.1 0.0 17312 8060 ? S 21:35 0:00 | \_ sshd: evermight@pts/0 evermig+ 1046 0.0 0.0 8868 5816 pts/0 Ss 21:35 0:00 | \_ -bash evermig+ 1164 0.0 0.0 8792 5524 pts/0 S 21:38 0:00 | \_ /bin/bash evermig+ 1184 0.0 0.0 8792 5512 pts/0 S 21:39 0:00 | \_ /bin/bash evermig+ 1241 0.0 0.0 5772 992 pts/0 S 21:39 0:00 | \_ unshare --pid --fork --mount-proc -- evermig+ 1242 0.0 0.0 8792 5584 pts/0 S 21:39 0:00 | \_ /bin/bash evermig+ 1281 0.0 0.0 5772 1012 pts/0 S+ 21:41 0:00 | \_ sleep 20 //// //// Set up virtual ethernet pairs root@evermight:~# ip link add veth0 type veth peer name ceth0 root@evermight:~# ip link set ceth0 netns /proc/1242/ns/net; root@evermight:~# ip link set veth0 up; root@evermight:~# ip link set veth0 master br0; //// //// Apply 50% CPU max cgroups rule root@localhost:~# mkdir /sys/fs/cgroup/container_cpu root@localhost:~# echo '50000 100000' | sudo tee /sys/fs/cgroup/container_cpu/cpu.max 50000 100000 root@localhost:~# echo 1242 | sudo tee /sys/fs/cgroup/container_cpu/cgroup.procs

Go to Terminal 2

//// //// Set up virtual ethernet pairs root@container1:~# ip link set lo up root@container1:~# ip link set ceth0 up root@container1:~# ip addr add 172.16.2.20/24 dev ceth0 root@container1:~# ip route add default via 172.16.2.11 //// //// Set up file system isolation root@container1:~# mount --bind alpine alpine root@container1:~# cd alpine root@container1:~/alpine# mkdir root_fs root@container1:~/alpine# pivot_root . root_fs root@container1:~/alpine# umount -l root_fs root@container1:~/alpine# cd /

Experiments for Container 1:

//// Terminal 2 with internet access Terminal 2: ping 8.8.8.8 Terminal 1: tcpdump -i veth0 # confirm traffic Terminal 1: tcpdump -i eth0 # no traffic from namespace //// //// root@container1:/# echo 'Hello from container 1' > /hello.txt # make sure it is not the parent's /hello.txt //// //// root@container1:/# yes > /dev/null

Container 2

The same steps as Container 1

Go to Terminal 3

//// //// Make a new copy of alpine and clean up evermight@evermight:~$ ls alpine bin etc home media opt root run srv tmp var dev hello.txt lib mnt proc root_fs sbin sys usr evermight@evermight:~$ mkdir alpine2 evermight@evermight:~$ cp -r alpine/* alpine2/ evermight@evermight:~$ cd alpine2/ evermight@evermight:~/alpine2$ rm hello.txt evermight@evermight:~/alpine2$ rmdir root_fs evermight@evermight:~/alpine2$ cd .. //// //// Make user namespace evermight@evermight:~$ unshare -U -r /bin/bash nobody@evermight:~$ id uid=0(root) gid=0(root) nobody@evermight:~$ unshare --uts /bin/bash root@evermight:~# hostname container5 root@evermight:~# unshare --pid --fork --mount-proc --net --ipc --cgroup /bin/bash root@container2:~# sleep 25

Go to Terminal 1

root@evermight:~# ps auxf USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1304 0.0 0.1 17184 11064 ? Ss 21:46 0:00 \_ sshd: evermight [priv] evermig+ 1361 0.1 0.1 17316 8144 ? S 21:46 0:00 \_ sshd: evermight@pts/3 evermig+ 1362 0.0 0.0 8992 5780 pts/3 Ss 21:46 0:00 \_ -bash evermig+ 1396 0.0 0.0 8792 5492 pts/3 S 21:47 0:00 \_ /bin/bash evermig+ 1410 0.0 0.0 8792 5572 pts/3 S 21:48 0:00 \_ /bin/bash evermig+ 1417 0.0 0.0 5772 956 pts/3 S 21:48 0:00 \_ unshare --pid --fork --mount-proc -- evermig+ 1418 0.0 0.0 8792 5548 pts/3 S 21:48 0:00 \_ /bin/bash evermig+ 1425 0.0 0.0 5772 1008 pts/3 S+ 21:48 0:00 \_ sleep 25 //// //// Set up virtual ethernet pairs root@evermight:~# ip link add veth1 type veth peer name ceth1 root@evermight:~# ip link set ceth1 netns /proc/1418/ns/net; root@evermight:~# ip link set veth1 up; root@evermight:~# ip link set veth1 master br0; //// //// Apply 50% cpu max rule root@localhost:~# echo 1418 | sudo tee /sys/fs/cgroup/container_cpu/cgroup.procs

Go to Terminal 3

//// //// Set up virtual ethernet pairs root@container2:~# ip link set lo up; root@container2:~# ip link set ceth1 up; root@container2:~# ip addr add 172.16.2.21/24 dev ceth1 root@container2:~# ip route add default via 172.16.2.11 //// //// Set up file system isolation root@container2:~# mount --bind alpine2 alpine2 root@container2:~# cd alpine2 root@container2:~/alpine2# mkdir root_fs root@container2:~/alpine2# pivot_root . root_fs root@container2:~/alpine2# umount -l root_fs root@container2:~/alpine2# cd /

Experiments for Container 2:

//// Terminal 3 with internet access Terminal 3: ping 8.8.8.8 Terminal 1: tcpdump -i veth1 # confirm traffic Terminal 1: tcpdump -i veth0 # no traffic from namespace Terminal 1: tcpdump -i eth0 # no traffic from namespace //// //// root@container2:/# echo 'Hello from container 2' > /hello.txt # make sure it is not the parent's /hello.txt //// //// root@container2:/# yes > /dev/null