Skip to content

Commit

Permalink
Use a -short- custom hash for controlpersist path by default (#20843)
Browse files Browse the repository at this point in the history
* A method to validate and alter the ssh control path automatically.
* First tries %C to use the shortened hash
* On further failure, it removes section by section from the original path
* Fix hostname
* Implement bcoca's suggested changes
* Remove unused option
* Remove unused class var
* Use to_string to avoid unicode error
* Switch from to_text to to_bytes
* Update the example config for the new controlpath feature
  • Loading branch information
jctanner committed Feb 1, 2017
1 parent 6244271 commit ac78347
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 10 deletions.
12 changes: 5 additions & 7 deletions examples/ansible.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,14 @@
# control_path_dir = /tmp/.ansible/cp
#control_path_dir = ~/.ansible/cp

# The path to use for the ControlPath sockets. This defaults to
# "%(directory)s/ansible-ssh-%%h-%%p-%%r", however on some systems with
# very long hostnames or very long path names (caused by long user names or
# deeply nested home directories) this can exceed the character limit on
# file socket names (108 characters for most platforms). In that case, you
# may wish to shorten the string below.
# The path to use for the ControlPath sockets. This defaults to a hashed string of the hostname,
# port and username (empty string in the config). The hash mitigates a common problem users
# found with long hostames and the conventional %(directory)s/ansible-ssh-%%h-%%p-%%r format.
# In those cases, a "too long for Unix domain socket" ssh error would occur.
#
# Example:
# control_path = %(directory)s/%%h-%%r
#control_path = %(directory)s/ansible-ssh-%%h-%%p-%%r
#control_path =

# Enabling pipelining reduces the number of SSH operations required to
# execute a module on the remote server. This can result in a significant
Expand Down
2 changes: 1 addition & 1 deletion lib/ansible/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ def load_config_file():
# to .format() in the future. be sure to read this:
# http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/ and understand
# that it may be a security risk to do so.
ANSIBLE_SSH_CONTROL_PATH = get_config(p, 'ssh_connection', 'control_path', 'ANSIBLE_SSH_CONTROL_PATH', u"%(directory)s/ansible-ssh-%%h-%%p-%%r")
ANSIBLE_SSH_CONTROL_PATH = get_config(p, 'ssh_connection', 'control_path', 'ANSIBLE_SSH_CONTROL_PATH', None)
ANSIBLE_SSH_CONTROL_PATH_DIR = get_config(p, 'ssh_connection', 'control_path_dir', 'ANSIBLE_SSH_CONTROL_PATH_DIR', u'~/.ansible/cp')
ANSIBLE_SSH_PIPELINING = get_config(p, 'ssh_connection', 'pipelining', 'ANSIBLE_SSH_PIPELINING', False, value_type='boolean')
ANSIBLE_SSH_RETRIES = get_config(p, 'ssh_connection', 'retries', 'ANSIBLE_SSH_RETRIES', 0, value_type='integer')
Expand Down
25 changes: 23 additions & 2 deletions lib/ansible/plugins/connection/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import errno
import fcntl
import hashlib
import os
import pty
import select
Expand Down Expand Up @@ -59,6 +60,10 @@ def __init__(self, *args, **kwargs):
super(Connection, self).__init__(*args, **kwargs)

self.host = self._play_context.remote_addr
self.port = self._play_context.port
self.user = self._play_context.remote_user
self.control_path = C.ANSIBLE_SSH_CONTROL_PATH
self.control_path_dir = C.ANSIBLE_SSH_CONTROL_PATH_DIR

# The connection is created by running ssh/scp/sftp from the exec_command,
# put_file, and fetch_file methods, so we don't need to do any connection
Expand All @@ -67,6 +72,16 @@ def __init__(self, *args, **kwargs):
def _connect(self):
return self

@staticmethod
def _create_control_path(host, port, user):
'''Make a hash for the controlpath based on con attributes'''
pstring = '%s-%s-%s' % (host, port, user)
m = hashlib.sha1()
m.update(to_bytes(pstring))
digest = m.hexdigest()
cpath = '%(directory)s/' + digest[:10]
return cpath

@staticmethod
def _sshpass_available():
global SSHPASS_AVAILABLE
Expand Down Expand Up @@ -228,15 +243,21 @@ def _build_command(self, binary, *other_args):
self._persistent = True

if not controlpath:
cpdir = unfrackpath(C.ANSIBLE_SSH_CONTROL_PATH_DIR)
cpdir = unfrackpath(self.control_path_dir)
b_cpdir = to_bytes(cpdir, errors='surrogate_or_strict')

# The directory must exist and be writable.
makedirs_safe(b_cpdir, 0o700)
if not os.access(b_cpdir, os.W_OK):
raise AnsibleError("Cannot write to ControlPath %s" % to_native(cpdir))

b_args = (b"-o", b"ControlPath=" + to_bytes(C.ANSIBLE_SSH_CONTROL_PATH % dict(directory=cpdir), errors='surrogate_or_strict'))
if not self.control_path:
self.control_path = self._create_control_path(
self.host,
self.port,
self.user
)
b_args = (b"-o", b"ControlPath=" + to_bytes(self.control_path % dict(directory=cpdir), errors='surrogate_or_strict'))
self._add_args(b_command, b_args, u"found only ControlPersist; added ControlPath")

# Finally, we add any caller-supplied extras.
Expand Down

0 comments on commit ac78347

Please sign in to comment.