From 119d0323892c65e8169ae57e42bbe8e3517551a3 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Thu, 13 Aug 2015 11:16:42 +0530 Subject: [PATCH] Be more explicit about why SSH arguments are added This adds vvvvv log messages that spell out in detail where each SSH command-line argument is obtained from. Unfortunately, we can't be sure if, say, self._play_context.remote_user is obtained from ANSIBLE_REMOTE_USER in the environment, remote_user in ansible.cfg, -u on the command line, or an ansible_ssh_user setting in the inventory or on a task or play. In some cases, e.g. timeout, we can't even be sure if it was set by the user or just a default. Nevertheless, on the theory that at five v's you can use all the hints available, I've mentioned the possible sources in the log messages. Note that this caveat applies only to the arguments that ssh.py adds by itself. In the case of ssh_args and ssh_extra_args, we know where they are from, and say so, though we can't say WHERE in the inventory they may be set (e.g. in host_vars or group_vars etc.). --- lib/ansible/plugins/connections/ssh.py | 105 +++++++++++++++++++------ 1 file changed, 82 insertions(+), 23 deletions(-) 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