The Routing Collision Problem
Imagine you have a single Linux server acting as a jump host. You want to run two different VPN clients (e.g., WireGuard and OpenVPN) on this exact same server simultaneously. Both VPN clients will attempt to manipulate the global Linux routing table to set their respective tunnels as the default gateway. They will violently overwrite each other’s routes, causing all network traffic on the server to instantly collapse.
To solve this, Linux provides a kernel-level isolation feature called Network Namespaces (netns). A Network Namespace is essentially a completely independent, virtualized TCP/IP stack. It has its own isolated routing table, its own isolated firewall (iptables), its own isolated loopback interface, and its own isolated set of physical or virtual ethernet adapters. It is the underlying technology that powers Docker containers, but you can use it natively via the iproute2 suite to isolate individual processes.
Step 1: Creating a Network Namespace
To begin, we will create a completely isolated network environment named vpn_sandbox.
Open a terminal with root privileges and execute:
sudo ip netns add vpn_sandbox
You can verify it was created by listing all active namespaces:
ip netns list
Step 2: Connecting the Namespace to the Physical World
Right now, vpn_sandbox is completely disconnected from the outside world. It doesn’t even have a loopback interface turned on. To give it internet access, we must create a virtual ethernet cable (a veth pair). One end of the cable plugs into the host’s global namespace, and the other end plugs into our sandbox.
# 1. Create the virtual ethernet cable with two ends: veth0 (host side) and veth1 (sandbox side)
sudo ip link add veth0 type veth peer name veth1
# 2. Move the veth1 end of the cable inside the namespace
sudo ip link set veth1 netns vpn_sandbox
Step 3: Configuring the IP Addresses
Now we must assign IP addresses to both ends of this virtual cable so they can communicate.
# 1. Configure the Host side (veth0)
sudo ip addr add 10.200.1.1/24 dev veth0
sudo ip link set veth0 up
# 2. Configure the Sandbox side (veth1). Notice we use the `ip netns exec` command to run the configuration INSIDE the namespace.
sudo ip netns exec vpn_sandbox ip addr add 10.200.1.2/24 dev veth1
sudo ip netns exec vpn_sandbox ip link set veth1 up
# 3. Turn on the loopback interface inside the sandbox (Required for many local applications)
sudo ip netns exec vpn_sandbox ip link set lo up
Step 4: Setting the Sandbox Routing
The sandbox has an IP, but it doesn’t know how to reach the internet. We must tell it to use the host machine (10.200.1.1) as its default gateway.
sudo ip netns exec vpn_sandbox ip route add default via 10.200.1.1
Step 5: Enabling NAT on the Host
The host machine is now receiving packets from the sandbox, but the host’s physical router doesn’t know what the 10.200.1.0/24 subnet is. We must configure the host machine to act as a NAT router using iptables.
# 1. Enable IP Forwarding in the kernel
sudo sysctl -w net.ipv4.ip_forward=1
# 2. Masquerade traffic originating from the sandbox as it leaves the host's physical interface (e.g., eth0)
sudo iptables -t nat -A POSTROUTING -s 10.200.1.0/24 -o eth0 -j MASQUERADE
Step 6: Executing Isolated Processes
The architecture is complete. You can now launch your VPN client, a web browser, or a database exclusively inside this namespace.
To open a bash shell entirely trapped inside the namespace, run:
sudo ip netns exec vpn_sandbox bash
If you run ip route or ifconfig within this new shell, you will not see the host’s physical network adapters. You will only see the isolated veth1 interface. If you launch OpenVPN from this shell, it will modify the vpn_sandbox routing table, successfully tunneling its traffic without ever touching or disrupting the host machine’s primary global routing table.