4

I have a process that has called unshare to create a new network namespace with just itself inside. When it calls execve to launch bash, the ip command shows that I have just an lo device. If I also create a user namespace and arrange for my process to be root inside the namespace, I can use the ip command to bring that device up and it works.

I can also use the ip command to create a veth device in this namespace. But it doesn't show up in ip netns list and the new veth device doesn't show up in the root level namespace (as I'd expect). How do I connect a veth device in the root-level namespace to my new veth device inside my process namespace? The ip command seems to require that the namespace has a name assigned by the ip command, and mine doesn't because I didn't use ip netns add to create it.

Maybe I could do it by writing my own program that used the netlink device and set things up. But I'd really prefer not to. Is there a way to do this through the command line?

There must be a way to do it, because docker containers have their own network namespace as well, and that namespace is also unnamed. Yet there is a veth device inside it that's connected to a veth device outside it.

My goal is to dynamically create a process isolation context, ideally without needing to become root outside the container. To this end I'm going to be creating a PID namespace, a UID namespace, a network namespace, an IPC namespace, and mount namespace. I may also create a cgroup namespace, but those are newish and I need to be able to run on currently supported versions of SLES, RHEL, and Ubuntu LTS.

I've been working through this one namespace at a time, and I currently have User, PID and mount namespaces working satisfactorily.

I can mount /proc/pid/ns/net if I must, but I would prefer to do that from inside the user namespace so (again) I don't have to be root outside the namespace. Mostly, I want everything to disappear as soon as all the processes in the namespace are gone. Having a bunch of state to clean up on the filesystem when I'm done would be less than ideal. Though creating it temporarily when the container is first allocated and then immediately removing it is far better than having to clean it up when the container exits.

No, I can't use docker, lxc, rkt, or any other existing solution such that I'd be relying on anything other than bog-standard system utilities (like ip), system libraries like glibc, and Linux system calls.

  • Why don't you set up network namespace first? After it is up, you can continue by setting up the rest of the namespaces (mount, pid, etc). – sebasth Oct 5 '17 at 12:23
  • @sebasth - That is an idea. Though I think setting up the user namespace first is probably the best. Then there's a context in which I have a full set of capabilities. But I could set up the network namespace right afterward. – Omnifarious Oct 5 '17 at 12:28
  • @sebasth - One extreme option for my case would be to create the network namespace and never set it up beyond bringing up the lo device, and force the application to use an AF_UNIX socket for the one thing it's allowed to communicate with outside the container. That would require some modifications to the program running outside the container though. – Omnifarious Oct 5 '17 at 12:32
4

ip link has a namespace option, which in addition to a network namespace name, can use a PID to refer a process' namespace. If PID namespaces are shared between the processes, you can move devices either way; it is probably easiest from inside, when you consider PID 1 being "outside". With separate PID namespaces you need to move from outer (PID) namespace to the inner one.

For example, from inside of a network namespace you can create a veth device pair to PID 1 namespace:

ip link add veth0 type veth peer name veth0 netns 1

How namespaces work in Linux

Every process has reference files for their namespaces in /proc/<pid>/ns/. Additionally, ip netns creates persistent reference files in /run/netns/. These files are used with setns system call to change the namespace of the running thread to a namespace pointed by such file.

From shell you can enter to another namespace using nsenter program, providing namespace files (paths) in arguments.

A good overview of Linux namespaces is given in the Namespaces in operation article series on LWN.net.

Setting up namespaces

When you set up multiple namespaces (mount, pid, user, etc.), set up network namespace as early as possible, before altering mount and pid namespaces. If you do not have shared mount or pid namespaces, you do not have any way to point to the network namespace outside, because you can not see the files referring to network namespaces outside.

If you need more flexibility than the command line utilities provide, you need to use the systemcalls to manage name spaces directly from your program. For documentation, see the relevant man pages: man 2 setns, man 2 unshare and man 7 namespaces.

  • I've read that overview. :-) I've clarified my question to make it clearer what I'm trying to do. Which was good for me to do aside from this question, because it made it clearer to me too. – Omnifarious Oct 5 '17 at 12:26
  • @Omnifarious I've added the note about in which order you should set up your namespaces. You might (I haven't tested/I don't recall documentation) be able to use bind mounts, if you need to access the outer namespace from inside after you've set everything up. You haven't mentioned what do you use for setting namespaces up. You can get more flexibility if you access namespace system calls directly from your own program (instead of relying on unshare, nsenter, etc. tools). – sebasth Oct 5 '17 at 12:41
  • @sebash - I was referring to the unshare system call, not the unshare user process. Perhaps I should've said unshare(2). :-) Yes, the capability dance is interesting and requires very careful handling and would be really hard to manage with just the command line utilities. I just really don't want to have to delve into the netlink protocol if I don't have to. – Omnifarious Oct 5 '17 at 12:50
  • I like both of your answers, and they've both helped me. I'm picking yours because it is concise, and I feel more focused on the question I was intending to ask. – Omnifarious Oct 5 '17 at 23:52
  • In order to make this work correctly for my case, I've had to add CAP_NET_ADMIN and I think also (though I need to test) CAP_NET_RAW to the ambient capability set before execing the ip command. – Omnifarious Oct 11 '17 at 21:33
1

A veth consists of a pair of devices, you put each of those in the network namespace (or root namespace) it wishes to communicate with.

Here is a script I call as root from the root namespace to create a new network namespace, and set up a veth-pair between the root namespace and the network namespace. The last line starts an xterm in that namespace. Adapt as needed.

#!/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

You can also use

ip link set name_of_if netns name_of_ns

to move network interfaces (including interfaces to physical devices) between network namespaces.

Edit

I just tested it, you can also create a pair from within the new namespace, and put one endpoint into the root namespace. The trick is that the root namespace apparently doesn't have a name, but is accessible as id 1:

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

I'm not familiar how it works with unshare, but I suppose you can still find the newly created network namespace in /proc/<pid>/ns/<type> somehow. If it doesn't have a name, it should at least have a numeric id. ip netns list could also help. And I guess all of this can also be done with system calls instead of using ip; strace or ltrace may help to figure out which ones.

Edit

I had a quick look at man namespaces 7; figuring out the namespace from /proc seems involved enough (you only have a file descriptor) that maybe it's better to create the namespace with ip netns add and then start your program in it with ip netns exec instead of unshare. Assuming that you only need a new network namespace, and not something else, too.

  • I presume that it would be impossible for a process in $NS to create an interface in the main namespace. Additionally, how would you do this if you didn't create the namespace with ip netns add? – Omnifarious Oct 5 '17 at 6:27
  • Oh, I missed the last bit. I'll comment on that tomorrow morning. – Omnifarious Oct 5 '17 at 6:41
  • I've clarified my question more. I might be able to use the trick of creating the veth devices from inside the container before I create the PID namespace. I also discovered that docker does give names beyond /proc/pid/ns/net to its namespaces. They just live in /var/run/docker/netns/. – Omnifarious Oct 5 '17 at 12:24
  • 2
    Then docker does its own thing, because the ip netns names are in /var/run/netns/ ... – dirkt Oct 5 '17 at 12:40
  • I like both of your answers, and they've both helped me. Thank you. – Omnifarious Oct 5 '17 at 23:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.