Skip to content

Instantly share code, notes, and snippets.

@dpino
Last active November 28, 2024 09:16
Show Gist options
  • Save dpino/6c0dca1742093346461e11aa8f608a99 to your computer and use it in GitHub Desktop.
Save dpino/6c0dca1742093346461e11aa8f608a99 to your computer and use it in GitHub Desktop.
Setup a network namespace with Internet access
#!/usr/bin/env bash
# set -x
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script"
exit 1
fi
# Returns all available interfaces, except "lo" and "veth*".
available_interfaces()
{
local ret=()
local ifaces=$(ip li sh | cut -d " " -f 2 | tr "\n" " ")
read -a arr <<< "$ifaces"
for each in "${arr[@]}"; do
each=${each::-1}
if [[ ${each} != "lo" && ${each} != veth* ]]; then
ret+=( "$each" )
fi
done
echo ${ret[@]}
}
IFACE="$1"
if [[ -z "$IFACE" ]]; then
ifaces=($(available_interfaces))
if [[ ${#ifaces[@]} -gt 0 ]]; then
IFACE=${ifaces[0]}
echo "Using interface $IFACE"
else
echo "Usage: ./ns-inet <IFACE>"
exit 1
fi
fi
NS="ns1"
VETH="veth1"
VPEER="vpeer1"
VETH_ADDR="10.200.1.1"
VPEER_ADDR="10.200.1.2"
trap cleanup EXIT
cleanup()
{
ip li delete ${VETH} 2>/dev/null
}
# Remove namespace if it exists.
ip netns del $NS &>/dev/null
# Create namespace
ip netns add $NS
# Create veth link.
ip link add ${VETH} type veth peer name ${VPEER}
# Add peer-1 to NS.
ip link set ${VPEER} netns $NS
# Setup IP address of ${VETH}.
ip addr add ${VETH_ADDR}/24 dev ${VETH}
ip link set ${VETH} up
# Setup IP ${VPEER}.
ip netns exec $NS ip addr add ${VPEER_ADDR}/24 dev ${VPEER}
ip netns exec $NS ip link set ${VPEER} up
ip netns exec $NS ip link set lo up
ip netns exec $NS ip route add default via ${VETH_ADDR}
# Enable IP-forwarding.
echo 1 > /proc/sys/net/ipv4/ip_forward
# Flush forward rules.
iptables -P FORWARD DROP
iptables -F FORWARD
# Flush nat rules.
iptables -t nat -F
# Enable masquerading of 10.200.1.0.
iptables -t nat -A POSTROUTING -s ${VPEER_ADDR}/24 -o ${IFACE} -j MASQUERADE
iptables -A FORWARD -i ${IFACE} -o ${VETH} -j ACCEPT
iptables -A FORWARD -o ${IFACE} -i ${VETH} -j ACCEPT
# Get into namespace
ip netns exec ${NS} /bin/bash --rcfile <(echo "PS1=\"${NS}> \"")
@purpletech77
Copy link

Hi,
Iam able to ping the host IP with your script from ns1, but Iam not able to ping outside of the host - Any idea ?

Thanks .

@andrew160
Copy link

@purpletech77 Make sure you got the interface name right. For me it was enp0s3, and not eth0. Now it works perfectly 😄

@markododa
Copy link

if the system uses a local dns server (like dnsmasqd on ubuntu ), a resolv.conf needs to be setup for the namespace, for example
mkdir -p /etc/netns/ns1/
echo 'nameserver 8.8.8.8' > /etc/netns/ns1/resolv.conf

@EJohnF
Copy link

EJohnF commented Feb 15, 2017

It works fine for me if change row 49:
iptables -t nat -A POSTROUTING -s ${VPEER_ADDR}/24 -o eth0 -j MASQUERADE
to
iptables -t nat -A POSTROUTING -s ${VETH_ADDR}/24 -o eth0 -j MASQUERADE

And it's logically true. In this row you set up the iptable routing. And try to connect eth0 with virtual interface.
But, VPEER_ADDR - locate in the ns1 network namespace while VETH_ADDR in global(default).

Am I right that it's a mistake in that row?

@aneeshdurg
Copy link

I don't think that row makes a difference, it's only looking at the prefix, right?

@mcgovern612
Copy link

Any ideas why I would be able to ping from the namespace, but not wget or curl?

@dpino
Copy link
Author

dpino commented Jun 27, 2019

Hi, sorry for not replying comments before. This is the first time I noticed there were actually comments here.

@mcgovern612 Are you pinging to an IPv4 address or to a host name? Maybe that's the issue and what's not actually working is DNS resolution (I generally ping to an IPv4 address and wget or curl an URL resource). For name host resolution check the comment by @markoda (https://gist.github.com/dpino/6c0dca1742093346461e11aa8f608a99#gistcomment-1987705) or add an external nameserver to your /etc/resolv.conf.

@dpino
Copy link
Author

dpino commented Jun 27, 2019

@EJohnF About line 49, I actually tried both VPEER_ADDR and VETH_ADDR and it works in both cases. That said, I think it's more correct to apply source natting in the host interface since the host interface should be reachable from the network namespace.

@dpino
Copy link
Author

dpino commented Jun 27, 2019

@andrew160 nice catch, the "eth0" string should be another variable in the script.

@mcgovern612
Copy link

@dpino DNS is working as ping can resolve hostnames, and in wget calls I can see resolution but it just doesn't connect.

I must say though, thank you for the awesome script and blog description. It was very useful for someone who has never used network namespaces.

@Lu-Yi-Hsun
Copy link

@dpino

I can ping 8.8.8.8

But can not ping www.google.com

How to fix ?
Have been set nameserver

@dpino
Copy link
Author

dpino commented Dec 1, 2019

@Lu-Yi-Hsun I have modified the script to fix the "eth0" issue pointed out by other users before, as well as introducing other improvements like select a default interface (if only one is available) and remove veth interfaces on exit.

I run this new version of the script in my laptop and this is what I got when pinging 8.8.8.8 and www.google.com:

$ sudo ./ns-inet.sh 
Using interface wlp59s0
ns1> ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=53 time=18.1 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=53 time=17.7 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=53 time=17.9 ms
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 17.663/17.873/18.089/0.173 ms
ns1> ping www.google.com
PING www.google.com (216.58.206.228) 56(84) bytes of data.
64 bytes from par10s34-in-f4.1e100.net (216.58.206.228): icmp_seq=1 ttl=53 time=17.6 ms
64 bytes from par10s34-in-f4.1e100.net (216.58.206.228): icmp_seq=2 ttl=53 time=17.5 ms
64 bytes from par10s34-in-f4.1e100.net (216.58.206.228): icmp_seq=3 ttl=53 time=17.0 ms
^C
--- www.google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 17.027/17.370/17.602/0.247 ms
ns1> 

So, I don't know what could be the reason why you can ping 8.8.8.8 but not www.google.com. It seems likely is due to a configuration problem in your settings or environment. Can you resolve www.google.com out of a networking namespace?

@Lu-Yi-Hsun
Copy link

Lu-Yi-Hsun commented Dec 1, 2019

@dpino

thank you!
I find the problem.

ns1 sent ping www.google.com
VPEER ----DNS(packet)------>VETH
VPEER<---------ICMP(Destination Unreachable, Communication Administratively Prohibited)--------VETH
Firewall block the packet sent from VETH to wlpXXs0

so this can fix my problem

sudo systemctl stop firewalld

@tokolar
Copy link

tokolar commented Dec 28, 2019

@dpino

Is it supposed to work with firefox as well? Hoped so but not working

@kedorlaomer
Copy link

Weird, executing the script (as well as following the corresponding blog post) only yields “Destination Host Unreachable” at the step of ping. All the outputs (routing tables, links and addresses) are as expected. I remember I tried this about half a year ago and it worked flawlessly. Anyone knows what might have happened? It's not the firewall. Tcpdump finds ARP who-has asking for “who's the gateway”.

@nddao
Copy link

nddao commented Apr 25, 2020

Hi,
After executing the script ns-inet.sh on a server, I am able to ping the host IP as well as any IP configured on this server, but I am not able to ping any IP outside of the host. If I am not in the namespace prompt, I am able to ping the IP outside of the host. Any idea ?

@programmer131
Copy link

I was getting error with interface name, also don't want to lose netns on exit, here is modified version
https://gist.github.com/programmer131/f1bf0d62249a2503bb7cdfaac6598d2d

@Animesh0817
Copy link

@mcgovern612, I am facing the same problem. Did u get any solution for this ?

@neoavalon
Copy link

neoavalon commented Aug 19, 2020

First thank you for the helpful script. These commands allow me to ping and communicate with systems on my local LAN and if using an exact IP connect out to the internet. However DNS doesn't work at all. If I do something like (from the namespace):

wget http://ftp.gnu.org/gnu/wget/wget-1.5.3.tar.gz

it fails to resolve. Looking at /etc/resolv.conf, it appears that it uses the systemd-resolver stub resolver at 127.0.0.53. As I understand it, this provides name resolution to applications via d-bus. A "systemd-resolve --status" gives some d-bus related errors about failing to get link data for unknown objects. /etc/resolv.conf is a symlink for:

lrwxrwxrwx 1 root root 27 Oct 22 2018 /etc/resolv.conf -> /run/resolvconf/resolv.conf

Not sure why but creating a /etc/netns/--my namespace--/resolv.conf and adding a nameserver (either an external one or internal LAN one) didn't work even w/ a reboot. The simplest way that I've been able to work around this is to just point /etc/resolv.conf to /run/systemd/resolve/resolv.conf which lists my LAN router as the nameserver.

I'm in a bit of a pinch right now so this is a good enough temporary workaround.

@dpino
Copy link
Author

dpino commented Aug 19, 2020

@neoavalon My experience tells me that any issue related with DNS resolution is likely due to a configuration problem. I run the script again and it is working fine. I've updated it however, because there was an issue with retrieving the available interfaces when the script was run without arguments. Here's the test I run:

$ sudo ./ns-inet.sh 
Using interface wlp59s0
ns1> ping -c 1 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=54 time=46.8 ms

--- 1.1.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 46.807/46.807/46.807/0.000 ms
ns1> ping -c 1 www.google.com
PING www.google.com (172.217.16.228) 56(84) bytes of data.
64 bytes from mad08s04-in-f4.1e100.net (172.217.16.228): icmp_seq=1 ttl=115 time=68.6 ms

--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 68.557/68.557/68.557/0.000 ms
ns1> wget www.google.com -O index.html
--2020-08-19 23:03:35--  http://www.google.com/
Resolving www.google.com (www.google.com)... 172.217.16.228, 2a00:1450:4003:805::2004
Connecting to www.google.com (www.google.com)|172.217.16.228|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘index.html’

index.html                                               [ <=>                                                                                                                  ]  12.77K  --.-KB/s    in 0.001s  

2020-08-19 23:03:35 (9.44 MB/s) - ‘index.html’ saved [13074]

My /etc/resolv.conf is a proper file, not a link. What you mentioned about 127.0.0.53 sounds familiar but I think at some point I changed or tuned how systemD manages /etc/resolv.conf. This is how '/etc/resolv.conf' looks like in my case:

# Generated by NetworkManager

# search wan
nameserver 1.1.1.1
nameserver 8.8.8.8

@aneeshdurg
Copy link

A minor improvement: local ifaces=$(ip li sh | cut -d " " -f 2 | tr "\n" " ") on line 15 could be local ifaces=$(ip li sh | grep "state UP" | cut -d " " -f 2 | tr "\n" " ") to only select interfaces that are actually up.

@utomsig
Copy link

utomsig commented Nov 2, 2020

Had the same problem with DNS as others on Ubuntu Desktop 20.04. My /etc/resolv.conf also has nameserver 127.0.0.53 in it. I didn't have to change the /etc/resolv.conf, I only created a link. This is what I added this to the script:

mkdir -p "/etc/netns/$NS"
ln -s /run/systemd/resolve/resolv.conf /etc/netns/"$NS"/resolv.conf

And added a line to the cleanup function:

cleanup()
{
   ip li delete ${VETH} 2>/dev/null
   rm -rf "/etc/netns/$NS" # Removing the resov.conf and it's directory
}

Will ping work?

$ sudo ./ns-inet.sh
Using interface enp5s0
ns1> ping www.google.com
PING www.google.com (172.217.21.132) 56(84) bytes of data.
64 bytes from arn11s02-in-f4.1e100.net (172.217.21.132): icmp_seq=1 ttl=115 time=10.4 ms

Hurray!
Thanx for the script!

@oleshii
Copy link

oleshii commented Dec 16, 2020

It would work ONLY if your local router has the allow policy to pass a packets with an appropriate addresses. Not all the routers have those policies. Your script does not work for me. In concrete I can not ping veth interface from it's paired one in the child ns and back. So I can not ping any external resources from the child ns

@Magicrafter13
Copy link

I tried this script, though I set VETH_ADDR to 192.168.0.1 and VPEER_ADDR to 192.168.0.2.

inside the namespace, I can ping 192.168.0.1, and I can ping my computer's actual ipv4: 10.0.0.199, but I can't ping the gateway 10.0.0.1 from inside the namespace. And it goes without saying, but I also can't ping any internet addresses like 1.1.1.1 for example.

Maybe I have the issue that @oleshii mentioned, though I'm not sure. I have Xfinity, and we currently have their XB6 modem. If their default settings do allow this however, then I'm still not sure what the issue is.

I'm on Manjaro. I don't believe there was any firewall preinstalled.

It's weird too, because, I had it working last night. Not this script specifically, but I followed this guide:
https://blog.laxu.de/2020/04/02/how-use-zoom-sandbox/
Yet after restarting my computer (and running the commands again once I realized my changes hadn't been saved), it doesn't work, which is the main reason I tried this script.

@dpino
Copy link
Author

dpino commented Jan 11, 2021

Considering the feedback from the last comments, I run the script again on my laptop and I can tell it works for me.

$ sudo ./ns-inet.sh 
Using interface wlp59s0
ns1> ping -c 1 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=61 time=2.04 ms

--- 1.1.1.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.044/2.044/2.044/0.000 ms
ns1> ping -c 1 www.google.com
PING www.google.com (172.217.24.36) 56(84) bytes of data.
64 bytes from hkg07s23-in-f36.1e100.net (172.217.24.36): icmp_seq=1 ttl=116 time=6.18 ms

--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.181/6.181/6.181/0.000 ms

@Magicrafter13 the script also works for me using VETH_ADDR and VPEER_ADDR as 192.168.0.1 and 192.168.0.2. Would you mind sharing your routing table (route -n) in the network namespace as well as in the host?

@yangtzeriverli
Copy link

I don't think using different private IP prefix block would cause trouble. @dpino

@tariromukute
Copy link

if the system uses a local dns server (like dnsmasqd on ubuntu ), a resolv.conf needs to be setup for the namespace, for example mkdir -p /etc/netns/ns1/ echo 'nameserver 8.8.8.8' > /etc/netns/ns1/resolv.conf

For anyone else, this resolved my name resolution issue on Ubuntu 22.04. Thanks @markododa.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment