Skip to content

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:

1
2
3
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

1
2
3
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:

1
2
3
4
5
6
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

1
2
3
4
5
6
7
8
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:

1
2
3
4
5
6
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