/var/run is a symlink to
/run and should be "resolved" in advance or
/mnt/var/run might point back to
/run with the various commands.
At first glance this simpler command (rather than OP's which can never work as explained in the last note at the end) from initial namespace should be enough to allow
ip netns exec to work from the chrooted pid namespace:
mount --bind /run/netns/test1 /mnt/run/netns/test1
but it isn't. It's not related to the pid namespace, but to chroot. Actually the whole problem is the same when not switching to a new pid namespace at all.
ip netns exec doesn't manipulate only network namespaces, but also has to manipulate mount namespaces and use mount(2) system calls for reasons I explained in this SF Q/A: Cannot create nested network namespace.
/proc should be mounted within the chroot for proper operations. With at least
/proc mounted from within the chroot, using
strace one can see
ip netns exec test1 ... will enter a new mount namespace and fail to remount
getuid() = 0
openat(AT_FDCWD, "/run/netns/test1", O_RDONLY|O_CLOEXEC) = 3
setns(3, CLONE_NEWNET) = 0
close(3) = 0
unshare(CLONE_NEWNS) = 0
mount("", "/", 0x5595a749373f, MS_REC|MS_SLAVE, NULL) = -1 EINVAL (Invalid argument)
write(2, "\"mount --make-rslave /\" failed: "..., 49) = 49
exit_group(-1) = ?
+++ exited with 255 +++
The remount fails with
EINVAL: because of the chroot,
/ is not a mount point anymore.
Note that the network namespace part itself doesn't have a problem. So for example doing this from the chrooted side:
nsenter --net=/run/netns/test1 ip link
will succeed and display test1's network interfaces, but will for example not switch relevant entries available in
/sys/ like would do
ip netns exec (as described in my former link), should those be needed.
Solution: add a bind mount over itself
The fix: bind-mount the chroot target to itself to forcibly turn it into a mount point, preferably in private mode to avoid interactions (especially when systemd turned
/ shared) before doing the chroot.
I reproduce below a working setup with shell commands.
I'm omitting some boiler plate, like probably mounting
/run (aka init's
/mnt/run) from a tmpfs etc. but mounting
/proc from within the chroot should really be done.
first in term1:
~# unshare --fork --pid bash
~# echo $$
~# mount --bind --make-private /mnt /mnt
~# chroot /mnt bash
/# mount -t proc proc /proc
then in an unrelated term2:
~# ip netns add test1
~# mkdir -p /mnt/run/netns
~# touch /mnt/run/netns/test1
~# mount --bind /run/netns/test1 /mnt/run/netns/test1
again in term1:
/# ip netns exec test1 ip link
1: lo: <LOOPBACK> mtu 65536 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
as stated before this issue has no relation to the pid namespace. Removing the command
unshare --fork --pid bash will fix the same problem when using just a chroot without pid namespace.
a mount command should never be run from within an
ip netns exec command. Remember that
ip netns exec itself creates a temporary mount namespace. Whatever mount is done within will disappear when
ip netns exec ends. This is also explained in my previous link. Doing this instead would still fine:
nsenter --net=/run/netns/test1 mount --bind /proc/self/ns/net /mnt/var/run/netns/test1
but of course not needed since it's the exact same entity as can be verified by having same inode in the nsfs virtual filesystem:
~# stat -f -c %T /run/netns/test1; stat -c %i /run/netns/test1
~# nsenter --net=/run/netns/test1 sh -c 'stat -f -L -c %T /proc/self/ns/net; stat -L -c %i /proc/self/ns/net'