71

When I create a new docker container like with

docker run -it -m 560m --cpuset-cpus=1,2 ubuntu sleep 120

and check its namespaces, I can see that new namespaces have been created (example for pid 7047).

root@dude2:~# ls /proc/7047/ns -la
total 0
dr-x--x--x 2 root root 0 Jul  7 12:17 .
dr-xr-xr-x 9 root root 0 Jul  7 12:16 ..
lrwxrwxrwx 1 root root 0 Jul  7 12:17 ipc -> ipc:[4026532465]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 mnt -> mnt:[4026532463]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 net -> net:[4026532299]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 pid -> pid:[4026532466]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jul  7 12:17 uts -> uts:[4026532464]
root@dude2:~# ls /proc/self/ns -la

When I check with ip netns list I cannot see the new net namespace.

dude@dude2:~/docker/testroot$ ip netns list
dude@dude2:~/docker/testroot$ 

Any idea why?

chrk
  • 4,037
  • 2
  • 39
  • 47
christian
  • 9,412
  • 10
  • 41
  • 51

4 Answers4

81

That's because docker is not creating the reqired symlink:

# (as root)
pid=$(docker inspect -f '{{.State.Pid}}' ${container_id})
mkdir -p /var/run/netns/
ln -sfT /proc/$pid/ns/net /var/run/netns/$container_id

Then, the container's netns namespace can be examined with ip netns ${container_id}, e.g.:

# e.g. show stats about eth0 inside the container 
ip netns exec "${container_id}" ip -s link show eth0
init_js
  • 4,143
  • 2
  • 23
  • 53
jary
  • 1,161
  • 6
  • 9
  • 2
    You can also mount `/var/run/docker/netns` where `ip` is expecting it `docker run -it --rm -v /var/run/docker/netns:/var/run/netns --privileged=true nicolaka/netshoot ip netns list` – Jarek Przygódzki Mar 15 '18 at 23:07
  • 8
    Please bind-mount (network) namespace references, because the symbolic link might later point to a reused PID and completely different namespace. Instead, create an empty file `foo` inside `/run/netns/` and then simply bind-mount the `/proc/.../ns/net` to `/run/netns/foo`. This will ensure that if the original process dies and the PID gets reused, the old PID's network namespace is still kept open until you unmount or reboot, but it won't ever wrongly point to a new process' network namespace. – TheDiveO Jul 10 '18 at 16:39
  • check if docker creates the mapping in wrong directory, it's easy to link (remove/save original): `cd /var/run; sudo ln -s docker/netns netns` – Alec Istomin Jul 11 '19 at 21:41
  • @TheDiveO Unfortunately this does not seem to be possible, as namespaces aren't regular files, in `ls -l` they are shown like `net -> 'net:[4026532543]'`. This leads to the error `mount: /run/netns/abc: mount(2) system call failed: Not a directory.` – Paul Apr 12 '20 at 20:27
  • Of course, this is possible, see link at the end of this comment. Make sure to *bind mount* (`mount --bind ...`) on a *file*, not a directory, which works perfectly with file paths pointing to the nsfs filesystem. See also an example in one of the unit tests of my Linux kernel namespaces discovery package for Go, https://github.com/TheDiveO/lxkns/blob/42457374d7d786e43244423bda7414677e48a525/discovery_bindmount_test.go#L52. – TheDiveO Apr 12 '20 at 21:10
  • 1
    Warning: never symlink namespaces, because that references a namespace inode by a path that may go away. Or even worse: replaced by a different namespace with the same path. Always bind-mount a namespace reference into another place as this correctly tracks the underlying inode reference(s). – TheDiveO Jun 08 '22 at 11:16
55

As @jary indicates, the ip netns command only works with namespace symlinks in /var/run/netns. However, if you you have the nsenter command available (part of the util-linux package), you can accomplish the same thing using the PID of your docker container.

To get the PID of a docker container, you can run:

docker inspect --format '{{.State.Pid}}' <container_name_or_Id>

To get a command inside the network namespace of a container:

nsenter -t <contanier_pid> -n <command>

E.g:

$ docker inspect --format '{{.State.Pid}}' weechat
4432
$ sudo nsenter -t 4432 -n ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
75: eth0@if76: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:1b brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.27/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:1b/64 scope link 
       valid_lft forever preferred_lft forever

The above was equivalent to running ip netns exec <some_namespace> ip addr show.

As you can see here, you will need to run nsenter with root privileges.

larsks
  • 277,717
  • 41
  • 399
  • 399
17

Similar but different with @jary’s answer.
There is no need to introduce /proc/<pid>/ or netster. Only one move below to achieve what you want. Thus, you could operate containers’ network namespace just like they are created manually on host machine.

One Move:

ln -s /var/run/docker/netns  /var/run/netns 

Result:

Start a container:

docker run -tid ubuntu:18.04 

List container:

root@Light-G:/var/run# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
972909a27ea1        ubuntu:18.04        "/bin/bash"         19 seconds ago      Up 18 seconds                           peaceful_easley

List network namespace of this container:

root@Light-G:/var/run# ip netns list
733443afef58 (id: 0)

Delete container:

root@Light-G:/var/run# docker rm -f 972909a27ea1
972909a27ea1

List network namespace again:

root@Light-G:/var/run# ip netns list
root@Light-G:/var/run#
Light.G
  • 5,548
  • 1
  • 14
  • 25
  • 1
    i totally disagree. ln -s /var/run/docker/netns /var/run/netns doesnt do what we need. it just creates symlink /var/run/netns/netns and when new container is created ip netns list wont show its namespace – Alex Apr 27 '20 at 19:47
  • 1
    @Alex I've tested this on Ubuntu and CentOS. What platform you r working on? Maybe we could research this further. – Light.G May 06 '20 at 05:41
  • 6
    As it often happens, you want `ln -Ts` instead of just `ln -s` . This way you'll never end up with doubly nested /var/run/netns/netns. – kubanczyk Aug 15 '20 at 10:03
0

NOTE: THIS SEEM MUCH MORE SECURE than the symlink practice BUT MAY STILL LEAD TO SERIOUS SECURITY PROBLEMS.

It seems to me we may be violating what the containers should be - a contained environments.

That's because docker is not creating the reqired symlink:

# (as root)
pid=$(docker inspect -f '{{.State.Pid}}' "${container_id}")
mkdir -p /var/run/netns/
touch /var/run/$container_id
mount -o ro,bind "/proc/$pid/ns/net" "/var/run/netns/$container_id"

Then, the container's netns namespace can be examined with ip netns "${container_id}", e.g.:

# e.g. show stats about eth0 inside the container 
ip netns exec "${container_id}" ip -s link show eth0

Thank you @TheDiveO for notifying me.

jary
  • 1,161
  • 6
  • 9