如何将“匿名”网络命名空间内的 veth 设备连接到外部网络命名空间?

如何将“匿名”网络命名空间内的 veth 设备连接到外部网络命名空间?

我有一个进程调用unshare创建一个新的网络命名空间,其中仅包含其自身。当它调用execve启动 bash 时,ip 命令显示我只有一个lo设备。如果我还创建一个用户命名空间并将我的进程安排为该命名空间内的 root,我可以使用该ip命令来启动该设备并且它可以工作。

我还可以使用该命令在此命名空间中ip创建设备。veth但它没有出现在根级别命名空间中ip netns list,并且新veth设备也没有出现在根级别命名空间中(正如我所期望的那样)。如何将veth根级命名空间中的设备连接到veth进程命名空间内的新设备?该ip命令似乎要求命名空间具有由该ip命令分配的名称,而我的则不需要,因为我没有用来ip netns add创建它。

也许我可以通过编写自己的程序来做到这一点,该程序使用 netlink 设备并进行设置。但我真的不想这么做。有没有办法通过命令行来做到这一点?

一定有办法做到这一点,因为 docker 容器也有自己的网络命名空间,并且该命名空间也是未命名的。然而,它内部有一个veth设备连接到veth它外部的设备。

我的目标是动态创建进程隔离上下文,理想情况下不需要成为容器外部的根。为此,我将创建一个 PID 命名空间、一个 UID 命名空间、一个网络命名空间、一个 IPC 命名空间和一个挂载命名空间。我还可以创建一个 cgroup 命名空间,但这些都是新的,我需要能够在当前支持的 SLES、RHEL 和 Ubuntu LTS 版本上运行。

我一次一直在处理这个命名空间,目前用户、PID 和挂载命名空间运行良好。

如果必须的话,我可以挂载/proc/pid/ns/net,但我更愿意从用户命名空间内部执行此操作,这样(再次)我不必成为命名空间之外的根用户。大多数情况下,我希望一旦命名空间中的所有进程都消失,所有内容都会消失。当我完成后,在文件系统上清理一堆状态并不理想。尽管在第一次分配容器时临时创建它然后立即删除它比容器退出时必须清理它要好得多。

不,我不能使用泊坞窗,长沙,库克特,或任何其他现有的解决方案,这样我就可以依赖沼泽标准系统实用程序以外的任何东西(比如ip),系统库如glibc,以及 Linux 系统调用。

答案1

ip link有一个命名空间选项,除了网络命名空间名称之外,还可以使用PID引用进程的名称空间。如果PID命名空间在进程之间共享,您可以以任何一种方式移动设备;这可能是最简单的里面,当你考虑PID 1存在“外部”。与单独的PID命名空间您需要从外部(PID)命名空间移动到内部命名空间。

例如,从网络命名空间内部,您可以创建一个veth 设备对PID 1命名空间:

ip link add veth0 type veth peer name veth0 netns 1

命名空间在 Linux 中如何工作

每个进程都有其命名空间的参考文件/proc/<pid>/ns/。此外,ip netns/run/netns/.这些文件与setns系统调用将正在运行的线程的名称空间更改为该文件指向的名称空间。

从 shell 中,您可以使用以下命令进入另一个命名空间nsenter程序,在参数中提供名称空间文件(路径)。

Linux 命名空间的一个很好的概述在运行中的命名空间LWN.net 上的文章系列。

设置命名空间

当您设置多个命名空间时(挂载、pid、用户、等),在更改之前尽早设置网络名称空间PID命名空间。如果您没有共享或者PID命名空间,你没有任何办法指向外部的网络命名空间,因为你看不到引用外部网络命名空间的文件。

如果您需要比命令行实用程序提供的更多灵活性,则需要使用系统调用直接从程序管理名称空间。有关文档,请参阅相关手册页:man 2 setns,man 2 unshareman 7 namespaces

答案2

Aveth由一对设备组成,您将每个设备放入它希望与之通信的网络命名空间(或根命名空间)中。

这是我从根命名空间以 root 身份调用的脚本,用于创建新的网络命名空间,并在根命名空间和网络命名空间之间设置 veth 对。最后一行在该命名空间中启动一个 xterm。根据需要进行调整。

#!/bin/bash
# Setup network namespace with veth pair, start xterm in it

if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root" 1>&2
   exit 1
fi

NS=${1:-ns0}
DEV=${2:-veth0}
DEV_A=${DEV}a
DEV_B=${DEV}b
ADDR=${3-:10.0.0}
ADDR_A=${ADDR}.254
ADDR_B=${ADDR}.1
MASK=${5:-24}
COL=${4:-yellow}

# echo ns=$NS dev=$DEV col=$COL mask=$MASK

ip netns add $NS
ip link add $DEV_A type veth peer name $DEV_B netns $NS
ip addr add $ADDR_A/$MASK dev $DEV_A
ip link set ${DEV}a up
ip netns exec $NS ip addr add $ADDR_B/$MASK dev $DEV_B
ip netns exec $NS ip link set ${DEV}b up
ip netns exec $NS ip route add default via $ADDR_A dev $DEV_B
ip netns exec $NS su -c "xterm -bg $COL &" your_user_id

您还可以使用

ip link set name_of_if netns name_of_ns

在网络命名空间之间移动网络接口(包括物理设备的接口)。

编辑

我刚刚测试过,你也可以创建一对之内新的命名空间,并将一个端点放入根命名空间。诀窍是根命名空间显然没有名称,但可以通过 id 访问1

sudo ip link add veth1b type veth peer name veth1a netns 1

我不熟悉它如何与 一起使用unshare,但我想您仍然可以以某种方式找到新创建的网络命名空间 /proc/<pid>/ns/<type>。如果它没有名称,它至少应该有一个数字 ID。ip netns list也可以提供帮助。我想所有这些也可以通过系统调用来完成,而不是使用ip;strace或者ltrace可能有助于找出哪些。

编辑

我快速浏览了一下man namespaces 7;找出命名空间 from/proc似乎涉及足够多的内容(您只有一个文件描述符),也许最好使用 创建命名空间ip netns add,然后在其中启动程序ip netns exec而不是unshare。假设你仅有的需要一个新的网络命名空间,而不是其他东西。

相关内容