Linux namespaces
π§© 1. Mount Namespace (mnt)
Isolates: Mount points and filesystems.
Each namespace can have its own view of the filesystem (mounted directories, devices, etc.).
When you chroot, you change the root directory; with mount namespaces, you change the entire filesystem view.
Commonly used in containers so each one has its own /, /proc, /dev, etc.
π§ Example:
| unshare --mount
mount --make-private /
mount -t tmpfs none /mnt/test
|
π 2. Network Namespace (net)
Isolates: Network interfaces, routing tables, iptables, and network stack.
Each namespace has its own interfaces, IP addresses, routes, ARP tables, and firewall rules.
Used in all container engines and network simulations (like your labs).
π§ Example:
| ip netns add ns1
ip netns exec ns1 ip addr
|
π€ 3. UTS Namespace (uts)
Isolates: Hostname and NIS domain name.
Each namespace can have its own hostname β useful so containers show their own name when you run hostname.
π§ Example:
| unshare --uts /bin/bash
hostname my-container
|
βοΈ 4. PID Namespace (pid)
Isolates: Process IDs and process trees.
Processes in one PID namespace donβt see (or affect) processes in another.
The process in the new namespace sees itself as PID 1 (like init).
π§ Example:
| unshare --pid --fork --mount-proc /bin/bash
ps aux
|
β Only shows processes inside that namespace.
π₯ 5. User Namespace (user)
Isolates: User and group IDs (UIDs/GIDs).
Lets processes run as root inside a namespace but mapped to an unprivileged user outside it.
Provides strong privilege isolation.
π§ Example:
| unshare --user --map-root-user /bin/bash
id
|
β You appear as UID 0 inside, but youβre not root globally.
πΎ 6. IPC Namespace (ipc)
Isolates: System V IPC and POSIX message queues.
Each namespace has its own shared memory segments, semaphores, and message queues.
Prevents processes from interfering with each other via shared memory.
π§ Example:
| unshare --ipc /bin/bash
ipcs
|
β±οΈ 7. Time Namespace (time)
Isolates: System clocks.
Each namespace can have its own view of system time (since Linux 5.6).
Useful for testing software depending on time without changing the host clock.
π§ Example:
| unshare --time /bin/bash
date
|
βοΈ Summary Table
| Namespace |
Flag |
Isolates |
Example Command |
| Mount |
CLONE_NEWNS |
Filesystem mount points |
unshare --mount |
| UTS |
CLONE_NEWUTS |
Hostname |
unshare --uts |
| IPC |
CLONE_NEWIPC |
System V & POSIX IPC |
unshare --ipc |
| PID |
CLONE_NEWPID |
Process IDs |
unshare --pid --fork |
| Network |
CLONE_NEWNET |
Network stack |
ip netns add ns1 |
| User |
CLONE_NEWUSER |
User & group IDs |
unshare --user |
| Time |
CLONE_NEWTIME |
System clocks |
unshare --time |
π§ How They Work Together
When you launch a container (e.g., Podman or Kubernetes pod), itβs typically created with a combination of these:
| Namespace |
Used For |
| UTS |
Give container its own hostname |
| PID |
Hide host processes |
| NET |
Separate network stack |
| MNT |
Separate filesystem mounts |
| USER |
Map root inside container to non-root on host |
| IPC |
Isolate shared memory |
| TIME |
Optional (for time virtualization) |
Lab: Build a Mini-Container with unshare + named netns
Topology
| Host ns Mini-container ns (named: mini)
--------- --------------------------------
veth-host 10.200.0.1/30 β veth-mini 10.200.0.2/30
|
Prereqs (openSUSE/most Linux)
- Packages: iproute2, util-linux (for unshare, nsenter), iputils, iproute2, tcpdump (optional)
- Run the setup as a regular user (user namespaces allow root inside without host root).
One-file script: mini_container.sh
| #!/usr/bin/env bash
set -euo pipefail
# Mini-container names/addresses
NS_NAME="mini"
VETH_HOST="veth-host"
VETH_CONT="veth-mini"
HOST_IP="10.200.0.1/30"
CONT_IP="10.200.0.2/30"
usage() {
cat <<EOF
Usage: $0 {setup|shell|status|cleanup}
setup : Create netns, veth, IPs, routes; enable forwarding on host
shell : Enter the mini-container with USER, PID, UTS, IPC, MNT namespaces
(root inside, isolated PID tree, private mounts, /proc)
status : Show brief status of links and IPs
cleanup : Remove namespace and interfaces
EOF
}
require() {
command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1"; exit 1; }
}
setup() {
# deps
for bin in ip unshare ss; do require "$bin"; done
# Create netns if not exists
if ! ip netns list | grep -q "^${NS_NAME}\b"; then
sudo ip netns add "${NS_NAME}"
fi
# Create veth if not exists
if ! ip link show "${VETH_HOST}" >/dev/null 2>&1; then
sudo ip link add "${VETH_HOST}" type veth peer name "${VETH_CONT}"
fi
# Move container end to netns
sudo ip link set "${VETH_CONT}" netns "${NS_NAME}"
# Assign IPs
sudo ip addr flush dev "${VETH_HOST}" || true
sudo ip addr add "${HOST_IP}" dev "${VETH_HOST}"
sudo ip link set "${VETH_HOST}" up
sudo ip netns exec "${NS_NAME}" ip addr flush dev "${VETH_CONT}" || true
sudo ip netns exec "${NS_NAME}" ip addr add "${CONT_IP}" dev "${VETH_CONT}"
sudo ip netns exec "${NS_NAME}" ip link set "${VETH_CONT}" up
sudo ip netns exec "${NS_NAME}" ip link set lo up
# Optional: allow the container to reach host services & vice versa (same /30 so already routed)
# Enable IP forwarding on host for later experiments (not strictly required for /30 link)
echo "Enabling IPv4 forwarding on host (persist only for this session)"
sudo sysctl -w net.ipv4.ip_forward=1 >/dev/null
echo "Setup complete."
status
}
status() {
echo "---- Host ----"
ip addr show dev "${VETH_HOST}" || true
echo
echo "---- ${NS_NAME} ----"
ip netns exec "${NS_NAME}" ip addr show dev "${VETH_CONT}" || true
echo
echo "Ping test from mini -> host:"
if ip netns exec "${NS_NAME}" ping -c1 -W1 10.200.0.1 >/dev/null 2>&1; then
echo " OK"
else
echo " (not reachable yet)"
fi
}
shell() {
# Enter the named NET namespace, then unshare the rest to simulate a container
# - user: map-root-user so we appear as root inside but are unprivileged outside
# - pid: private PID tree; --fork so our shell is PID 1 inside
# - mnt: private mount namespace; --mount-proc gives us our own /proc
# - uts/ipc: private hostname and IPC objects
# Inside shell we set hostname and demo private mounts.
ip netns exec "${NS_NAME}" \
unshare --user --map-root-user \
--pid --fork --mount-proc \
--mount --uts --ipc \
/bin/bash -c '
set -euo pipefail
# inside mini-container
hostname mini
echo "Inside mini-container namespaces:"
echo " id: $(id)"
echo " hostname: $(hostname)"
echo " my PID should be 1: $$"
echo " IPs:"
ip -brief addr
echo
echo "[Mount demo] Creating a private tmpfs only visible in this mount namespace..."
mkdir -p /mnt/mini
mount -t tmpfs tmpfs /mnt/mini
echo "hello-from-mini" > /mnt/mini/hello.txt
echo " Wrote /mnt/mini/hello.txt (not visible on host)"
echo
echo "Try some tests:"
echo " ps -eF # should show only processes in this PID ns"
ps -eF | head -n 20
echo
echo " ping host (10.200.0.1) ..."
ping -c2 10.200.0.1 || true
echo
echo " Optional: curl a service on host if listening (e.g., http://10.200.0.1:8080)"
echo
echo "Type exit to leave the mini-container shell."
exec /bin/bash
'
}
cleanup() {
set +e
sudo ip netns del "${NS_NAME}" 2>/dev/null
# Deleting one end of veth removes the pair, but ensure host end is gone
if ip link show "${VETH_HOST}" >/dev/null 2>&1; then
sudo ip link del "${VETH_HOST}"
fi
echo "Cleanup complete."
}
# main
cmd="${1:-}"
case "$cmd" in
setup) setup ;;
status) status ;;
shell) shell ;;
cleanup) cleanup ;;
*) usage; exit 1 ;;
esac
|
How to use
| chmod +x mini_container.sh
|
1) Create the netns, veth, and IPs
| ./mini_container.sh setup
|
2) Drop into the "container" shell (isolated USER, PID, UTS, IPC, MNT; joined to the NET ns)
| ./mini_container.sh shell
|
(Inside) verify:
| id # shows uid=0 (root) thanks to user namespace
hostname # shows "mini"
ps -eF # private PID view, your shell is PID 1
ip a # shows 10.200.0.2/30 on veth-mini
ls /mnt/mini # shows hello.txt (private mount)
exit # leave the mini-container
|
3) Check status anytime
| ./mini_container.sh status
|
4) Tear it all down
| ./mini_container.sh cleanup
|
π§ͺ Advanced Lab β Full βMini Containerβ Environment
π― Goal
Build a minimal container-like environment without Docker or Podman, using only Linux namespaces, veth pairs, iptables, and mount isolation.
π§© Topology
| Host (root ns) Mini-container (namespaced)
-------------- -----------------------------
eth0 (real NIC)
β
β NAT via iptables
βΌ
veth-host 10.200.0.1/30 β veth-mini 10.200.0.2/30
βββ default route β 10.200.0.1
|
π§° Script: mini_container_advanced.sh
| #!/usr/bin/env bash
set -euo pipefail
# --- Configuration ---
NS_NAME="mini"
VETH_HOST="veth-host"
VETH_CONT="veth-mini"
HOST_IP="10.200.0.1/30"
CONT_IP="10.200.0.2/30"
ROOTFS_DIR="$HOME/rootfs_mini" # for bind-mounted root
RESOLV_CONF="$ROOTFS_DIR/etc/resolv.conf"
usage() {
cat <<EOF
Usage: $0 {setup|shell|status|cleanup}
setup : Create namespaces, veth, NAT, DNS, rootfs
shell : Enter the container shell (isolated namespaces, own /, Internet)
status : Show namespace connectivity
cleanup : Remove everything
EOF
}
require() {
command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1"; exit 1; }
}
setup() {
echo "==> Checking dependencies"
for bin in ip unshare mount iptables curl; do require "$bin"; done
echo "==> Creating network namespace and veth pair"
if ! ip netns list | grep -q "^${NS_NAME}\b"; then
sudo ip netns add "${NS_NAME}"
fi
if ! ip link show "${VETH_HOST}" >/dev/null 2>&1; then
sudo ip link add "${VETH_HOST}" type veth peer name "${VETH_CONT}"
fi
sudo ip link set "${VETH_CONT}" netns "${NS_NAME}"
echo "==> Assigning IPs"
sudo ip addr add "${HOST_IP}" dev "${VETH_HOST}" || true
sudo ip link set "${VETH_HOST}" up
sudo ip netns exec "${NS_NAME}" ip addr add "${CONT_IP}" dev "${VETH_CONT}"
sudo ip netns exec "${NS_NAME}" ip link set lo up
sudo ip netns exec "${NS_NAME}" ip link set "${VETH_CONT}" up
sudo ip netns exec "${NS_NAME}" ip route add default via 10.200.0.1
echo "==> Enabling IP forwarding + NAT on host"
sudo sysctl -w net.ipv4.ip_forward=1 >/dev/null
sudo iptables -t nat -C POSTROUTING -s 10.200.0.0/24 -o "$(ip route show default | awk '/default/ {print $5}')" -j MASQUERADE 2>/dev/null \
|| sudo iptables -t nat -A POSTROUTING -s 10.200.0.0/24 -o "$(ip route show default | awk '/default/ {print $5}')" -j MASQUERADE
echo "==> Setting up minimal rootfs"
mkdir -p "$ROOTFS_DIR"/{bin,dev,etc,proc,sys,run,tmp,var,usr,home}
chmod 755 "$ROOTFS_DIR"
sudo mount -t tmpfs tmpfs "$ROOTFS_DIR/tmp"
# Provide basic tools from host
for bin in bash ls cat ip ping curl mount ps; do
if command -v "$bin" >/dev/null; then
mkdir -p "$ROOTFS_DIR$(dirname $(which $bin))"
cp -a "$(which $bin)" "$ROOTFS_DIR$(which $bin)"
fi
done
echo "==> Creating minimal /etc/resolv.conf for DNS"
mkdir -p "$(dirname "$RESOLV_CONF")"
echo "nameserver 8.8.8.8" > "$RESOLV_CONF"
echo "Setup complete."
status
}
status() {
echo
echo "--- Host ---"
ip addr show dev "${VETH_HOST}" || true
echo
echo "--- ${NS_NAME} ---"
ip netns exec "${NS_NAME}" ip addr show dev "${VETH_CONT}" || true
echo
echo "Ping test container β host"
ip netns exec "${NS_NAME}" ping -c1 -W1 10.200.0.1 && echo "OK" || echo "Failed"
}
shell() {
echo "==> Entering isolated container shell"
ip netns exec "${NS_NAME}" \
unshare --user --map-root-user \
--pid --fork --mount-proc \
--mount --uts --ipc \
/bin/bash -c "
set -e
hostname mini
mount --make-rprivate /
mount --rbind $ROOTFS_DIR /
mount --make-rslave /
echo 'Inside mini-container:'
echo ' id: ' \$(id)
echo ' hostname: ' \$(hostname)
echo ' PID: ' \$\$
echo
echo 'Checking network...'
ping -c2 8.8.8.8 || echo 'No Internet access!'
echo
echo 'Checking DNS...'
curl -s https://example.com | head -n3 || echo 'DNS or Internet issue'
echo
echo 'Type exit to leave the container shell.'
exec /bin/bash
"
}
cleanup() {
echo "==> Cleaning up"
set +e
sudo ip netns del "${NS_NAME}" 2>/dev/null
if ip link show "${VETH_HOST}" >/dev/null 2>&1; then
sudo ip link del "${VETH_HOST}"
fi
sudo umount -lf "$ROOTFS_DIR/tmp" 2>/dev/null || true
sudo rm -rf "$ROOTFS_DIR"
echo "Cleanup complete."
}
case "${1:-}" in
setup) setup ;;
shell) shell ;;
status) status ;;
cleanup) cleanup ;;
*) usage; exit 1 ;;
esac
|
π§ How to Run the Lab
| chmod +x mini_container_advanced.sh
|
Step 1οΈβ£ β Setup
| ./mini_container_advanced.sh setup
|
β
Creates the namespace, veth link, NAT, root filesystem, and DNS configuration.
Step 2οΈβ£ β Check connectivity
| ./mini_container_advanced.sh status
|
Should show both interfaces and successful ping from container to host.
Step 3οΈβ£ β Enter the container shell
| ./mini_container_advanced.sh shell
|
Inside, you get:
| id # -> uid=0(root)
hostname # -> mini
ps -eF # private process tree (PID 1)
ping 8.8.8.8
curl https://example.com
ls / # private root filesystem
|
Youβre root inside, but unprivileged on the host.
Step 4οΈβ£ β Cleanup
| ./mini_container_advanced.sh cleanup
|
π§ What You Learned
| Feature |
Namespace |
Purpose |
| Private network stack |
net |
Own IP, routing, and interface |
| Private process tree |
pid |
Own init (PID 1) |
| Private filesystem |
mnt |
Own /, isolated mounts |
| Private hostname |
uts |
Own hostname (mini) |
| Private IPC resources |
ipc |
Own semaphores/message queues |
| Rootless root |
user |
Privilege isolation |
| NAT to Internet |
(iptables) |
Real-world outbound access |
| DNS |
(filesystem) |
Controlled via /etc/resolv.conf |
| Rootfs |
(bind mount) |
Simulated container filesystem |