Paramiko and ssh-agent

Hello.

Now Paramiko transport use have the following logic regarding to usage
of ssh-agent (and default ~/.ssh/id_{d,r}sa keys):

ssh.connect(self.host, username=user, allow_agent=True, \
look_for_keys=True<...>

So it always uses ssh-agent first, even if password is supplied to
Ansible with -k option. Also it always tries default SSH keys [1].

This is a problem if ssh-agent stores keys with 'confirmation required'
mode (keys added with ssh-add -c). Since Ansible connects to target for
every separate task, ssh-agent asks for confirmation way too much times
for big playbooks. Despite the fact that authtorization is performed
with password and ssh-agent is not related at all.

(As a side note, this 'try everything what we can' approach can mask
errors and does not look like the best way for me even if it would not
cause troubles)

I thought that it may be reasonable to stop checking default id_{d,r}sa
keys if key or password is provided. And if password is provided, then
also stop to talk with ssh-agent.
But Paramiko can use 'password' argument to SSHClient.connect() both as
password to decrypt private key and as password to authenticate with SSH
server. So it is not clear for Ansible if password provided with -k
option is intended for authentication via SSH or as passphrase for
~/.ssh/id_rsa.

So I'm not sure what can be done here. May be separate options for
private key password and remote system SSH password, not -k for both?
Would such patch be useful for anyone and accepted?

I understand that most Ansible users are not affected. But we placed
Ansible at a central server and allow it to login to our systems by
means of agent forwarding. And so it is desirable to keep keys with
'confirmation required' mode. But tons of unneeded requests to ssh-agent
make Ansible unusable. Paramiko transport is really needed for initial
setup as native 'ssh' supports only keys, not passwords.

[1]:
http://www.lag.net/paramiko/docs/paramiko.SSHClient-class.html#connect

My suggestion would be just use -c ssh as paramiko is there for ease of initial usage and for older SSH hosts that can't do ControlPersist.

-- Michael

Yes, we use -c ssh, but Paramiko is needed for basic host setup
(includes SSHd configuration and setting authorized_keys). Native ssh
does not support password authentication.

By the way, I bet Debian and Red Hat systems without ControlPersist are
quite usual in bigger companies (where configuration management systems
are important).

You can use a newer SSH on the control machine to talk to older RHEL systems, etc.

I don't think we want to change our paramiko connection policy but since modules are pluggable you could easily tweak it and make a variant connection plugin to meet your use case.

-- Michael

So it always uses ssh-agent first, even if password is supplied to
Ansible with -k option. Also it always tries default SSH keys [1].

...

I thought that it may be reasonable to stop checking default id_{d,r}sa
keys if key or password is provided. And if password is provided, then
also stop to talk with ssh-agent.

IIRC, you can `unset SSH_AUTH_SOCK` to prevent it from talking to ssh-agent.

For those interested in (or forced to) running Ansible without ControlPersist, paramiko could work a lot better if each Runner task did not spawn a new connection, but connections would be reused and connections idle for a certain idle time would be closed (in fact this would be ControlPersist implemented in Ansible).

It was discussed here:

     https://github.com/ansible/ansible/issues/987

This would speed up Ansible considerably using paramiko. We just need someone with the skills and persistence to implement it :wink:

This would speed up Ansible considerably using paramiko. We just need
someone with the skills and persistence to implement it :wink:

Yep, the issue/idea queue is quite large at the moment. If someone
else wants to take a look (preferably someone who has a vested
interest in using the paramiko connection type over a decent number of
hosts), that would be welcome.

We already patched code a bit, but yes, deleting SSH_AUTH_SOCK helps
and would be easier, thank you!

diff --git a/lib/ansible/runner/connection_plugins/paramiko_ssh.py b/lib/ansible/runner/connection_plugins/paramiko_ssh.py
index d4083f5..e89c778 100644
--- a/lib/ansible/runner/connection_plugins/paramiko_ssh.py
+++ b/lib/ansible/runner/connection_plugins/paramiko_ssh.py
@@ -58,8 +58,18 @@ class Connection(object):
         ssh = paramiko.SSHClient()
         ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

+ # If password is given, don't mess with anything else at all
+ # If private key is explicitly specified, don't try default ~/.ssh/id_dsa and ~/.ssh/id_rsa
+ # Otherwise use Paramiko's default: first try ssh-agent and after that id_{d,r}sa
+ allow_agent = True
+ look_for_keys = True
+ if not self.runner.remote_pass is None:
+ allow_agent = False
+ look_for_keys = False
+ elif not self.runner.private_key_file is None:
+ look_for_keys = False
         try:
- ssh.connect(self.host, username=user, allow_agent=True, look_for_keys=True,
+ ssh.connect(self.host, username=user, allow_agent=allow_agent, look_for_keys=look_for_keys,
                 key_filename=self.runner.private_key_file, password=self.runner.remote_pass,
                 timeout=self.runner.timeout, port=self.port)
         except Exception, e:

Does setting look_for_keys=False make it not look for regular SSH
keys? That seems quite undesirable because while you might not be
setting a keyfile, you could have keys of your own. This would more
or less force usage of ssh-agent.

I'm fine with the part that disables the agent if the password is set
-- send me a pull request.

Does setting look_for_keys=False make it not look for regular SSH
keys? That seems quite undesirable because while you might not be
setting a keyfile, you could have keys of your own. This would more
or less force usage of ssh-agent.

This makes Ansible not look for ~/.ssh/id_dsa and ~/.ssh/id_rsa if
private_key_file is set. I believe this is correct behaviour: if user
specified private key, we don't need to try default ones.

ok, send me a pull request and I can try it.