$RodHat_
Syscall reference · Processes

setns(2)

Move the calling thread into an existing namespace, given an fd for it (or a pidfd).

Since
Linux 3.0check your kernel →
glibc
glibc 2.14
System
Linux
Man page
setns(2)

Synopsis

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <sched.h>

int setns(int fd, int nstype);

Rod's take

Containers aren't magic, they're namespace flags, and setns(2) is how you walk into one from the outside. nsenter opens the files under /proc/PID/ns and calls setns, so you can run the host's tcpdump and ss inside a distroless container's network namespace without installing a damn thing in the image. Join only the namespace you need; skip the mount namespace and you keep your own tools while /proc/PID/root shows you the container's files. Hold an fd on the ns file and the namespace sticks around for forensics after its processes are gone. Root required for most of it, as it should be.

Flags and arguments that matter

NameWhat it does
0Join whatever namespace type fd refers to; fine when you don't need the type checked.
CLONE_NEWNETLinux 3.0fd must refer to a network namespace.
CLONE_NEWIPCLinux 3.0fd must refer to an IPC namespace.
CLONE_NEWUTSLinux 3.0fd must refer to a UTS namespace.
CLONE_NEWNSLinux 3.8Mount namespace. Needs CAP_SYS_CHROOT and CAP_SYS_ADMIN; caller must not share CLONE_FS state.
CLONE_NEWPIDLinux 3.8Descendant PID namespace. Only affects children created afterwards, not the caller.
CLONE_NEWUSERLinux 3.8User namespace. Caller must be single-threaded; needs CAP_SYS_ADMIN in the target namespace.
CLONE_NEWCGROUPLinux 4.6fd must refer to a cgroup namespace.
CLONE_NEWTIMELinux 5.8fd must refer to a time namespace.

Errors you will actually hit

errnoMeaning
EPERMThe calling thread lacks the capability this namespace type requires.
EINVALNamespace type doesn't match nstype, ancestor PID ns, or multithreaded caller joining a user ns.
EBADFfd is not a valid file descriptor.
ESRCHfd is a pidfd but that process has terminated and been waited on.
ENOMEMCannot allocate memory to change the namespace.

Example

pid=$(docker inspect --format '{{.State.Pid}}' mycontainer)

# container's network stack, host's binaries
sudo nsenter -t "$pid" --net -- ss -tulpn
sudo nsenter -t "$pid" --net -- tcpdump -i eth0 -n

# distroless image: its pid/net/ipc/uts namespaces, host's /bin/sh
sudo nsenter -t "$pid" --pid --net --ipc --uts -- /bin/sh

# what nsenter calls setns(2) on
ls -la /proc/"$pid"/ns/

Trimmed from the full walkthrough in Your container is six clone(2) flags. nsenter gets you back in..

BSD equivalent

FreeBSD: no namespaces; jail_attach(2) (FreeBSD 5.1) is the closest thing to joining an existing isolated environment. OpenBSD: none.

The long version, from the console

Sources

  1. setns(2), man7.org
  2. nsenter(1), man7.org
  3. jail(2), FreeBSD

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.