setns: support changing user.

To match existing third party plugin.
pull/232/head
David Wilson 7 years ago
parent 947d35649c
commit 7c5bbc5168

@ -112,6 +112,26 @@ def _connect_lxc(spec):
}
def _connect_machinectl(spec):
return _connect_setns(dict(spec, mitogen_kind='machinectl'))
def _connect_setns(spec):
print 'ULTRAFLEEN', spec['remote_addr'], spec['remote_user']
return {
'method': 'setns',
'kwargs': {
'container': spec['remote_addr'],
'username': spec['remote_user'],
'python_path': spec['python_path'],
'kind': spec['mitogen_kind'],
'docker_path': spec['mitogen_docker_path'],
'lxc_info_path': spec['mitogen_lxc_info_path'],
'machinectl_path': spec['mitogen_machinectl_path'],
}
}
def _connect_sudo(spec):
return {
'method': 'sudo',
@ -132,6 +152,8 @@ CONNECTION_METHOD = {
'local': _connect_local,
'lxc': _connect_lxc,
'lxd': _connect_lxc,
'machinectl': _connect_machinectl,
'setns': _connect_setns,
'ssh': _connect_ssh,
'sudo': _connect_sudo,
}
@ -178,6 +200,10 @@ def config_from_play_context(transport, inventory_name, connection):
for term in shlex.split(s or '')
],
'mitogen_via': connection.mitogen_via,
'mitogen_kind': connection.mitogen_kind,
'mitogen_docker_path': connection.mitogen_docker_path,
'mitogen_lxc_info_path': connection.mitogen_lxc_info_path,
'mitogen_machinectl_path': connection.mitogen_machinectl_path,
}
@ -202,6 +228,10 @@ def config_from_hostvars(transport, inventory_name, connection,
'private_key_file': (hostvars.get('ansible_ssh_private_key_file') or
hostvars.get('ansible_private_key_file')),
'mitogen_via': hostvars.get('mitogen_via'),
'mitogen_kind': hostvars.get('mitogen_kind'),
'mitogen_docker_path': hostvars.get('mitogen_docker_path'),
'mitogen_lxc_info_path': hostvars.get('mitogen_lxc_info_path'),
'mitogen_machinectl_path': hostvars.get('mitogen_machinctl_path'),
})
@ -232,6 +262,18 @@ class Connection(ansible.plugins.connection.ConnectionBase):
#: Set to 'mitogen_via' by on_action_run().
mitogen_via = None
#: Set to 'mitogen_kind' by on_action_run().
mitogen_kind = None
#: Set to 'mitogen_docker_path' by on_action_run().
mitogen_docker_path = None
#: Set to 'mitogen_lxc_info_path' by on_action_run().
mitogen_lxc_info_path = None
#: Set to 'mitogen_lxc_info_path' by on_action_run().
mitogen_machinectl_path = None
#: Set to 'inventory_hostname' by on_action_run().
inventory_hostname = None
@ -267,6 +309,10 @@ class Connection(ansible.plugins.connection.ConnectionBase):
self.python_path = task_vars.get('ansible_python_interpreter',
'/usr/bin/python')
self.mitogen_via = task_vars.get('mitogen_via')
self.mitogen_kind = task_vars.get('mitogen_kind')
self.mitogen_docker_path = task_vars.get('mitogen_docker_path')
self.mitogen_lxc_info_path = task_vars.get('mitogen_lxc_info_path')
self.mitogen_machinectl_path = task_vars.get('mitogen_machinectl_path')
self.inventory_hostname = task_vars['inventory_hostname']
self.host_vars = task_vars['hostvars']
self.close(new_task=True)

@ -63,12 +63,11 @@ def wrap_action_loader__get(name, *args, **kwargs):
def wrap_connection_loader__get(name, play_context, new_stdin, **kwargs):
"""
While the mitogen strategy is active, rewrite connection_loader.get() calls
for the 'ssh' and 'local' transports into corresponding requests for the
'mitogen' connection type, passing the original transport name into it as
an argument, so that it can emulate the original type.
While the strategy is active, rewrite connection_loader.get() calls for
some transports into requests for a compatible Mitogen transport.
"""
if name in ('ssh', 'local', 'docker', 'lxc', 'lxd', 'jail'):
if name in ('docker', 'jail', 'local', 'lxc',
'lxd', 'machinectl', 'setns', 'ssh'):
name = 'mitogen_' + name
return connection_loader__get(name, play_context, new_stdin, **kwargs)

@ -159,7 +159,7 @@ def transfer_file(context, in_path, out_path, sync=False, set_owner=False):
prefix='.ansible_mitogen_transfer-',
dir=os.path.dirname(out_path))
fp = os.fdopen(fd, 'wb', mitogen.core.CHUNK_SIZE)
LOG.debug('transfer_file(%r) tempory file: %s', out_path, tmp_path)
LOG.debug('transfer_file(%r) temporary file: %s', out_path, tmp_path)
try:
try:

@ -455,6 +455,20 @@ connection delegation is supported.
* ``ansible_user``: Name of user within the container to execute as.
Machinectl
~~~~~~~~~~
Behaves like `machinectl
<https://github.com/BaxterStockman/ansible-connection-machinectl>`_ except
connection delegation is supported. This is a lightweight wrapper around the
``setns`` method below.
* ``ansible_host``: Name of Docker container (default: inventory hostname).
* ``ansible_user``: Name of user within the container to execute as.
* ``mitogen_machinectl_path``: path to ``machinectl`` command if not available
as ``/bin/machinectl``.
Sudo
~~~~
@ -478,9 +492,10 @@ supported.
Utility programs must still be installed to discover the PID of the container's
root process.
* ``mitogen_container_kind``: one of ``docker``, ``lxc`` or ``machinectl``.
* ``mitogen_kind``: one of ``docker``, ``lxc`` or ``machinectl``.
* ``ansible_host``: Name of container as it is known to the corresponding tool
(default: inventory hostname).
* ``ansible_user``: Name of user within the container to execute as.
* ``mitogen_docker_path``: path to Docker if not available on the system path.
* ``mitogen_lxc_info_path``: path to ``lxc-info`` command if not available as
``/usr/bin/lxc-info``.
@ -537,6 +552,7 @@ rather than the LXC Python bindings, as is usual with the ``lxc`` method.
The ``lxc-attach`` command must be available on the host machine.
* ``ansible_python_interpreter``
* ``ansible_host``: Name of LXC container (default: inventory hostname).

@ -484,9 +484,9 @@ def _proxy_connect(name, method_name, kwargs, econtext):
return {
'id': None,
'name': None,
'msg': '%s (error occurred on host %s)' % (
sys.exc_info()[1],
'msg': 'error occurred on host %s: %s' % (
socket.gethostname(),
sys.exc_info()[1],
),
}

@ -27,9 +27,12 @@
# POSSIBILITY OF SUCH DAMAGE.
import ctypes
import grp
import logging
import os
import pwd
import subprocess
import sys
import mitogen.core
import mitogen.parent
@ -53,11 +56,16 @@ def setns(kind, fd):
def _run_command(args):
proc = subprocess.Popen(
args=args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
)
argv = mitogen.parent.Argv(args)
try:
proc = subprocess.Popen(
args=args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
)
except OSError:
e = sys.exc_info()[1]
raise Error('could not execute %s: %s', argv, e)
output, _ = proc.communicate()
if not proc.returncode:
@ -97,6 +105,7 @@ def get_machinectl_pid(path, name):
class Stream(mitogen.parent.Stream):
container = None
username = None
kind = None
docker_path = 'docker'
lxc_info_path = 'lxc-info'
@ -108,7 +117,7 @@ class Stream(mitogen.parent.Stream):
'machinectl': ('machinectl_path', get_machinectl_pid),
}
def construct(self, container, kind, docker_path=None,
def construct(self, container, kind, username=None, docker_path=None,
lxc_info_path=None, machinectl_path=None, **kwargs):
super(Stream, self).construct(**kwargs)
if kind not in self.GET_LEADER_BY_KIND:
@ -116,6 +125,8 @@ class Stream(mitogen.parent.Stream):
self.container = container
self.kind = kind
if username:
self.username = username
if docker_path:
self.docker_path = docker_path
if lxc_info_path:
@ -140,12 +151,43 @@ class Stream(mitogen.parent.Stream):
except Exception, e:
raise Error(str(e))
os.chroot('/proc/%s/root' % (self.leader_pid,))
os.chdir('/proc/%s/root' % (self.leader_pid,))
os.chroot('.')
os.chdir('/')
for fp in ns_fps:
setns(fp.name, fp.fileno())
fp.close()
for sym in 'endpwent', 'endgrent', 'endspent', 'endsgent':
try:
getattr(LIBC, sym)()
except AttributeError:
pass
if self.username:
try:
os.setgroups([grent.gr_gid
for grent in grp.getgrall()
if self.username in grent.gr_mem])
pwent = pwd.getpwnam(self.username)
os.setreuid(pwent.pw_uid, pwent.pw_uid)
# shadow-4.4/libmisc/setupenv.c. Not done: MAIL, PATH
os.environ.update({
'HOME': pwent.pw_dir,
'SHELL': pwent.pw_shell or '/bin/sh',
'LOGNAME': self.username,
'USER': self.username,
})
if ((os.path.exists(pwent.pw_dir) and
os.access(pwent.pw_dir, os.X_OK))):
os.chdir(pwent.pw_dir)
except Exception:
e = sys.exc_info()[1]
raise Error(self.username_msg, self.username, self.container,
type(e).__name__, e)
username_msg = 'while transitioning to user %r in container %r: %s: %s'
def get_boot_command(self):
# With setns(CLONE_NEWPID), new children of the caller receive a new
# PID namespace, however the caller's namespace won't change. That

@ -68,7 +68,7 @@ class StreamErrorTest(testlib.RouterMixin, testlib.TestCase):
connect_timeout=3,
)
)
self.assertEquals(e.args[0], "EOF on stream; last 300 bytes received: ''")
self.assertTrue("EOF on stream; last 300 bytes received: ''" in e.args[0])
def test_direct_enoent(self):
e = self.assertRaises(mitogen.core.StreamError,
@ -89,8 +89,8 @@ class StreamErrorTest(testlib.RouterMixin, testlib.TestCase):
connect_timeout=3,
)
)
prefix = 'Child start failed: [Errno 2] No such file or directory.'
self.assertTrue(e.args[0].startswith(prefix))
s = 'Child start failed: [Errno 2] No such file or directory.'
self.assertTrue(s in e.args[0])
class ContextTest(testlib.RouterMixin, unittest2.TestCase):

Loading…
Cancel
Save