def simple_unshare(mount=True, uts=True, ipc=True, net=False, pid=False, user=False, hostname=None): """Simpler helper for setting up namespaces quickly. If support for any namespace type is not available, we'll silently skip it. Args: mount: Create a mount namespace. uts: Create a UTS namespace. ipc: Create an IPC namespace. net: Create a net namespace. pid: Create a pid namespace. user: Create a user namespace. hostname: hostname to use for the UTS namespace """ # user namespace must be first if user and os.getuid() != 0: create_userns() # The mount namespace is the only one really guaranteed to exist -- # it's been supported forever and it cannot be turned off. if mount: unshare(CLONE_NEWNS) # Avoid mounts in the new namespace from affecting the parent namespace # on systems that share the rootfs by default, but allow events in the # parent to propagate down. try: _mount(None, '/', None, MS_REC | MS_SLAVE) except OSError as e: if e.errno != errno.EINVAL: raise if uts: create_utsns(hostname) # The IPC namespace was added 2.6.19 and may be disabled in the kernel. if ipc: try: unshare(CLONE_NEWIPC) except OSError as e: if e.errno != errno.EINVAL: pass if net: create_netns() if pid: create_pidns()
def create_pidns(): """Start a new pid namespace This will launch all the right manager processes. The child that returns will be isolated in a new pid namespace. If functionality is not available, then it will return w/out doing anything. Returns: The last pid outside of the namespace. """ first_pid = os.getpid() try: # First create the namespace. unshare(CLONE_NEWPID) except OSError as e: if e.errno == errno.EINVAL: # For older kernels, or the functionality is disabled in the config, # return silently. We don't want to hard require this stuff. return first_pid else: # For all other errors, abort. They shouldn't happen. raise # Now that we're in the new pid namespace, fork. The parent is the master # of it in the original namespace, so it only monitors the child inside it. # It is only allowed to fork once too. pid = os.fork() if pid: # Mask SIGINT with the assumption that the child will catch & process it. # We'll pass that back up below. signal.signal(signal.SIGINT, signal.SIG_IGN) # Forward the control of the terminal to the child so it can manage input. _safe_tcsetpgrp(sys.stdin.fileno(), pid) # Reap the children as the parent of the new namespace. exit_as_status(_reap_children(pid)) else: # Make sure to unshare the existing mount point if needed. Some distros # create shared mount points everywhere by default. try: _mount(None, '/proc', 'proc', MS_PRIVATE | MS_REC) except OSError as e: if e.errno != errno.EINVAL: raise # The child needs its own proc mount as it'll be different. _mount( 'proc', '/proc', 'proc', MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME) pid = os.fork() if pid: # Mask SIGINT with the assumption that the child will catch & process it. # We'll pass that back up below. signal.signal(signal.SIGINT, signal.SIG_IGN) # Now that we're in a new pid namespace, start a new process group so that # children have something valid to use. Otherwise getpgrp/etc... will get # back 0 which tends to confuse -- you can't setpgrp(0) for example. os.setpgrp() # Forward the control of the terminal to the child so it can manage input. _safe_tcsetpgrp(sys.stdin.fileno(), pid) # Watch all of the children. We need to act as the master inside the # namespace and reap old processes. exit_as_status(_reap_children(pid)) # Create a process group for the grandchild so it can manage things # independent of the init process. os.setpgrp() # The grandchild will return and take over the rest of the sdk steps. return first_pid
def create_pidns(): """Start a new pid namespace This will launch all the right manager processes. The child that returns will be isolated in a new pid namespace. If functionality is not available, then it will return w/out doing anything. Returns: The last pid outside of the namespace. """ first_pid = os.getpid() try: # First create the namespace. unshare(CLONE_NEWPID) except OSError as e: if e.errno == errno.EINVAL: # For older kernels, or the functionality is disabled in the config, # return silently. We don't want to hard require this stuff. return first_pid else: # For all other errors, abort. They shouldn't happen. raise # Now that we're in the new pid namespace, fork. The parent is the master # of it in the original namespace, so it only monitors the child inside it. # It is only allowed to fork once too. pid = os.fork() if pid: # Mask SIGINT with the assumption that the child will catch & process it. # We'll pass that back up below. signal.signal(signal.SIGINT, signal.SIG_IGN) # Forward the control of the terminal to the child so it can manage input. _safe_tcsetpgrp(sys.stdin.fileno(), pid) # Reap the children as the parent of the new namespace. exit_as_status(_reap_children(pid)) else: # Make sure to unshare the existing mount point if needed. Some distros # create shared mount points everywhere by default. try: _mount(None, '/proc', 'proc', MS_PRIVATE | MS_REC) except OSError as e: if e.errno != errno.EINVAL: raise # The child needs its own proc mount as it'll be different. _mount('proc', '/proc', 'proc', MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME) pid = os.fork() if pid: # Mask SIGINT with the assumption that the child will catch & process it. # We'll pass that back up below. signal.signal(signal.SIGINT, signal.SIG_IGN) # Now that we're in a new pid namespace, start a new process group so that # children have something valid to use. Otherwise getpgrp/etc... will get # back 0 which tends to confuse -- you can't setpgrp(0) for example. os.setpgrp() # Forward the control of the terminal to the child so it can manage input. _safe_tcsetpgrp(sys.stdin.fileno(), pid) # Watch all of the children. We need to act as the master inside the # namespace and reap old processes. exit_as_status(_reap_children(pid)) # Create a process group for the grandchild so it can manage things # independent of the init process. os.setpgrp() # The grandchild will return and take over the rest of the sdk steps. return first_pid