Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NSenter tunnel that allows to enter in any linux namespace container #48

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ remote hosts over SSH.
Naturally this is agentless and nothing needs to be installed on the remote
host except Python and an SSH agent.

It also has support for executing code in Docker containers.
It also has support for executing code in Docker or any linux namespaces based
containers.

It's perhaps best compared to Ansible or Fabric, but has some clever transport
magic which means it's very easy to develop with: you just write Python
Expand Down
1 change: 1 addition & 0 deletions chopsticks/ioloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,4 +302,5 @@ def run(self):
self.running = True
while self.running and (self.read or self.write):
self.step()
self.stop(self.result)
return self.result
59 changes: 59 additions & 0 deletions chopsticks/tunnel.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,65 @@ def close(self):
self.proc.wait()


class NSEnter(SubprocessTunnel):
"""A tunnel to a process on the same host, launched with nsenter.

This is a generic tunnel for all containers using linux namespaces
such as docker, systemd-nspawn, rkt, ... that can be spawned as a
subprocess of any given process id. This tunnel uses sudo hence it
requires the same passworless setup than the Sudo tunnel plus the
``nsenter`` command line utility found in ``linux-utils`` package.


Args:
pid (int): the process id that will be used as parent process for the
python interpreter

Kwargs:
keep_environ (bool): specify if the caller's environment should be kept

namespaces (list): optional list of namespaces to enter, valid
namespaces are: 'mount', 'uts', 'ipc', 'net', 'pid'
Defautls to all namespaces.
"""

def __init__(self, pid, keep_environ=False, namespaces=None):
self.pid = int(pid)
self.host = 'PID#%s@localhost' % self.pid
self.keep_environ = keep_environ
valid_namespaces = set(('mount', 'uts', 'ipc', 'net', 'pid'))
if namespaces and not set(namespaces).issubset(valid_namespaces):
raise ValueError("Invalid namespace %s" %
(namespaces - valid_namespaces))
self.namespaces = namespaces or valid_namespaces
super(NSEnter, self).__init__()

def cmd_args(self):
uid = os.getuid()
gid = os.getegid()
args = []
if uid != 0:
args += ['sudo', '--non-interactive']
args += ['nsenter', '-t', str(self.pid)]
args += ['--%s' % name for name in self.namespaces]
args += ['-S', str(uid), '-G', str(gid)]
if not self.keep_environ:
env = self.get_environ()
args += ['--', 'env', '-i', '-'] + env
else:
args.append('--')
args += super(NSEnter, self).cmd_args()
return args

def get_environ(self):
with open('/proc/%d/environ' % self.pid, 'r') as f:
return [var for var in f.read().split('\x00') if var.strip()]

def close(self):
self.wpipe.close()
self.proc.wait()


class Docker(SubprocessTunnel):
"""A tunnel connected to a throwaway Docker container.

Expand Down
6 changes: 6 additions & 0 deletions doc/tunnels.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Docker
.. autoclass:: Docker


NSenter
''''''

.. autoclass:: NSenter


Subprocess
''''''''''

Expand Down