
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:
- Episode 1: Introduction to Linux Namespaces
- Episode 2: Mount Namespace
- Episode 3: PID Namespace
- Episode 4: Network Namespace
- Episode 5: IPC Namespace
- Episode 6: UTS Namespace
- Episode 7: Cgroups Namespace
- Episode 8: User Namespace
- 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