winrm: attempting to get kerb auth to work on MacOS (#33795)
* winrm: attempting to get kerb auth to work on MacOS * moved to use pexpect if possible as it is simpler * Made the pexpect event more lenient around different localisations
This commit is contained in:
parent
b25e333076
commit
92e52ef515
1 changed files with 42 additions and 8 deletions
|
@ -75,6 +75,10 @@ DOCUMENTATION = """
|
||||||
- kerberos usage mode.
|
- kerberos usage mode.
|
||||||
- The managed option means Ansible will obtain kerberos ticket.
|
- The managed option means Ansible will obtain kerberos ticket.
|
||||||
- While the manual one means a ticket must already have been obtained by the user.
|
- While the manual one means a ticket must already have been obtained by the user.
|
||||||
|
- If having issues with Ansible freezing when trying to obtain the
|
||||||
|
Kerberos ticket, you can either set this to C(manual) and obtain
|
||||||
|
it outside Ansible or install C(pexpect) through pip and try
|
||||||
|
again.
|
||||||
choices: [managed, manual]
|
choices: [managed, manual]
|
||||||
vars:
|
vars:
|
||||||
- name: ansible_winrm_kinit_mode
|
- name: ansible_winrm_kinit_mode
|
||||||
|
@ -110,7 +114,6 @@ except ImportError:
|
||||||
|
|
||||||
from ansible.errors import AnsibleError, AnsibleConnectionFailure
|
from ansible.errors import AnsibleError, AnsibleConnectionFailure
|
||||||
from ansible.errors import AnsibleFileNotFound
|
from ansible.errors import AnsibleFileNotFound
|
||||||
from ansible.module_utils.six import string_types
|
|
||||||
from ansible.module_utils.six.moves.urllib.parse import urlunsplit
|
from ansible.module_utils.six.moves.urllib.parse import urlunsplit
|
||||||
from ansible.module_utils._text import to_bytes, to_native, to_text
|
from ansible.module_utils._text import to_bytes, to_native, to_text
|
||||||
from ansible.module_utils.six import binary_type
|
from ansible.module_utils.six import binary_type
|
||||||
|
@ -135,6 +138,12 @@ except ImportError as e:
|
||||||
HAS_XMLTODICT = False
|
HAS_XMLTODICT = False
|
||||||
XMLTODICT_IMPORT_ERR = e
|
XMLTODICT_IMPORT_ERR = e
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pexpect
|
||||||
|
HAS_PEXPECT = True
|
||||||
|
except ImportError as e:
|
||||||
|
HAS_PEXPECT = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from __main__ import display
|
from __main__ import display
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -239,20 +248,45 @@ class Connection(ConnectionBase):
|
||||||
def _kerb_auth(self, principal, password):
|
def _kerb_auth(self, principal, password):
|
||||||
if password is None:
|
if password is None:
|
||||||
password = ""
|
password = ""
|
||||||
|
|
||||||
self._kerb_ccache = tempfile.NamedTemporaryFile()
|
self._kerb_ccache = tempfile.NamedTemporaryFile()
|
||||||
display.vvvvv("creating Kerberos CC at %s" % self._kerb_ccache.name)
|
display.vvvvv("creating Kerberos CC at %s" % self._kerb_ccache.name)
|
||||||
krb5ccname = "FILE:%s" % self._kerb_ccache.name
|
krb5ccname = "FILE:%s" % self._kerb_ccache.name
|
||||||
krbenv = dict(KRB5CCNAME=krb5ccname)
|
|
||||||
os.environ["KRB5CCNAME"] = krb5ccname
|
os.environ["KRB5CCNAME"] = krb5ccname
|
||||||
kinit_cmdline = [self._kinit_cmd, principal]
|
krb5env = dict(KRB5CCNAME=krb5ccname)
|
||||||
|
|
||||||
display.vvvvv("calling kinit for principal %s" % principal)
|
# pexpect runs the process in its own pty so it can correctly send
|
||||||
p = subprocess.Popen(kinit_cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=krbenv)
|
# the password as input even on MacOS which blocks subprocess from
|
||||||
|
# doing so. Unfortunately it is not available on the built in Python
|
||||||
|
# so we can only use it if someone has installed it
|
||||||
|
if HAS_PEXPECT:
|
||||||
|
kinit_cmdline = "%s %s" % (self._kinit_cmd, principal)
|
||||||
|
password = to_text(password, encoding='utf-8',
|
||||||
|
errors='surrogate_or_strict')
|
||||||
|
|
||||||
# TODO: unicode/py3
|
display.vvvv("calling kinit with pexpect for principal %s"
|
||||||
stdout, stderr = p.communicate(password + b'\n')
|
% principal)
|
||||||
|
events = {
|
||||||
|
".*:": password + "\n"
|
||||||
|
}
|
||||||
|
# technically this is the stdout but to match subprocess we wil call
|
||||||
|
# it stderr
|
||||||
|
stderr, rc = pexpect.run(kinit_cmdline, withexitstatus=True, events=events, env=krb5env, timeout=60)
|
||||||
|
else:
|
||||||
|
kinit_cmdline = [self._kinit_cmd, principal]
|
||||||
|
password = to_bytes(password, encoding='utf-8',
|
||||||
|
errors='surrogate_or_strict')
|
||||||
|
|
||||||
if p.returncode != 0:
|
display.vvvv("calling kinit with subprocess for principal %s"
|
||||||
|
% principal)
|
||||||
|
p = subprocess.Popen(kinit_cmdline, stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
env=krb5env)
|
||||||
|
stdout, stderr = p.communicate(password + b'\n')
|
||||||
|
rc = p.returncode != 0
|
||||||
|
|
||||||
|
if rc != 0:
|
||||||
raise AnsibleConnectionFailure("Kerberos auth failure: %s" % stderr.strip())
|
raise AnsibleConnectionFailure("Kerberos auth failure: %s" % stderr.strip())
|
||||||
|
|
||||||
display.vvvvv("kinit succeeded for principal %s" % principal)
|
display.vvvvv("kinit succeeded for principal %s" % principal)
|
||||||
|
|
Loading…
Reference in a new issue