diff --git a/lib/ansible/plugins/connections/ssh.py b/lib/ansible/plugins/connections/ssh.py index 369d1822488..4edbf849d72 100644 --- a/lib/ansible/plugins/connections/ssh.py +++ b/lib/ansible/plugins/connections/ssh.py @@ -73,6 +73,22 @@ class Connection(ConnectionBase): ''' used to identify this connection object from other classes ''' return 'ssh' + def _split_args(self, argstring): + """ + Takes a string like '-o Foo=1 -o Bar="foo bar"' and returns a + list ['-o', 'Foo=1', '-o', 'Bar=foo bar'] that can be added to + the argument list. The list will not contain any empty elements. + """ + return [x.strip() for x in shlex.split(argstring) if x.strip()] + + def add_args(self, explanation, args): + """ + Adds the given args to _common_args and displays a + caller-supplied explanation of why they were added. + """ + self._common_args += args + self._display.vvvvv('SSH: ' + explanation + ': (%s)' % ')('.join(args), host=self._play_context.remote_addr) + def _connect(self): ''' connect to the remote host ''' @@ -81,16 +97,25 @@ class Connection(ConnectionBase): if self._connected: return self - ssh_args = self.ssh_args or C.ANSIBLE_SSH_ARGS - if ssh_args is not None: - # make sure there is no empty string added as this can produce weird errors - self._common_args += [x.strip() for x in shlex.split(ssh_args) if x.strip()] + # We start with ansible_ssh_args from the inventory if it's set, + # or [ssh_connection]ssh_args from ansible.cfg, or the default + # Control* settings. + + if self.ssh_args: + args = self._split_args(self.ssh_args) + self.add_args("inventory set ansible_ssh_args", args) + elif C.ANSIBLE_SSH_ARGS: + args = self._split_args(C.ANSIBLE_SSH_ARGS) + self.add_args("ansible.cfg set ssh_args", args) else: - self._common_args += ( + args = ( "-o", "ControlMaster=auto", - "-o", "ControlPersist=60s", - "-o", "ControlPath=\"{0}\"".format(C.ANSIBLE_SSH_CONTROL_PATH % dict(directory=self._cp_dir)), + "-o", "ControlPersist=60s" ) + self.add_args("default arguments", args) + + # If any of the above have set ControlPersist but not a + # ControlPath, add one ourselves. cp_in_use = False cp_path_set = False @@ -101,34 +126,68 @@ class Connection(ConnectionBase): cp_path_set = True if cp_in_use and not cp_path_set: - self._common_args += ("-o", "ControlPath=\"{0}\"".format( + args = ("-o", "ControlPath=\"{0}\"".format( C.ANSIBLE_SSH_CONTROL_PATH % dict(directory=self._cp_dir)) ) + self.add_args("found only ControlPersist; added ControlPath", args) if not C.HOST_KEY_CHECKING: - self._common_args += ("-o", "StrictHostKeyChecking=no") + self.add_args( + "ANSIBLE_HOST_KEY_CHECKING/host_key_checking disabled", + ("-o", "StrictHostKeyChecking=no") + ) if self._play_context.port is not None: - self._common_args += ("-o", "Port={0}".format(self._play_context.port)) - if self._play_context.private_key_file is not None: - self._common_args += ("-o", "IdentityFile=\"{0}\"".format(os.path.expanduser(self._play_context.private_key_file))) + self.add_args( + "ANSIBLE_REMOTE_PORT/remote_port/ansible_ssh_port set", + ("-o", "Port={0}".format(self._play_context.port)) + ) + + key = self._play_context.private_key_file + if key: + self.add_args( + "ANSIBLE_PRIVATE_KEY_FILE/private_key_file/ansible_ssh_private_key_file set", + ("-o", "IdentityFile=\"{0}\"".format(os.path.expanduser(key))) + ) + if self._play_context.password: - self._common_args += ("-o", "GSSAPIAuthentication=no", - "-o", "PubkeyAuthentication=no") + self.add_args( + "ansible_password/ansible_ssh_pass set", ( + "-o", "GSSAPIAuthentication=no", + "-o", "PubkeyAuthentication=no" + ) + ) else: - self._common_args += ("-o", "KbdInteractiveAuthentication=no", - "-o", "PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey", - "-o", "PasswordAuthentication=no") - if self._play_context.remote_user is not None and self._play_context.remote_user != pwd.getpwuid(os.geteuid())[0]: - self._common_args += ("-o", "User={0}".format(self._play_context.remote_user)) - self._common_args += ("-o", "ConnectTimeout={0}".format(self._play_context.timeout)) + self.add_args( + "ansible_password/ansible_ssh_pass not set", ( + "-o", "KbdInteractiveAuthentication=no", + "-o", "PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey", + "-o", "PasswordAuthentication=no" + ) + ) + + user = self._play_context.remote_user + if user and user != pwd.getpwuid(os.geteuid())[0]: + self.add_args( + "ANSIBLE_REMOTE_USER/remote_user/ansible_ssh_user/user/-u set", + ("-o", "User={0}".format(self._play_context.remote_user)) + ) + + self.add_args( + "ANSIBLE_TIMEOUT/timeout set", + ("-o", "ConnectTimeout={0}".format(self._play_context.timeout)) + ) # If any extra SSH arguments are specified in the inventory for # this host, or specified as an override on the command line, # add them in. - extra_args = self._play_context.ssh_extra_args or self.ssh_extra_args - if extra_args is not None: - self._common_args += [x.strip() for x in shlex.split(extra_args) if x.strip()] + + if self._play_context.ssh_extra_args: + args = self._split_args(self._play_context.ssh_extra_args) + self.add_args("command-line added --ssh-extra-args", args) + elif self.ssh_extra_args: + args = self._split_args(self.ssh_extra_args) + self.add_args("inventory added ansible_ssh_extra_args", args) self._connected = True