Merge pull request #242 from dw/dmw

su method, CTRL+C exception handler, real host key checking modes
pull/246/head
dw 7 years ago committed by GitHub
commit 4f46d8a1e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -74,6 +74,9 @@ echo --- ansible/inventory/hosts: ----
cat ansible/inventory/hosts cat ansible/inventory/hosts
echo --- echo ---
# Now we have real host key checking, we need to turn it off. :)
export ANSIBLE_HOST_KEY_CHECKING=False
echo travis_fold:end:job_setup echo travis_fold:end:job_setup

@ -60,10 +60,15 @@ def _connect_local(spec):
def _connect_ssh(spec): def _connect_ssh(spec):
if C.HOST_KEY_CHECKING:
check_host_keys = 'enforce'
else:
check_host_keys = 'ignore'
return { return {
'method': 'ssh', 'method': 'ssh',
'kwargs': { 'kwargs': {
'check_host_keys': False, # TODO 'check_host_keys': check_host_keys,
'hostname': spec['remote_addr'], 'hostname': spec['remote_addr'],
'username': spec['remote_user'], 'username': spec['remote_user'],
'password': spec['password'], 'password': spec['password'],
@ -131,6 +136,20 @@ def _connect_setns(spec):
} }
def _connect_su(spec):
return {
'method': 'su',
'enable_lru': True,
'kwargs': {
'username': spec['become_user'],
'password': spec['become_pass'],
'python_path': spec['python_path'],
'su_path': spec['become_exe'],
'connect_timeout': spec['timeout'],
}
}
def _connect_sudo(spec): def _connect_sudo(spec):
return { return {
'method': 'sudo', 'method': 'sudo',
@ -146,6 +165,20 @@ def _connect_sudo(spec):
} }
def _connect_mitogen_su(spec):
# su as a first-class proxied connection, not a become method.
return {
'method': 'su',
'kwargs': {
'username': spec['remote_user'],
'password': spec['password'],
'python_path': spec['python_path'],
'su_path': spec['become_exe'],
'connect_timeout': spec['timeout'],
}
}
def _connect_mitogen_sudo(spec): def _connect_mitogen_sudo(spec):
# sudo as a first-class proxied connection, not a become method. # sudo as a first-class proxied connection, not a become method.
return { return {
@ -170,7 +203,9 @@ CONNECTION_METHOD = {
'machinectl': _connect_machinectl, 'machinectl': _connect_machinectl,
'setns': _connect_setns, 'setns': _connect_setns,
'ssh': _connect_ssh, 'ssh': _connect_ssh,
'su': _connect_su,
'sudo': _connect_sudo, 'sudo': _connect_sudo,
'mitogen_su': _connect_mitogen_su,
'mitogen_sudo': _connect_mitogen_sudo, 'mitogen_sudo': _connect_mitogen_sudo,
} }
@ -266,8 +301,8 @@ class Connection(ansible.plugins.connection.ConnectionBase):
#: target machine (i.e. via sudo). #: target machine (i.e. via sudo).
context = None context = None
#: Only sudo is supported for now. #: Only sudo and su are supported for now.
become_methods = ['sudo'] become_methods = ['sudo', 'su']
#: Set to 'ansible_python_interpreter' by on_action_run(). #: Set to 'ansible_python_interpreter' by on_action_run().
python_path = None python_path = None

@ -0,0 +1,43 @@
# Copyright 2017, David Wilson
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import os.path
import sys
try:
import ansible_mitogen.connection
except ImportError:
base_dir = os.path.dirname(__file__)
sys.path.insert(0, os.path.abspath(os.path.join(base_dir, '../../..')))
del base_dir
import ansible_mitogen.connection
class Connection(ansible_mitogen.connection.Connection):
transport = 'mitogen_su'

@ -110,11 +110,10 @@ Installation
Noteworthy Differences Noteworthy Differences
---------------------- ----------------------
* Ansible 2.3, 2.4 and 2.5 are supported. File bugs to register interest in * Ansible 2.3, 2.4 and 2.5 are supported on Python 2.7.
older releases.
* The ``sudo`` become method is available and ``su`` is planned. File bugs to * The ``su`` and ``sudo`` become methods are available. File bugs to register
register interest in additional methods. interest in more.
* The `docker <https://docs.ansible.com/ansible/2.5/plugins/connection/docker.html>`_, * The `docker <https://docs.ansible.com/ansible/2.5/plugins/connection/docker.html>`_,
`jail <https://docs.ansible.com/ansible/2.5/plugins/connection/jail.html>`_, `jail <https://docs.ansible.com/ansible/2.5/plugins/connection/jail.html>`_,
@ -123,8 +122,9 @@ Noteworthy Differences
`lxd <https://docs.ansible.com/ansible/2.5/plugins/connection/lxd.html>`_, `lxd <https://docs.ansible.com/ansible/2.5/plugins/connection/lxd.html>`_,
and `ssh <https://docs.ansible.com/ansible/2.5/plugins/connection/ssh.html>`_ and `ssh <https://docs.ansible.com/ansible/2.5/plugins/connection/ssh.html>`_
built-in connection types are supported, along with Mitogen-specific built-in connection types are supported, along with Mitogen-specific
:ref:`machinectl <machinectl>`, :ref:`mitogen_sudo <sudo>`, and :ref:`machinectl <machinectl>`, :ref:`mitogen_su <su>`, :ref:`mitogen_sudo
:ref:`setns <setns>` types. File bugs to register interest in others. <sudo>`, and :ref:`setns <setns>` types. File bugs to register interest in
others.
* Local commands execute in a reuseable interpreter created identically to * Local commands execute in a reuseable interpreter created identically to
interpreters on targets. Presently one interpreter per ``become_user`` interpreters on targets. Presently one interpreter per ``become_user``
@ -558,6 +558,31 @@ process.
as ``/bin/machinectl``. as ``/bin/machinectl``.
.. _su:
Su
~~
Su can be used as a connection method that supports connection delegation, or
as a become method.
When used as a become method:
* ``ansible_python_interpreter``
* ``ansible_su_exe``, ``ansible_become_exe``
* ``ansible_su_user``, ``ansible_become_user`` (default: ``root``)
* ``ansible_su_pass``, ``ansible_become_pass`` (default: assume passwordless)
* ``su_flags``, ``become_flags``
* ansible.cfg: ``timeout``
When used as the ``mitogen_su`` connection method:
* The inventory hostname has no special meaning.
* ``ansible_user``: username to su as.
* ``ansible_password``: password to su as.
* ``ansible_python_interpreter``
.. _sudo: .. _sudo:
Sudo Sudo

@ -780,6 +780,36 @@ Router Class
will be searched if given as a filename. Defaults to will be searched if given as a filename. Defaults to
``machinectl``. ``machinectl``.
.. method:: su (username=None, password=None, su_path=None, password_prompt=None, incorrect_prompts=None, \**kwargs)
Construct a context on the local machine over a ``su`` invocation. The
``su`` process is started in a newly allocated pseudo-terminal, and
supports typing interactive passwords.
Accepts all parameters accepted by :py:meth:`local`, in addition to:
:param str username:
Username to pass to ``su``, defaults to ``root``.
:param str password:
The account password to use if requested.
:param str su_path:
Filename or complete path to the ``su`` binary. ``PATH`` will be
searched if given as a filename. Defaults to ``su``.
:param str password_prompt:
The string to wait to that signals ``su`` is requesting a password.
Defaults to ``Password:``.
:param str password_prompt:
The string that signal a request for the password. Defaults to
``Password:``.
:param str incorrect_prompts:
Strings that signal the password is incorrect. Defaults to `("su:
sorry", "su: authentication failure")`.
:raises mitogen.su.PasswordError:
A password was requested but none was provided, the supplied
password was incorrect, or (on BSD) the target account did not
exist.
.. method:: sudo (username=None, sudo_path=None, password=None, \**kwargs) .. method:: sudo (username=None, sudo_path=None, password=None, \**kwargs)
Construct a context on the local machine over a ``sudo`` invocation. Construct a context on the local machine over a ``sudo`` invocation.
@ -812,7 +842,7 @@ Router Class
:py:class:`mitogen.core.StreamError` to be raised, and that :py:class:`mitogen.core.StreamError` to be raised, and that
attributes of the stream match the actual behaviour of ``sudo``. attributes of the stream match the actual behaviour of ``sudo``.
.. method:: ssh (hostname, username=None, ssh_path=None, port=None, check_host_keys=True, password=None, identity_file=None, compression=True, \**kwargs) .. method:: ssh (hostname, username=None, ssh_path=None, port=None, check_host_keys='enforce', password=None, identity_file=None, compression=True, \**kwargs)
Construct a remote context over a ``ssh`` invocation. The ``ssh`` Construct a remote context over a ``ssh`` invocation. The ``ssh``
process is started in a newly allocated pseudo-terminal, and supports process is started in a newly allocated pseudo-terminal, and supports
@ -828,10 +858,16 @@ Router Class
:param int port: :param int port:
Port number to connect to; default is unspecified, which causes SSH Port number to connect to; default is unspecified, which causes SSH
to pick the port number. to pick the port number.
:param bool check_host_keys: :param str check_host_keys:
If ``False``, arrange for SSH to perform no verification of host Specifies the SSH host key checking mode:
keys. If ``True``, cause SSH to pick the default behaviour, which
is usually to verify host keys. * ``ignore``: no host key checking is performed. Connections never
fail due to an unknown or changed host key.
* ``accept``: known hosts keys are checked to ensure they match,
new host keys are automatically accepted and verified in future
connections.
* ``enforce``: known host keys are checke to ensure they match,
unknown hosts cause a connection failure.
:param str password: :param str password:
Password to type if/when ``ssh`` requests it. If not specified and Password to type if/when ``ssh`` requests it. If not specified and
a password is requested, :py:class:`mitogen.ssh.PasswordError` is a password is requested, :py:class:`mitogen.ssh.PasswordError` is

@ -498,6 +498,7 @@ class Importer(object):
'service', 'service',
'setns', 'setns',
'ssh', 'ssh',
'su',
'sudo', 'sudo',
'utils', 'utils',
]} ]}
@ -1287,7 +1288,7 @@ class Router(object):
if respondent: if respondent:
assert policy is None assert policy is None
def policy(msg, _stream): def policy(msg, _stream):
return msg.src_id == respondent.context_id return msg.is_dead or msg.src_id == respondent.context_id
def on_disconnect(): def on_disconnect():
if handle in self._handle_map: if handle in self._handle_map:
fn(Message.dead()) fn(Message.dead())
@ -1683,6 +1684,8 @@ class ExternalContext(object):
_profile_hook('main', self._dispatch_calls) _profile_hook('main', self._dispatch_calls)
_v and LOG.debug('ExternalContext.main() normal exit') _v and LOG.debug('ExternalContext.main() normal exit')
except KeyboardInterrupt:
LOG.debug('KeyboardInterrupt received, exiting gracefully.')
except BaseException: except BaseException:
LOG.exception('ExternalContext.main() crashed') LOG.exception('ExternalContext.main() crashed')
raise raise

@ -1031,12 +1031,15 @@ class Router(mitogen.core.Router):
def setns(self, **kwargs): def setns(self, **kwargs):
return self.connect('setns', **kwargs) return self.connect('setns', **kwargs)
def ssh(self, **kwargs): def su(self, **kwargs):
return self.connect('ssh', **kwargs) return self.connect('su', **kwargs)
def sudo(self, **kwargs): def sudo(self, **kwargs):
return self.connect('sudo', **kwargs) return self.connect('sudo', **kwargs)
def ssh(self, **kwargs):
return self.connect('ssh', **kwargs)
class ProcessMonitor(object): class ProcessMonitor(object):
def __init__(self): def __init__(self):

@ -45,12 +45,18 @@ LOG = logging.getLogger('mitogen')
PASSWORD_PROMPT = 'password:' PASSWORD_PROMPT = 'password:'
PERMDENIED_PROMPT = 'permission denied' PERMDENIED_PROMPT = 'permission denied'
HOSTKEY_REQ_PROMPT = 'are you sure you want to continue connecting (yes/no)?'
HOSTKEY_FAIL = 'host key verification failed.'
class PasswordError(mitogen.core.StreamError): class PasswordError(mitogen.core.StreamError):
pass pass
class HostKeyError(mitogen.core.StreamError):
pass
class Stream(mitogen.parent.Stream): class Stream(mitogen.parent.Stream):
create_child = staticmethod(mitogen.parent.hybrid_tty_create_child) create_child = staticmethod(mitogen.parent.hybrid_tty_create_child)
python_path = 'python2.7' python_path = 'python2.7'
@ -62,16 +68,24 @@ class Stream(mitogen.parent.Stream):
#: The path to the SSH binary. #: The path to the SSH binary.
ssh_path = 'ssh' ssh_path = 'ssh'
hostname = None
username = None
port = None
identity_file = None identity_file = None
password = None password = None
port = None
ssh_args = None ssh_args = None
check_host_keys_msg = 'host_keys= must be set to accept, enforce or ignore'
def construct(self, hostname, username=None, ssh_path=None, port=None, def construct(self, hostname, username=None, ssh_path=None, port=None,
check_host_keys=True, password=None, identity_file=None, check_host_keys='enforce', password=None, identity_file=None,
compression=True, ssh_args=None, keepalive_enabled=True, compression=True, ssh_args=None, keepalive_enabled=True,
keepalive_count=3, keepalive_interval=15, **kwargs): keepalive_count=3, keepalive_interval=15, **kwargs):
super(Stream, self).construct(**kwargs) super(Stream, self).construct(**kwargs)
if check_host_keys not in ('accept', 'enforce', 'ignore'):
raise ValueError(self.check_host_keys_msg)
self.hostname = hostname self.hostname = hostname
self.username = username self.username = username
self.port = port self.port = port
@ -93,8 +107,6 @@ class Stream(mitogen.parent.Stream):
def get_boot_command(self): def get_boot_command(self):
bits = [self.ssh_path] bits = [self.ssh_path]
# bits += ['-o', 'BatchMode yes']
if self.username: if self.username:
bits += ['-l', self.username] bits += ['-l', self.username]
if self.port is not None: if self.port is not None:
@ -110,7 +122,11 @@ class Stream(mitogen.parent.Stream):
'-o', 'ServerAliveInterval %s' % (self.keepalive_interval,), '-o', 'ServerAliveInterval %s' % (self.keepalive_interval,),
'-o', 'ServerAliveCountMax %s' % (self.keepalive_count,), '-o', 'ServerAliveCountMax %s' % (self.keepalive_count,),
] ]
if not self.check_host_keys: if self.check_host_keys == 'enforce':
bits += ['-o', 'StrictHostKeyChecking yes']
if self.check_host_keys == 'accept':
bits += ['-o', 'StrictHostKeyChecking ask']
elif self.check_host_keys == 'ignore':
bits += [ bits += [
'-o', 'StrictHostKeyChecking no', '-o', 'StrictHostKeyChecking no',
'-o', 'UserKnownHostsFile /dev/null', '-o', 'UserKnownHostsFile /dev/null',
@ -131,6 +147,27 @@ class Stream(mitogen.parent.Stream):
auth_incorrect_msg = 'SSH authentication is incorrect' auth_incorrect_msg = 'SSH authentication is incorrect'
password_incorrect_msg = 'SSH password is incorrect' password_incorrect_msg = 'SSH password is incorrect'
password_required_msg = 'SSH password was requested, but none specified' password_required_msg = 'SSH password was requested, but none specified'
hostkey_config_msg = (
'SSH requested permission to accept unknown host key, but '
'check_host_keys=ignore. This is likely due to ssh_args= '
'conflicting with check_host_keys=. Please correct your '
'configuration.'
)
hostkey_failed_msg = (
'check_host_keys is set to enforce, and SSH reported an unknown '
'or changed host key.'
)
def _host_key_prompt(self):
if self.check_host_keys == 'accept':
LOG.debug('%r: accepting host key', self)
self.tty_stream.transmit_side.write('y\n')
return
# _host_key_prompt() should never be reached with ignore or enforce
# mode, SSH should have handled that. User's ssh_args= is conflicting
# with ours.
raise HostKeyError(self.hostkey_config_msg)
def _connect_bootstrap(self, extra_fd): def _connect_bootstrap(self, extra_fd):
self.tty_stream = mitogen.parent.TtyLogStream(extra_fd, self) self.tty_stream = mitogen.parent.TtyLogStream(extra_fd, self)
@ -146,6 +183,10 @@ class Stream(mitogen.parent.Stream):
if buf.endswith('EC0\n'): if buf.endswith('EC0\n'):
self._ec0_received() self._ec0_received()
return return
elif HOSTKEY_REQ_PROMPT in buf.lower():
self._host_key_prompt()
elif HOSTKEY_FAIL in buf.lower():
raise HostKeyError(self.hostkey_failed_msg)
elif PERMDENIED_PROMPT in buf.lower(): elif PERMDENIED_PROMPT in buf.lower():
if self.password is not None and password_sent: if self.password is not None and password_sent:
raise PasswordError(self.password_incorrect_msg) raise PasswordError(self.password_incorrect_msg)
@ -154,7 +195,7 @@ class Stream(mitogen.parent.Stream):
elif PASSWORD_PROMPT in buf.lower(): elif PASSWORD_PROMPT in buf.lower():
if self.password is None: if self.password is None:
raise PasswordError(self.password_required_msg) raise PasswordError(self.password_required_msg)
LOG.debug('sending password') LOG.debug('%r: sending password', self)
self.tty_stream.transmit_side.write(self.password + '\n') self.tty_stream.transmit_side.write(self.password + '\n')
password_sent = True password_sent = True

@ -0,0 +1,113 @@
# Copyright 2017, David Wilson
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import logging
import os
import mitogen.core
import mitogen.parent
LOG = logging.getLogger(__name__)
class PasswordError(mitogen.core.StreamError):
pass
class Stream(mitogen.parent.Stream):
# TODO: BSD su cannot handle stdin being a socketpair, but it does let the
# child inherit fds from the parent. So we can still pass a socketpair in
# for hybrid_tty_create_child(), there just needs to be either a shell
# snippet or bootstrap support for fixing things up afterwards.
create_child = staticmethod(mitogen.parent.tty_create_child)
#: Once connected, points to the corresponding TtyLogStream, allowing it to
#: be disconnected at the same time this stream is being torn down.
username = 'root'
password = None
su_path = 'su'
password_prompt = 'password:'
incorrect_prompts = (
'su: sorry', # BSD
'su: authentication failure', # Linux
)
def construct(self, username=None, password=None, su_path=None,
password_prompt=None, incorrect_prompts=None, **kwargs):
super(Stream, self).construct(**kwargs)
if username is not None:
self.username = username
if password is not None:
self.password = password
if su_path is not None:
self.su_path = su_path
if password_prompt is not None:
self.password_prompt = password_prompt.lower()
if incorrect_prompts is not None:
self.incorrect_prompts = map(str.lower, incorrect_prompts)
def connect(self):
super(Stream, self).connect()
self.name = 'su.' + self.username
def on_disconnect(self, broker):
super(Stream, self).on_disconnect(broker)
def get_boot_command(self):
argv = mitogen.parent.Argv(super(Stream, self).get_boot_command())
return [self.su_path, self.username, '-c', str(argv)]
password_incorrect_msg = 'su password is incorrect'
password_required_msg = 'su password is required'
def _connect_bootstrap(self, extra_fd):
password_sent = False
it = mitogen.parent.iter_read(
fds=[self.receive_side.fd],
deadline=self.connect_deadline,
)
for buf in it:
LOG.debug('%r: received %r', self, buf)
if buf.endswith('EC0\n'):
self._ec0_received()
return
if any(s in buf.lower() for s in self.incorrect_prompts):
if password_sent:
raise PasswordError(self.password_incorrect_msg)
elif self.password_prompt in buf.lower():
if self.password is None:
raise PasswordError(self.password_required_msg)
if password_sent:
raise PasswordError(self.password_incorrect_msg)
LOG.debug('sending password')
self.transmit_side.write(self.password + '\n')
password_sent = True
raise mitogen.core.StreamError('bootstrap failed')

@ -1,4 +1,5 @@
- import_playbook: su_password.yml
- import_playbook: sudo_flags_failure.yml - import_playbook: sudo_flags_failure.yml
- import_playbook: sudo_nonexistent.yml - import_playbook: sudo_nonexistent.yml
- import_playbook: sudo_nopassword.yml - import_playbook: sudo_nopassword.yml

@ -0,0 +1,58 @@
# Verify passwordful su behaviour
# Ansible can't handle this on OS X. I don't care why.
- name: integration/become/su_password.yml
hosts: test-targets
become_method: su
any_errors_fatal: true
tasks:
- name: Ensure su password absent but required.
shell: whoami
become: true
become_user: mitogen__user1
register: out
ignore_errors: true
when: is_mitogen
- assert:
that:
- out.failed
- (
('password is required' in out.msg) or
('password is required' in out.module_stderr)
)
when: is_mitogen
- name: Ensure password su incorrect.
shell: whoami
become: true
become_user: mitogen__user1
register: out
vars:
ansible_become_pass: nopes
ignore_errors: true
when: is_mitogen
- assert:
that: |
out.failed and (
('Incorrect su password' in out.msg) or
('su password is incorrect' in out.msg)
)
when: is_mitogen
- name: Ensure password su succeeds.
shell: whoami
become: true
become_user: mitogen__user1
register: out
vars:
ansible_become_pass: user1_password
when: is_mitogen
- assert:
that:
- out.stdout == 'mitogen__user1'
when: is_mitogen

@ -242,7 +242,7 @@ class DockerMixin(RouterMixin):
def docker_ssh(self, **kwargs): def docker_ssh(self, **kwargs):
kwargs.setdefault('hostname', self.dockerized_ssh.host) kwargs.setdefault('hostname', self.dockerized_ssh.host)
kwargs.setdefault('port', self.dockerized_ssh.port) kwargs.setdefault('port', self.dockerized_ssh.port)
kwargs.setdefault('check_host_keys', False) kwargs.setdefault('check_host_keys', 'ignore')
return self.router.ssh(**kwargs) return self.router.ssh(**kwargs)
def docker_ssh_any(self, **kwargs): def docker_ssh_any(self, **kwargs):

Loading…
Cancel
Save