summaryrefslogtreecommitdiff
path: root/lib/ansible/runner/connection_plugins/paramiko_ssh.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ansible/runner/connection_plugins/paramiko_ssh.py')
-rw-r--r--lib/ansible/runner/connection_plugins/paramiko_ssh.py52
1 files changed, 47 insertions, 5 deletions
diff --git a/lib/ansible/runner/connection_plugins/paramiko_ssh.py b/lib/ansible/runner/connection_plugins/paramiko_ssh.py
index a17cde2861..0eafea098b 100644
--- a/lib/ansible/runner/connection_plugins/paramiko_ssh.py
+++ b/lib/ansible/runner/connection_plugins/paramiko_ssh.py
@@ -15,6 +15,14 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# ---
+# The paramiko transport is provided because many distributions, in particular EL6 and before
+# do not support ControlPersist in their SSH implementations. This is needed on the Ansible
+# control machine to be reasonably efficient with connections. Thus paramiko is faster
+# for most users on these platforms. Users with ControlPersist capability can consider
+# using -c ssh or configuring the transport in ansible.cfg.
+
import warnings
import os
import pipes
@@ -24,11 +32,18 @@ import logging
import traceback
import fcntl
import sys
+from termios import tcflush, TCIFLUSH
from binascii import hexlify
from ansible.callbacks import vvv
from ansible import errors
from ansible import utils
from ansible import constants as C
+
+AUTHENTICITY_MSG="""
+paramiko: The authenticity of host '%s' can't be established.
+The %s key fingerprint is %s.
+Are you sure you want to continue connecting (yes/no)?
+"""
# prevent paramiko warning noise -- see http://stackoverflow.com/questions/3920502/
HAVE_PARAMIKO=False
@@ -41,22 +56,49 @@ with warnings.catch_warnings():
except ImportError:
pass
-class MyAutoAddPolicy(object):
+class MyAddPolicy(object):
"""
- Modified version of AutoAddPolicy in paramiko so we can determine when keys are added.
+ Based on AutoAddPolicy in paramiko so we can determine when keys are added
+ and also prompt for input.
Policy for automatically adding the hostname and new host key to the
local L{HostKeys} object, and saving it. This is used by L{SSHClient}.
"""
+ def __init__(self, runner):
+ self.runner = runner
+
def missing_host_key(self, client, hostname, key):
+ if C.HOST_KEY_CHECKING:
+
+ KEY_LOCK = self.runner.lockfile
+ fcntl.lockf(KEY_LOCK, fcntl.LOCK_EX)
+
+ old_stdin = sys.stdin
+ sys.stdin = self.runner._new_stdin
+ fingerprint = hexlify(key.get_fingerprint())
+ ktype = key.get_name()
+
+ # clear out any premature input on sys.stdin
+ tcflush(sys.stdin, TCIFLUSH)
+
+ inp = raw_input(AUTHENTICITY_MSG % (hostname, ktype, fingerprint))
+ sys.stdin = old_stdin
+ if inp not in ['yes','y','']:
+ fcntl.flock(KEY_LOCK, fcntl.LOCK_UN)
+ raise errors.AnsibleError("host connection rejected by user")
+
+ fcntl.flock(KEY_LOCK, fcntl.LOCK_UN)
+
+
key._added_by_ansible_this_time = True
# existing implementation below:
client._host_keys.add(hostname, key.get_name(), key)
- if client._host_keys_filename is not None:
- client.save_host_keys(client._host_keys_filename)
+
+ # host keys are actually saved in close() function below
+ # in order to control ordering.
# keep connection objects on a per host basis to avoid repeated attempts to reconnect
@@ -103,7 +145,7 @@ class Connection(object):
if C.HOST_KEY_CHECKING:
ssh.load_system_host_keys()
- ssh.set_missing_host_key_policy(MyAutoAddPolicy())
+ ssh.set_missing_host_key_policy(MyAddPolicy(self.runner))
allow_agent = True
if self.password is not None: