Passwordless Python LDAP3 authentication from Windows client
我使用的是出色的ldap3软件包,并且尝试与活动目录服务器建立连接,但无需提供纯文本形式的实际凭据。
支持以下SASL机制。
我尝试安装GSSAPI软件包,但在Windows计算机上不起作用。
有人可以为此提供一个简单的例子吗?
我相信GSS-SPNEGO可能是解决方案,但我在互联网上找不到任何可理解的例子。
谢谢您提出这个问题。我今天给了它最后一枪,让它起作用。
请参阅Davide的答案
它要求您具有ldap3软件包并安装winkerberos软件包:
1 | pip install winkerberos |
然后,您需要用站点链接(
您需要在替换的kerberos.py文件中更改以下行:
1 | from treadmill import kerberoswrapper as kerberos |
更改为
1 | import winkerberos as kerberos |
然后您可以像这样连接:
1 2 3 4 5 6 7 8 9 | from ldap3 import Server, Connection, Tls, SASL, GSSAPI import ssl tls = Tls(validate=ssl.CERT_NONE, version=ssl.PROTOCOL_TLSv1) server = Server('server_fqdn', use_ssl=True, tls=tls) c = Connection(server, authentication=SASL, sasl_mechanism=GSSAPI) c.bind() print(c.extend.standard.who_am_i()) c.unbind() |
将server_fqdn替换为AD服务器的标准域名。
您可能希望将版本值更改为AD服务器使用的任何协议。
如果有人没有那么麻烦的方法来完成此操作,请输入提示音!
使用初始答案并避免猴子打补丁,可以根据此处提供的文件和
ldap3kerberos.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | """Replaces the use of python-gssapi with kerberos in ldap3. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals import base64 import socket import ldap3 from ldap3.core.exceptions import LDAPCommunicationError from ldap3.protocol.sasl.sasl import send_sasl_negotiation from ldap3.protocol.sasl.sasl import abort_sasl_negotiation from ldap3.protocol.sasl.external import sasl_external from ldap3.protocol.sasl.digestMd5 import sasl_digest_md5 from ldap3.protocol.sasl.plain import sasl_plain from ldap3.utils.log import log, log_enabled, BASIC from ldap3 import EXTERNAL, DIGEST_MD5, GSSAPI import winkerberos as kerberos NO_SECURITY_LAYER = 1 INTEGRITY_PROTECTION = 2 CONFIDENTIALITY_PROTECTION = 4 class Connection(ldap3.Connection): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def do_sasl_bind(self, controls): if log_enabled(BASIC): log(BASIC, 'start SASL BIND operation via <%s>', self) self.last_error = None with self.connection_lock: result = None if not self.sasl_in_progress: self.sasl_in_progress = True try: if self.sasl_mechanism == EXTERNAL: result = sasl_external(self, controls) elif self.sasl_mechanism == DIGEST_MD5: result = sasl_digest_md5(self, controls) elif self.sasl_mechanism == GSSAPI: result = sasl_gssapi(self, controls) elif self.sasl_mechanism == 'PLAIN': result = sasl_plain(self, controls) finally: self.sasl_in_progress = False if log_enabled(BASIC): log(BASIC, 'done SASL BIND operation, result <%s>', result) return result def sasl_gssapi(connection, controls): """ Performs a bind using the Kerberos v5 ("GSSAPI") SASL mechanism from RFC 4752. Does not support any security layers, only authentication! sasl_credentials can be empty or a tuple with one or two elements. The first element determines which service principal to request a ticket for and can be one of the following: - None or False, to use the hostname from the Server object - True to perform a reverse DNS lookup to retrieve the canonical hostname for the hosts IP address - A string containing the hostname The optional second element is what authorization ID to request. - If omitted or None, the authentication ID is used as the authorization ID - If a string, the authorization ID to use. Should start with"dn:" or "user:". """ # pylint: disable=too-many-branches target_name = None authz_id = b'' if connection.sasl_credentials: if (len(connection.sasl_credentials) >= 1 and connection.sasl_credentials[0]): if connection.sasl_credentials[0] is True: hostname = \\ socket.gethostbyaddr(connection.socket.getpeername()[0])[0] target_name = 'ldap@' + hostname else: target_name = 'ldap@' + connection.sasl_credentials[0] if (len(connection.sasl_credentials) >= 2 and connection.sasl_credentials[1]): authz_id = connection.sasl_credentials[1].encode("utf-8") if target_name is None: target_name = 'ldap@' + connection.server.host gssflags = ( kerberos.GSS_C_MUTUAL_FLAG | kerberos.GSS_C_SEQUENCE_FLAG | kerberos.GSS_C_INTEG_FLAG | kerberos.GSS_C_CONF_FLAG ) _, ctx = kerberos.authGSSClientInit(target_name, gssflags=gssflags) in_token = b'' try: while True: status = kerberos.authGSSClientStep( ctx, base64.b64encode(in_token).decode('ascii') ) out_token = kerberos.authGSSClientResponse(ctx) or '' result = send_sasl_negotiation( connection, controls, base64.b64decode(out_token) ) in_token = result['saslCreds'] or b'' if status == kerberos.AUTH_GSS_COMPLETE: break kerberos.authGSSClientUnwrap( ctx, base64.b64encode(in_token).decode('ascii') ) unwrapped_token = base64.b64decode( kerberos.authGSSClientResponse(ctx) or '' ) if len(unwrapped_token) != 4: raise LDAPCommunicationError('Incorrect response from server') server_security_layers = unwrapped_token[0] if not isinstance(server_security_layers, int): server_security_layers = ord(server_security_layers) if server_security_layers in (0, NO_SECURITY_LAYER): if unwrapped_token.message[1:] != '\\x00\\x00\\x00': raise LDAPCommunicationError( 'Server max buffer size must be 0 if no security layer' ) if not server_security_layers & NO_SECURITY_LAYER: raise LDAPCommunicationError( 'Server requires a security layer, but this is not implemented' ) client_security_layers = bytearray([NO_SECURITY_LAYER, 0, 0, 0]) kerberos.authGSSClientWrap( ctx, base64.b64encode( bytes(client_security_layers) + authz_id ).decode('ascii') ) out_token = kerberos.authGSSClientResponse(ctx) or '' return send_sasl_negotiation( connection, controls, base64.b64decode(out_token) ) except (kerberos.GSSError, LDAPCommunicationError): abort_sasl_negotiation(connection, controls) raise |
安装winkerberos:
在脚本中,使用以下代码(
1 2 3 4 5 6 7 8 | import ldap import ldap3kerberos server = ldap3.Server(fqdn, connect_timeout=10, mode=ldap3.IP_V4_ONLY) conn = ldap3kerberos.Connection( server, authentication=ldap3.SASL, sasl_mechanism=ldap3.GSSAPI, auto_bind=True, receive_timeout=10 ) |
If you have several domain controller servers for an AD domain, ensure you are connecting to some specific server, otherwise you will get the exception:
winkerberos.GSSError: SSPI: InitializeSecurityContext: The specified target is unknown or unreachable