@ -75,6 +75,10 @@ DOCUMENTATION = """
- kerberos usage mode .
- The managed option means Ansible will obtain kerberos ticket .
- 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 ]
vars :
- name : ansible_winrm_kinit_mode
@ -110,7 +114,6 @@ except ImportError:
from ansible . errors import AnsibleError , AnsibleConnectionFailure
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 . _text import to_bytes , to_native , to_text
from ansible . module_utils . six import binary_type
@ -135,6 +138,12 @@ except ImportError as e:
HAS_XMLTODICT = False
XMLTODICT_IMPORT_ERR = e
try :
import pexpect
HAS_PEXPECT = True
except ImportError as e :
HAS_PEXPECT = False
try :
from __main__ import display
except ImportError :
@ -239,20 +248,45 @@ class Connection(ConnectionBase):
def _kerb_auth ( self , principal , password ) :
if password is None :
password = " "
self . _kerb_ccache = tempfile . NamedTemporaryFile ( )
display . vvvvv ( " creating Kerberos CC at %s " % self . _kerb_ccache . name )
krb5ccname = " FILE: %s " % self . _kerb_ccache . name
krbenv = dict ( KRB5CCNAME = krb5ccname )
os . environ [ " KRB5CCNAME " ] = krb5ccname
kinit_cmdline = [ self . _kinit_cmd , principal ]
display . vvvvv ( " calling kinit for principal %s " % principal )
p = subprocess . Popen ( kinit_cmdline , stdin = subprocess . PIPE , stdout = subprocess . PIPE , stderr = subprocess . PIPE , env = krbenv )
# TODO: unicode/py3
stdout , stderr = p . communicate ( password + b ' \n ' )
if p . returncode != 0 :
krb5env = dict ( KRB5CCNAME = krb5ccname )
# pexpect runs the process in its own pty so it can correctly send
# 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 ' )
display . vvvv ( " calling kinit with pexpect for principal %s "
% 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 ' )
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 ( ) )
display . vvvvv ( " kinit succeeded for principal %s " % principal )