def main(): """sync AD and WX Teams team membership""" with open(CONFIG_FILE, 'r') as config_file: config_params = yaml.full_load(config_file) wx_config = config_params['wxteams'] wx_token = wx_config['auth_token'] ldap_config = config_params['ldap'] ldap_host = ldap_config['server'] ldap_user = ldap_config['user'] ldap_password = ldap_config['password'] ldap_basedn = ldap_config['basedn'] ldap_basedn_groups = ldap_config['basedn_groups'] ldap_basedn_len = len(ldap_basedn) wx_teamquery = input('Please enter name of the team to examine: ') print('\nBuilding Webex Teams team list, please wait...', end='') api = WebexTeamsAPI(access_token=wx_token) wx_team_fulllist = list(api.teams.list(type='group')) print_done() # Populate list of query matches from full list, case insensitive wx_team_matchlist = [{'name': wx_team.name, 'id': wx_team.id} for wx_team in wx_team_fulllist if wx_teamquery.upper() in wx_team.name.upper()] if not wx_team_matchlist: bad_choice() # Finalize team list elif len(wx_team_matchlist) > 1: # multiple matches found, present user with choice for count, wx_team in enumerate(wx_team_matchlist, 1): print(f'{count}: {wx_team["name"]}') try: team_number = int(input('\nPlease enter number of team to compare: ')) except ValueError: bad_choice() if team_number >= 0 and team_number < len(wx_team_matchlist)+1: team_number -= 1 else: bad_choice() else: # one match found, use it team_number = 0 # team selected if team_number >= 0: wx_team_match = {'name': wx_team_matchlist[team_number]['name'], 'id': wx_team_matchlist[team_number]['id']} else: bad_choice() # Catalogue team members if confirmed(f'Webex Teams \"{wx_team_match["name"]}\" team selected, are you sure?'): print(f'Gathering details on \"{wx_team_match["name"]}\" team...', end='') wx_team_members = list(api.team_memberships.list(teamId=wx_team_match['id'])) print_done() else: bad_choice() ad_dl_query = input_with_default('Please enter name of AD DL to compare against: ', wx_teamquery) # Open LDAP connection to Active Directory print(f'Connecting to AD...', end='') server = ldap3.Server(ldap_host, use_ssl=True) connection = ldap3.Connection(server, user=ldap_user, password=ldap_password, authentication=ldap3.NTLM, auto_bind=True) print_done() # set up OU search query_parameters = {'search_base': ldap_basedn_groups, 'search_filter': f'(&({LDAPFILTER_GROUP})(displayName=*{ad_dl_query}*))', 'paged_size': LDAP_PAGE_SIZE, 'attributes': LDAP_GROUP_ATTRIBUTES} print(f'Querying AD groups...', end='') connection.search(**query_parameters) print_done() ad_dl_matchlist = [{'dn': entry.entry_dn, **entry.entry_attributes_as_dict} for entry in connection.entries] if not ad_dl_matchlist: bad_choice() elif len(ad_dl_matchlist) > 1: # multiple matches found, present user with choice for count, ad_dl in enumerate(ad_dl_matchlist, 1): print(f'{count}: {ad_dl["displayName"][0]} ({ad_dl["dn"]})') try: dl_number = int(input('\nPlease enter number of DL to compare: ')) except ValueError: bad_choice() if dl_number >= 0 and dl_number < len(ad_dl_matchlist)+1: dl_number -= 1 else: bad_choice() else: # one match found, use it dl_number = 0 # DL selected if dl_number >= 0: ad_dl_match = {'dn': ad_dl_matchlist[dl_number]['dn'], 'displayName': ad_dl_matchlist[dl_number]['displayName'][0]} else: bad_choice() if confirmed(f'AD group \"{ad_dl_match["displayName"]}\" selected, are you sure?'): ad_dl_userlist = list() query_parameters = {'search_base': ad_dl_match['dn'], 'search_filter': f'({LDAPFILTER_GROUP})', 'paged_size': LDAP_PAGE_SIZE, 'attributes': ['member']} print(f'Querying AD group membership...', end='') connection.search(**query_parameters) print_done() for member in connection.entries[0].member.values: print_status(f'Gathering details on {member[:(len(member)-ldap_basedn_len-1)]}') query_parameters = {'search_base': member, 'search_filter': LDAPFILTER_USER, 'paged_size': LDAP_PAGE_SIZE, 'attributes': LDAP_USER_ATTRIBUTES} connection.search(**query_parameters) if connection.entries: attributes = connection.entries[0] created = attributes.whenCreated.values[0] ad_dl_userlist.append({'name': attributes.displayName.values[0], 'email': attributes.mail.values[0].lower(), 'created': f'{created.year}-{created.month}-{created.day}'}) print_status(' done.') else: bad_choice() # Buid list of AD user to add to team wx_team_additions = list() for ad_user in ad_dl_userlist: if not any(wx_user.personEmail.lower() == ad_user["email"] for wx_user in wx_team_members): if confirmed(f'AD user \"{ad_user["name"]}\" ({ad_user["created"]}) ' + f'not in \"{wx_team_match["name"]}\" team, add?'): wx_team_additions.append(ad_user["email"]) # Add users selected to team if wx_team_additions: for addition in wx_team_additions: api.team_memberships.create(wx_team_match['id'], personEmail=addition) else: print('### No users selected to add to team!') print('\n') # Notify if space includes users not in AD for wx_user in wx_team_members: if not any(ad_user['email'] == wx_user.personEmail.lower() for ad_user in ad_dl_userlist): print(f'\"{wx_user["name"]}\" not in {ad_dl_match["displayName"]} AD group!') print('\nComplete.')
# Check host alive def check_host_alive(hostname): try: urllib2.urlopen(hostname, timeout=1) return 1 # OK except urllib2.URLError as err: return 0 # Error print "Starting ..." print "-" * 45 # AD connect ad_connect = ldap3.Connection(server=ad_host, user=ad_user, password=ad_pass, version=3, authentication='SIMPLE') if not ad_connect.bind(): print("ERROR: not connect to AD server") sys.exit() else: print "=> Connect to " + ad_host + " is success." # Get data from AD, generate contacts list and URL list total_entries = 0 ad_connect.search(search_base=ad_basedn, search_filter='(objectclass=person)', attributes=ad_attr) total_entries += len(ad_connect.response) if total_entries > 0:
# TLS configuration fort the LDAP connection #tls_configuration = ldap3.Tls( # validate=ssl.CERT_NONE, # version=ssl.PROTOCOL_TLSv1 # ) # The LDAP server ldap_server = ldap3.Server(config.get("ldap", "url"), #tls=tls_configuration, #port=389 ) # Connect to the LDAP server ldap_connection = ldap3.Connection(ldap_server, config.get("ldap", "user_dn"), config.get("ldap", "user_pass"), auto_bind=False) ldap_connection.open() ldap_connection.start_tls() ldap_connection.bind() # Get the group from LDAP ldap_connection.search(config.get("ldap", "group_dn"), "(objectClass=*)", search_scope=ldap3.BASE, attributes=['*']) # Must have a single group assert len(ldap_connection.entries) == 1, "No of matching groups must be 1"
def synchronize(): from src.application.security.models import LDAPUser import logging logging.basicConfig(filename='/service_django/error.log', level=logging.DEBUG) from ldap3.utils.log import set_library_log_detail_level, set_library_log_hide_sensitive_data, EXTENDED set_library_log_detail_level(EXTENDED) set_library_log_hide_sensitive_data(False) server = ldap3.Server(host=settings.LDAP_SERVER_HOST, port=settings.LDAP_SERVER_PORT, use_ssl=False, get_info=ldap3.ALL) # define the connection connection = ldap3.Connection(server=server, user=settings.LDAP_SERVER_USER, password=settings.LDAP_SERVER_PASSWORD, auto_bind='NONE', version=3, authentication='SIMPLE', client_strategy='SYNC', auto_referrals=True, check_names=True, read_only=False, lazy=False, raise_exceptions=False) instance = LDAPUser.objects.get(pk=11) if connection.bind(): attributes = { 'uid': instance.identifier, 'uidNumber': '%s' % (100000 + instance.pk), 'gidNumber': settings.LDAP_SERVER_GROUPS_GROUP_GIDNUMBER, 'cn': instance.__str__(), 'givenName': (instance.first_name if instance.first_name != '' else '-'), 'sn': (instance.last_name if instance.last_name != '' else '-'), 'mail': instance.email, 'userPassword': instance.password, 'description': instance.detail, 'homeDirectory': '%s%s/%s' % ( settings.LDAP_SERVER_USERS_HOMEDIRECTORY, settings.LDAP_SERVER_GROUPS_GROUP_CN, instance.identifier, ), 'loginShell': '/bin/bash', # 'institute': instance.institute, 'researchField': instance.researchField, 'researchGroup': instance.researchGroup, 'serviceType': 'serviceType', 'userProfile': instance.userProfile, } if instance.tutorInstitution: attributes.update({'tutorInstitution': instance.tutorInstitution}) if instance.tutorMail: attributes.update({'tutorMail': instance.tutorMail}) if instance.tutorName: attributes.update({'tutorName': instance.tutorName}) boolean___is_add = connection.add(dn='uid=%s,ou=%s,%s' % ( instance.identifier, settings.LDAP_SERVER_GROUPS_GROUP_CN, settings.LDAP_SERVER_USERS_SEARCH_BASE, ), object_class=[ 'inetOrgPerson', 'posixAccount', 'top', 'hpcCubaUser' ], attributes=attributes) print(attributes) print(boolean___is_add) else: print("not bind") connection.unbind()
"deakinArea": [], "faculty": [], "institute": [] } server = ldap3.Server(ldap_uri) for i in range(len(res_support)): name = res_support.iloc[i, 3] # extract user's name if pd.notnull(name): search_filter = "(cn=" + name + ")" # construct search query else: continue try: with ldap3.Connection( server, auto_bind=True) as conn: # estab connection and perform query conn.search(search_base, search_filter, attributes=attrs) pprint(conn.entries) query = conn.entries[0] email = query.mail # save the email from the user deakinArea = query.deakinArea # save deakinArea/school faculty = query.deakinFacultyDivision # save facilty institute = query.o contact_dict["name"].append(name) # append to contacts contact_dict["email"].append(email) contact_dict["deakinArea"].append(deakinArea) contact_dict["faculty"].append(faculty) contact_dict["institute"].append(institute) except:
def _create_ldap_connection(self, queried_domain=str(), ads_path=str(), ads_prefix=str()): if not self._domain: self._domain = self._get_netfqdn() if not queried_domain: queried_domain = self._get_netfqdn() self._queried_domain = queried_domain base_dn = str() if ads_prefix: self._ads_prefix = ads_prefix base_dn = '{},'.format(self._ads_prefix) if ads_path: # TODO: manage ADS path starting with 'GC://' if ads_path.upper().startswith('LDAP://'): ads_path = ads_path[7:] self._ads_path = ads_path base_dn += self._ads_path else: base_dn += ','.join('dc={}'.format(x) for x in self._queried_domain.split('.')) # base_dn is no longer used within `_create_ldap_connection()`, but I don't want to break # the function call. So we store it in an attriute and use it in `_ldap_search()` self._base_dn = base_dn # Format the username and the domain # ldap3 seems not compatible with USER@DOMAIN format user = '******'.format(self._domain, self._user) # Choose between password or pth if self._lmhash and self._nthash: lm_nt_hash = '{}:{}'.format(self._lmhash, self._nthash) ldap_server = ldap3.Server('ldap://{}'.format( self._domain_controller)) ldap_connection = ldap3.Connection(ldap_server, user, lm_nt_hash, authentication=ldap3.NTLM, raise_exceptions=True) try: ldap_connection.bind() except ldap3.core.exceptions.LDAPStrongerAuthRequiredResult: # We need to try SSL (pth version) ldap_server = ldap3.Server('ldaps://{}'.format( self._domain_controller)) ldap_connection = ldap3.Connection(ldap_server, user, lm_nt_hash, authentication=ldap3.NTLM, raise_exceptions=True) ldap_connection.bind() else: ldap_server = ldap3.Server('ldap://{}'.format( self._domain_controller)) ldap_connection = ldap3.Connection(ldap_server, user, self._password, authentication=ldap3.NTLM, raise_exceptions=True) try: ldap_connection.bind() except ldap3.core.exceptions.LDAPStrongerAuthRequiredResult: # We nedd to try SSL (password version) ldap_server = ldap3.Server('ldaps://{}'.format( self._domain_controller)) ldap_connection = ldap3.Connection(ldap_server, user, self._password, authentication=ldap3.NTLM, raise_exceptions=True) ldap_connection.bind() self._ldap_connection = ldap_connection
def create_connection(authtype=None, server=None, user=None, password=None, auto_bind=False, client_strategy=ldap3.SYNC, check_names=True, auto_referrals=False, receive_timeout=5): """ Create a connection to the LDAP server. :param authtype: :param server: :param user: :param password: :param auto_bind: :param client_strategy: :param check_names: :param auto_referrals: :param receive_timeout: At the moment we do not use this, since receive_timeout is not supported by ldap3 < 2. :return: """ authentication = None if not user: authentication = ldap3.ANONYMOUS if authtype == AUTHTYPE.SIMPLE: if not authentication: authentication = ldap3.SIMPLE l = ldap3.Connection(server, user=user, password=to_utf8(password), auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.NTLM: # pragma: no cover if not authentication: authentication = ldap3.NTLM l = ldap3.Connection(server, user=user, password=to_utf8(password), auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.SASL_DIGEST_MD5: # pragma: no cover if not authentication: authentication = ldap3.SASL sasl_credentials = (str(user), str(password)) l = ldap3.Connection(server, sasl_mechanism="DIGEST-MD5", sasl_credentials=sasl_credentials, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) else: raise Exception("Authtype {0!s} not supported".format(authtype)) return l
def test_multiple_instances(self): servers = {} for sid in (1, 2): domain = 'example{0}'.format(sid) servers[sid] = LdapServer({ 'port': 10389 + (sid * 1000), 'bind_dn': 'cn=admin,dc={0},dc=com'.format(domain), 'base': { 'objectclass': ['domain'], 'dn': 'dc={0},dc=com'.format(domain), 'attributes': { 'dc': domain } }, }) servers[sid].start() search_filter = '(objectclass=domain)' attrs = ['dc'] # server1 dn = servers[1].config['bind_dn'] pw = servers[1].config['password'] base_dn = servers[1].config['base']['dn'] port = servers[1].config['port'] srv = ldap3.Server('localhost', port=port) conn = ldap3.Connection(srv, user=dn, password=pw, auto_bind=True) conn.search(base_dn, search_filter, attributes=attrs) self.assertEqual(conn.response, [{ 'dn': 'dc=example1,dc=com', 'raw_attributes': { 'dc': [b'example1'] }, 'attributes': { 'dc': ['example1'] }, 'type': 'searchResEntry' }]) conn.unbind() # server2 dn = servers[2].config['bind_dn'] pw = servers[2].config['password'] base_dn = servers[2].config['base']['dn'] port = servers[2].config['port'] srv = ldap3.Server('localhost', port=port) conn = ldap3.Connection(srv, user=dn, password=pw, auto_bind=True) conn.search(base_dn, search_filter, attributes=attrs) self.assertEqual(conn.response, [{ 'dn': 'dc=example2,dc=com', 'raw_attributes': { 'dc': [b'example2'] }, 'attributes': { 'dc': ['example2'] }, 'type': 'searchResEntry' }]) conn.unbind() for server in servers.values(): server.stop()
# so take off successive elements from end of split path list p = "/".join(split_path[:-i]) if p in self.path_list: for endpoint in self.auth_dict["endpoints"]: if strip_end(strip_end(prefix, "/") + endpoint["endpoint_path"], "/") == p: return {"path": p, "auth_info": endpoint} i += 1 # Initialize a global instance of the authlist class, to be used inside the isallowed() function myauthjson = _AuthJSON() # initialise LDAP server outside of isallowed so to reduce set-up/tear-down costs s = ldap3.Server(myauthjson.auth_dict["server"]) c = ldap3.Connection(s, client_strategy=ldap3.RESTARTABLE) c.open() c.start_tls() # ldap details remain in cache for 30 mins cache = TTLCache(maxsize=256, ttl=1800) # given a authorisation condition and the user info, does the user satisfy the condition? # return true or false based on condition def process_condition(condition, user_info): # empty list = don't check any attributes, so auto match if len(condition) == 0: return True if "attribute" in condition: # only one attribute to check
def consume_auth_request(): # Kill any existing session session.clear() saml_request = request.args.get('SAMLRequest', None) relay_state = request.args.get('RelayState', None) signature = request.args.get('Signature', None) signature_alg = request.args.get('SigAlg', None) if not all([saml_request, relay_state, signature, signature_alg]): msg = ("Received malformed HTTP-Redirect request, missing at least " "one of these query parameters: SAMLRequest, RelayState, " "Signature, SigAlg.") logger.error(msg) abort(400) logger.debug("SAMLRequest is {}".format(saml_request)) logger.debug("RelayState is {}".format(relay_state)) logger.debug("Signature is {}".format(signature)) logger.debug("SigAlg is {}".format(signature_alg)) idp_config = current_app.config['IDP_CONFIG'] idp_config_object = IdPConfig().load(copy.deepcopy(idp_config), metadata_construction=False) idp = Server(config=idp_config_object) # TODO # Need to check signature here or does the parse below do that? # What about replay? # What about time skew? try: parsed_request = idp.parse_authn_request(saml_request, BINDING_HTTP_REDIRECT) except Exception as e: msg = "Caught exception while parsing SAMLRequest: {}".format(e) logger.error(msg) abort(400) if not isinstance(parsed_request, Request): msg = "Received invalid SAMLRequest" logger.error(msg) abort(400) authn_request = parsed_request.message msg = "Parsed SAML request is \n{}" msg = msg.format( minidom.parseString(str(authn_request)).toprettyxml(indent=' ')) logger.debug(msg) subject = authn_request.subject name_id = subject.name_id.text if subject else None if not name_id: msg = "Unable to obtain NameID from SAMLRequest" logger.error(msg) abort(400) logger.info("NameID is {}".format(name_id)) # Use the name ID to query LDAP ldap_config = current_app.config['IDP_CONFIG']['ldap'] url = ldap_config['server_url'] bind_dn = ldap_config['bind_dn'] bind_password = ldap_config['bind_password'] search_base = ldap_config['search_base'] search_filter_template = ldap_config['search_filter_template'] ldap_server = ldap3.Server(url) args = { 'server': ldap_server, 'user': bind_dn, 'password': bind_password, 'auto_bind': True } ldap_connection = ldap3.Connection(**args) args = { 'search_base': search_base, 'search_filter': search_filter_template.format(name_id), 'search_scope': ldap3.SUBTREE, 'attributes': ldap3.ALL_ATTRIBUTES } ldap_connection.search(**args) ldap_response = ldap_connection.response # TODO Should send back a SAML error. if len(ldap_response) == 0: msg = "LDAP query with search filter {} returned zero results" msg = msg.format(args['search_filter']) logger.error(msg) abort(400) # TODO Should send back a SAML error. if len(ldap_response) > 1: msg = "LDAP query with search filter {} returned multiple results" msg = msg.format(args['search_filter']) logger.error(msg) abort(400) logger.debug(ldap_response[0]) privacyidea_config = current_app.config['IDP_CONFIG']['privacyidea'] privacyidea_url = privacyidea_config['server_url'] privacyidea_admin_token = privacyidea_config['admin_token'] headers = { 'Authorization': privacyidea_admin_token, 'Accept': 'application/json' } url = privacyidea_url + '/token/?user={}'.format(name_id) # TODO Catch exceptions resp = requests.get(url, headers=headers) token_response = resp.json() logger.debug(token_response) if token_response['jsonrpc'] != '2.0': msg = "Invalid JSON RPC version number" logger.error(msg) abort(400) # User cannot be found # TODO Should send back a SAML error. result = token_response['result'] if 'error' in result and result['error']['code'] == 904: err_msg = result['error']['message'] msg = "Token query for user {} generated error: {}" msg = msg.format(name_id, err_msg) logger.error(msg) abort(400) # TODO Should send back a SAML error. if not token_response['result']['status']: msg = "Token query for user {} response status not True" msg = msg.format(name_id) logger.error(msg) abort(400) # TODO Need to do better here, handling case where the user is found but # for example has no tokens enabled. actionable_mfa_tokens = [] for token in result['value']['tokens']: if token['tokentype'] == 'totp': actionable_mfa_tokens.append(token) if not actionable_mfa_tokens: # TODO Should send back SAML error. msg = "Could not find actionable MFA token for user {}".format(name_id) logger.error(msg) abort(400) # User is in LDAP and we have found at least one token we can work with # so start a session. session['name_id'] = name_id session['saml_authn_request_id'] = authn_request.id session['saml_sp_entity_id'] = authn_request.issuer.text session['saml_sp_acs'] = authn_request.assertion_consumer_service_url session['saml_relay_state'] = relay_state return redirect(url_for('token.token_index'))
def _ldap_bind(self, action_result=None): """ returns phantom.APP_SUCCESS if connection succeeded, else phantom.APP_ERROR. If an action_result is passed in, method will appropriately use it. Otherwise just return APP_SUCCESS/APP_ERROR """ if self._ldap_connection and \ self._ldap_connection.bound and \ not self._ldap_connection.closed: return True elif self._ldap_connection is not None: self._ldap_connection.unbind() try: if self._validate_ssl_cert: tls = Tls(validate=ssl.CERT_REQUIRED) else: tls = Tls(validate=ssl.CERT_NONE) server_param = { "use_ssl": self._ssl, "port": self._ssl_port, "host": self._server, "get_info": ldap3.ALL, "tls": tls } self._ldap_server = ldap3.Server(**server_param) self.save_progress("configured server {}...".format(self._server)) self._ldap_connection = ldap3.Connection(self._ldap_server, user=self._username, password=self._password, raise_exceptions=True) self.save_progress("binding to directory...") if not self._ldap_connection.bind(): if action_result: return action_result.set_status( phantom.APP_ERROR, self._ldap_connection.result['description'] ) else: return phantom.APP_ERROR if action_result: return action_result.set_status(phantom.APP_SUCCESS) else: return phantom.APP_SUCCESS except Exception as e: self.debug_print("[DEBUG] ldap_bind, e = {}".format(e)) if action_result: return action_result.set_status( phantom.APP_ERROR, status_message=e, exception=e ) else: return phantom.APP_ERROR
def __init__(self): """Initialise the plugin and underlying LDAP connection.""" self._server = ldap3.Server("ldap://xldap.cern.ch") self._connection = ldap3.Connection(self._server, client_strategy=ldap3.SAFE_SYNC, auto_bind=True)
def authenticate(self, user=None, password=None, **kwargs): # Validate username if not self.check_user(user): self.logger.error("Invalid username: %s", user) raise self.LoginError("Invalid username") # Get domain domain, user = self.split_user_domain(user) if domain: ldap_domain = AuthLDAPDomain.get_by_name(domain) if not ldap_domain: self.logger.error("LDAP Auth domain '%s' is not configured", domain) raise self.LoginError("Invalid LDAP domain '%s'" % domain) else: ldap_domain = AuthLDAPDomain.get_default_domain() if not ldap_domain: self.logger.error("Default LDAP Auth domain is not configured") raise self.LoginError("Default LDAP domain is not configured") if not ldap_domain.is_active: self.logger.error("LDAP Auth domain '%s' is disabled", domain) raise self.LoginError("LDAP Auth domain '%s' is disabled" % domain) # Get servers server_pool = self.get_server_pool(ldap_domain) if not server_pool: self.logger.error("No active servers configured for domain '%s'", domain) raise self.LoginError( "No active servers configured for domain '%s'" % domain) # Connect and bind connect_kwargs = self.get_connection_kwargs(ldap_domain, user, password) dkw = connect_kwargs.copy() if "password" in dkw: dkw["password"] = "******" self.logger.debug("Connect to ldap: %s", ", ".join("%s='%s'" % (kk, dkw[kk]) for kk in dkw)) connect = ldap3.Connection(server_pool, **connect_kwargs) try: self.logger.debug("Bind to ldap") if not connect.bind(): raise self.LoginError("Failed to bind to LDAP: %s" % connect.result) except (LDAPCommunicationError, LDAPServerPoolExhaustedError) as e: self.logger.error("Failed to bind to LDAP: connect failed by %s" % e.message) raise self.LoginError( "Failed to bind to LDAP: connect failed by %s" % e.message) # Rebind as privileged user if ldap_domain.bind_user: # Rebind as privileged user connect = ldap3.Connection( server_pool, **self.get_connection_kwargs(ldap_domain, ldap_domain.bind_user, ldap_domain.bind_password)) if not connect.bind(): self.logger.error("Cannot bind as %s to search groups", ldap_domain.bind_user) connect = None # Get user information user_info = self.get_user_info(connect, ldap_domain, user) user_info["user"] = user user_info["domain"] = domain user_info["is_active"] = True # Get user groups user_groups = set( g.lower() for g in self.get_user_groups(connect, ldap_domain, user_info)) if ldap_domain.require_any_group and not user_groups: self.logger.error( "User %s in not a member of any mapped groups. Deny access", user) raise self.LoginError("No groups") if ldap_domain.require_group and ldap_domain.require_group.lower( ) not in user_groups: self.logger.error( "User %s is not a member of required group %s but member of %s", user, ldap_domain.require_group, user_groups, ) raise self.LoginError("Login is not permitted") if ldap_domain.deny_group and ldap_domain.deny_group.lower( ) in user_groups: self.logger.error("User %s is a member of deny group %s", user, ldap_domain.deny_group) user_info["is_active"] = False # Synchronize user user = ldap_domain.clean_username(user) u = self.ensure_user(user, **user_info) # Get group mappings group_mappings = ldap_domain.get_group_mappings() # Apply groups ug = [] for group in group_mappings: if group_mappings[group] & user_groups: self.logger.debug("%s: Ensure group %s", u.username, group.name) self.ensure_group(u, group) ug += [group.name] else: self.logger.debug("%s: Deny group %s", u.username, group.name) self.deny_group(u, group) # Final check if not user_info["is_active"]: raise self.LoginError("Access denied") self.logger.info("Authenticated as %s. Groups: %s", u.username, ", ".join(ug)) return u.username
#!/usr/bin/python import ldap3 CUSER_DN = 'cn=Directory Manager' PEOPLE_DN = 'ou=people,dc=prorail,dc=nl' cuser_password = '******' local_ldap = 'ldap1.home.org' install = 'allard' password = '******' s = ldap3.Server(local_ldap) c = ldap3.Connection(s, user=CUSER_DN, password=cuser_password) c.open() c.start_tls() c.bind() install_dn = 'uid=%s,%s' % (install, PEOPLE_DN) c.modify( install_dn, { 'passwordexpirationtime': [(ldap3.MODIFY_REPLACE, ['20170402170000Z']) ], 'nsroledn': [(ldap3.MODIFY_REPLACE, [])] }) print c.result c.unbind()
def ldap_server(unused_port, docker, request): docker.pull('minkwe/389ds:latest') ssl_directory = os.path.join(os.path.dirname(__file__), 'resources', 'ssl') ca_file = os.path.join(ssl_directory, 'ca.pem') ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) ctx.check_hostname = False ctx.load_verify_locations(cafile=ca_file) host = "127.0.0.1" host_port = unused_port() server_params = { 'host': host, 'port': host_port, 'base_dn': 'dc=example,dc=com', 'user': '******', 'password': '******', 'test_ou1': 'ou=test1,dc=example,dc=com', 'test_ou2': 'ou=test2,dc=example,dc=com', 'test_ou3': 'ou=test3,dc=example,dc=com', 'whoami': 'dn: cn=directory manager', 'ctx': ctx } container_args = { 'image': 'minkwe/389ds:latest', 'name': 'aioldap-test-server-{0}'.format(session_id()), 'ports': [389], 'detach': True, 'hostname': 'ldap.example.com', 'environment': { 'DIR_HOSTNAME': 'ldap.example.com', 'DIR_MANAGER_PASSWORD': server_params['password'], 'DIR_SUFFIX': server_params['base_dn'] }, 'host_config': docker.create_host_config(port_bindings={389: (host, host_port)}, binds={ ssl_directory: { 'bind': '/certs', 'ro': False }, }), 'volumes': ['/certs'] } container = docker.create_container(**container_args) try: docker.start(container=container['Id']) delay = 0.001 # 389 takes at least 15 to come up, go down, come up with SSL time.sleep(15) for i in range(100): try: server = ldap3.Server(host=host, port=host_port, get_info=None) conn = ldap3.Connection(server, user='******', password=server_params['password']) conn.bind() whoami = conn.extend.standard.who_am_i() assert whoami == 'dn: cn=directory manager', "Unexpected bind user" # Create an OU to throw some stuff res = conn.add(server_params['test_ou1'], object_class='organizationalUnit') assert res, "Failed to create ou=test1" res = conn.add(server_params['test_ou2'], object_class='organizationalUnit') assert res, "Failed to create ou=test2" res = conn.add(server_params['test_ou3'], object_class='organizationalUnit') assert res, "Failed to create ou=test3" break except AssertionError as err: pytest.fail(str(err)) except Exception as err: if delay > 40: pytest.fail('container startup took too long') time.sleep(delay) delay *= 2 else: pytest.fail("Cannot start LDAP server") container['host'] = host container['port'] = host_port container['ldap_params'] = server_params yield container finally: docker.kill(container=container['Id']) docker.remove_container(container['Id'])
##----------LOAD CONFIG-----------------------------------## import yaml with open('./config.yml', 'r') as f: cfg = yaml.load(f) ##-----------------CONNECT TO AD----------------------------## import ldap3 import json from tqdm import tqdm server = ldap3.Server(cfg['host'], get_info=ldap3.ALL) conn = ldap3.Connection(server, user=cfg['user'], password=cfg['pwd'], authentication=ldap3.NTLM, auto_bind=True) conn.start_tls() #Searching personal account of user MODULARIZE THIS SEGEMENT emailID = "*****@*****.**" conn.search(' ou=Users-AAD,dc=lux,dc=intra,dc=lighting,dc=com', '(mail=' + emailID + ')', attributes=ldap3.ALL_ATTRIBUTES) personal_entry_user = [ json.loads(e.entry_to_json()) for e in tqdm(conn.entries) ] #splicing out the LUX Id of the manager Refer "seeAlso" attribute for user, All LUX IDs do not have uniform number of elements
def ldap_link(self, username, password, mode='LOGIN'): # If no password provided, we will not try to authenticate if password: # Prepare LDAP connection details nt4_domain = settings.AD_NT4_DOMAIN.upper() dns_name = getattr(settings, "AD_DNS_NAME", nt4_domain).upper() use_ssl = getattr(settings, "AD_SSL", False) if use_ssl: default_port = 636 proto = 'ldaps' else: default_port = 389 proto = 'ldap' port = getattr(settings, 'AD_LDAP_PORT', default_port) ldap_url = '{}://{}:{}'.format(proto, dns_name, port) self.debug('ldap.initialize :: url: {}'.format(ldap_url)) # Prepare library ser = {} ser['allowed_referral_hosts'] = [("*", True)] con = {} con['user'] = "******".format(nt4_domain, username) con['password'] = password con['raise_exceptions'] = True con['authentication'] = ldap3.NTLM if use_ssl: certfile = settings.AD_CERT_FILE self.debug( 'ldap.ssl :: Activated - Cert file: {}'.format(certfile)) con['auto_bind'] = ldap3.AUTO_BIND_TLS_BEFORE_BIND ser['use_ssl'] = True ser['tls'] = ldap3.Tls(validate=ssl.CERT_REQUIRED, version=ssl.PROTOCOL_TLSv1) else: con['auto_bind'] = ldap3.AUTO_BIND_NO_TLS try: #self.debug('ldap.server params :: {}'.format(ser)) server = ldap3.Server(ldap_url, **ser) self.debug('ldap.server :: {}'.format(server)) #self.debug('ldap.connection params :: {}'.format(con)) answer = ldap3.Connection(server, **con) self.debug('ldap.connection :: {}'.format(answer)) #answer.open() #answer.bind() self.debug('ldap.connected :: Authorized') except LDAPSocketOpenError as e: # The access for this user has been denied, Debug it if required self.debug( "LDAP connect failed 'SocketOpenError' for url '{}' with error '{}'" .format(ldap_url, e)) answer = False except LDAPException as e: # The access for this user has been denied, Debug it if required self.debug( "LDAP connect failed 'LDAPException' for user '{}' with error '{}'" .format(username, e)) answer = False else: # The access for this user has been denied, Debug it if required self.debug("No password provided for user '{}'".format(username)) answer = False # Return the final result return answer
print(c.bind()) # perform the Bind operation if not c.bind(): print('error in bind', c.result) #_____________ # import ldap3 ldapServer = 'ldap://172.17.17.201:389' domain = 'd1' userName = '******' domainUserName = domain + '\\' + userName password = '******' conn = ldap3.__version__ print(conn) conn = ldap3.Connection(ldapServer) conn.open(domainUserName, password) from ldap3 import Server, Connection, ALL # define the server s = Server( 'ldap://172.17.17.201', get_info=ALL ) # define an unsecure LDAP server, requesting info on DSE and schema # define the connection c = Connection(s, user='******', password='******') print(c.bind()) # perform the Bind operation if not c.bind(): print('error in bind', c.result)
def POST(self, id): # Get configuration settings = self.user_manager.get_auth_method(id).get_settings() login_data = flask.request.form login = login_data["login"].strip().lower() password = login_data["password"] # do not send empty password to the LDAP if password.rstrip() == "": return self.template_helper.render( "custom_auth_form.html", template_folder="frontend/plugins/auth", settings=settings, error=_("Empty password")) try: # Connect to the ldap logger.debug('Connecting to ' + settings['host'] + ", port " + str(settings['port'])) if "bind_dn" in settings: bind_dn = { "user": settings["bind_dn"].format(login), "password": password } else: bind_dn = {} auto_bind = settings.get("auto_bind", True) conn = ldap3.Connection(ldap3.Server( settings['host'], port=settings['port'], use_ssl=settings["encryption"] == 'ssl', get_info=ldap3.ALL), auto_bind=auto_bind, **bind_dn) logger.debug('Connected to ' + settings['host'] + ", port " + str(settings['port'])) except LDAPException as e: logger.exception("Can't initialze connection to " + settings['host'] + ': ' + str(e)) return self.template_helper.render( "custom_auth_form.html", template_folder="frontend/plugins/auth", settings=settings, error=_("Cannot contact host")) attr_cn = settings.get("cn", "cn") attr_mail = settings.get("mail", "mail") try: ldap_request = settings["request"].format(login) conn.search(settings["base_dn"], ldap_request, attributes=[attr_cn, attr_mail]) user_data = conn.response[0] except (LDAPException, IndexError) as ex: logger.exception("Can't get user data : " + str(ex)) conn.unbind() return self.template_helper.render( "custom_auth_form.html", template_folder="frontend/plugins/auth", settings=settings, error=_("Unknown user")) if conn.rebind(user_data['dn'], password=password): try: email = user_data['attributes'][attr_mail] if isinstance(email, list): email = email[0] username = login realname = user_data["attributes"][attr_cn] if isinstance(realname, list): realname = realname[0] except KeyError as e: logger.exception("Can't get field " + str(e) + " from your LDAP server") return self.template_helper.render( "custom_auth_form.html", template_folder="frontend/plugins/auth", settings=settings, error=_("Can't get field {} from your LDAP server").format( str(e))) except LDAPException as e: logger.exception("Can't get some user fields") return self.template_helper.render( "custom_auth_form.html", template_folder="frontend/plugins/auth", settings=settings, error=_("Can't get some user fields")) finally: conn.unbind() if not self.user_manager.bind_user( id, (username, realname, email, {})): return redirect("/signin?binderror") auth_storage = self.user_manager.session_auth_storage().setdefault( id, {}) return redirect(auth_storage.get("redir_url", "/")) else: logger.debug('Auth Failed') conn.unbind() return self.template_helper.render( "custom_auth_form.html", base_template_folder="frontend/plugins/auth", settings=settings, error=_("Incorrect password"))
def main(arguments): parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('user') parser.add_argument('--cert-file', default=getenv('LDAP_CERT_FILE'), help="""Path to TLS certificate used for encrypting the connection to the LDAP server. Uses value of LDAP_CERT_FILE if defined.""") parser.add_argument('--host', default=getenv('LDAP_HOST'), help="""Name of the LDAP host. Uses value of LDAP_HOST by default.""") parser.add_argument( '--user-dn', default=getenv('LDAP_USER_DN'), help="""Base DN for user search in LDAP directory, including a placeholder for USER (will look something like "CN=%%s,CN=Users,DC=MyDomain,DC=com"). Uses value of LDAP_USER_DN by default.""") parser.add_argument( '--timeout', type=int, default=3, help='Timeout for LDAP server connection in seconds [%(default)s].') args = parser.parse_args(arguments) if not sys.stdin.isatty(): # stdin has content password = sys.stdin.read().strip() else: password = getpass.getpass() conn = ldap3.Connection( ldap3.Server( args.host, port=636, use_ssl=True, tls=ldap3.Tls( ca_certs_file=args.cert_file, # validate=ssl.CERT_NONE, validate=ssl.CERT_REQUIRED, ), get_info=ldap3.NONE, connect_timeout=args.timeout, ), auto_bind=False, client_strategy=ldap3.SYNC, user=args.user_dn % args.user, password=password, raise_exceptions=True, check_names=True) conn.start_tls() try: conn.bind() except LDAPInvalidCredentialsResult as err: print(err) status = 1 else: print('Success') status = 0 finally: conn.unbind() sys.exit(status)
def main(): argparser = argparse.ArgumentParser() argparser.add_argument('--ldapconfig', help='Path to YAML LDAP config file', default='/etc/ldap.yaml') argparser.add_argument( '--infrastructure-users', help=('Path to CSV file with infrastructure users config' ' (tokenauth format)'), default='/etc/kubernetes/infrastructure-users.csv') argparser.add_argument('--debug', help='Turn on debug logging', action='store_true') argparser.add_argument('--project', help='Project name to fetch LDAP users from', default='tools') argparser.add_argument('--interval', help='Seconds between between runs', default=60) argparser.add_argument('--once', help='Run once and exit', action='store_true') argparser.add_argument('kube_master_url', help='Full URL of Kubernetes Master') argparser.add_argument('tokenauth_output_path', help='Path to output tokenauth CSV file') argparser.add_argument('abac_output_path', help='Path to output abac JSONL file') args = argparser.parse_args() loglvl = logging.DEBUG if args.debug else logging.INFO logging.basicConfig(format='%(message)s', level=loglvl) with open(args.ldapconfig, encoding='utf-8') as f: ldapconfig = yaml.safe_load(f) cur_users = get_users_from_csv(args.tokenauth_output_path) infra_users = get_users_from_csv(args.infrastructure_users) while True: logging.info('starting a run') servers = ldap3.ServerPool([ ldap3.Server(s, connect_timeout=1) for s in ldapconfig['servers'] ], ldap3.POOLING_STRATEGY_ROUND_ROBIN, active=True, exhaust=True) with ldap3.Connection( servers, read_only=True, user=ldapconfig['user'], auto_bind=True, password=ldapconfig['password'], raise_exceptions=True, ) as conn: tools = get_tools_from_ldap(conn, args.project) new_tools = set(tools).union(set(infra_users)) - set(cur_users) if new_tools: # There are at least some new tools, so we have to: # 1. Regenerate the entire tokenauth file # 2. Regenerate entire ABAC file # 3. Write out kubeconfig files for all the new tools # 4. Restart the apiserver for uid in new_tools: if uid in tools: tools[uid].token = generate_pass(64) create_homedir(tools[uid]) write_kubeconfig(tools[uid], args.kube_master_url) create_namespace(tools[uid]) cur_users[uid] = tools[uid] logging.info('Provisioned creds for tool %s', tools[uid].name) elif uid in infra_users: cur_users[uid] = infra_users[uid] logging.info('Provisioned creds for infra user %s', infra_users[uid].name) write_tokenauth(cur_users.values(), args.tokenauth_output_path) write_abac(cur_users.values(), args.abac_output_path) subprocess.check_call( ['/bin/systemctl', 'restart', 'kube-apiserver']) logging.info('finished run, wrote %s new accounts', len(new_tools)) if args.once: break time.sleep(args.interval)
gluu_ldap_prop_fn = '/etc/jans/conf/jans-ldap.properties' for l in open(gluu_ldap_prop_fn): if l.startswith('bindPassword'): n = l.find(':') passwd_enc = l[n + 1:].strip() ldap_admin_pw = os.popen('/opt/jans/bin/encode.py -D ' + passwd_enc).read().strip() break else: print("Can't find ldap admin password") server = ldap3.Server("ldaps://localhost:1636", use_ssl=True) conn = ldap3.Connection(server, user="******", password=ldap_admin_pw) conn.bind() app_base_dn = 'ou={},ou=configuration,o=jans'.format(argsp.l) conn.search(search_base=app_base_dn, search_scope=ldap3.BASE, search_filter='(objectclass=*)', attributes=['jansConfDyn']) if conn.response and 'jansConfDyn' in conn.response[0]['attributes']: jansConfDyn = json.loads(conn.response[0]['attributes']['jansConfDyn'][0], object_pairs_hook=OrderedDict) else: print("Can't load jansConfDyn") sys.exit()
def create_connection(authtype=None, server=None, user=None, password=None, auto_bind=False, client_strategy=ldap3.SYNC, check_names=True, auto_referrals=False, receive_timeout=5, start_tls=False): """ Create a connection to the LDAP server. :param authtype: :param server: :param user: :param password: :param auto_bind: :param client_strategy: :param check_names: :param auto_referrals: :param receive_timeout: At the moment we do not use this, since receive_timeout is not supported by ldap3 < 2. :return: """ authentication = None if not user: authentication = ldap3.ANONYMOUS if authtype == AUTHTYPE.SIMPLE: if not authentication: authentication = ldap3.SIMPLE # SIMPLE works with passwords as UTF8 and unicode l = ldap3.Connection( server, user=user, password=password, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.NTLM: # pragma: no cover if not authentication: authentication = ldap3.NTLM # NTLM requires the password to be unicode l = ldap3.Connection( server, user=user, password=password, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) elif authtype == AUTHTYPE.SASL_DIGEST_MD5: # pragma: no cover if not authentication: authentication = ldap3.SASL password = to_utf8(password) sasl_credentials = (str(user), str(password)) l = ldap3.Connection( server, sasl_mechanism="DIGEST-MD5", sasl_credentials=sasl_credentials, auto_bind=auto_bind, client_strategy=client_strategy, authentication=authentication, check_names=check_names, # receive_timeout=receive_timeout, auto_referrals=auto_referrals) else: raise Exception("Authtype {0!s} not supported".format(authtype)) if start_tls: l.open(read_server_info=False) log.debug("Doing start_tls") r = l.start_tls(read_server_info=False) return l
def connection(**kwargs): """ Creates and returns a connection to the LDAP server. The user identifier, if given, should be keyword arguments matching the fields in settings.LDAP_AUTH_USER_LOOKUP_FIELDS, plus a `password` argument. """ # Format the DN for the username. format_username = import_func(settings.LDAP_AUTH_FORMAT_USERNAME) kwargs = {key: value for key, value in kwargs.items() if value} username = None password = None domain = None if kwargs: password = kwargs.pop("password") domain = kwargs.get("domain") username = format_username(kwargs) # Configure the connection. if settings.LDAP_AUTH_USE_TLS: auto_bind = ldap3.AUTO_BIND_TLS_BEFORE_BIND else: auto_bind = ldap3.AUTO_BIND_NO_TLS # Connect. if domain is not None: auth_url = settings.LDAP_AUTH_MULTIDOMAIN_URL.get(domain) else: auth_url = settings.LDAP_AUTH_URL try: c = ldap3.Connection( ldap3.Server( auth_url, allowed_referral_hosts=[("*", True)], get_info=ldap3.NONE, connect_timeout=settings.LDAP_AUTH_CONNECT_TIMEOUT, ), user=username, password=password, auto_bind=auto_bind, raise_exceptions=True, receive_timeout=settings.LDAP_AUTH_RECEIVE_TIMEOUT, ) except LDAPException as ex: logger.warning("LDAP connect failed: {ex}".format(ex=ex)) yield None return # If the settings specify an alternative username and password for querying, rebind as that. if ((settings.LDAP_AUTH_CONNECTION_USERNAME or settings.LDAP_AUTH_CONNECTION_PASSWORD) and (settings.LDAP_AUTH_CONNECTION_USERNAME != username or settings.LDAP_AUTH_CONNECTION_PASSWORD != password)): User = get_user_model() try: c.rebind( user=format_username({ User.USERNAME_FIELD: settings.LDAP_AUTH_CONNECTION_USERNAME }), password=settings.LDAP_AUTH_CONNECTION_PASSWORD, ) except LDAPException as ex: logger.warning("LDAP rebind failed: {ex}".format(ex=ex)) yield None return # Return the connection. logger.info("LDAP connect succeeded") try: yield Connection(c) finally: c.unbind()
def __init__(self, url, dn=None, secret=None, base="", debug=False, paged_size=1000, size_limit=None, time_limit=None): """If you do not specify credentials (dn and secret) it will try to load them from ~/.netrc file. @param server: url of LDAP Server @param dn: username of the service account @param secret: password of the servce account """ self.filter = '' self.scope = ldap3.SEARCH_SCOPE_WHOLE_SUBTREE self.paged_size = paged_size if not size_limit: self.size_limit = Integer0ToMax(0) else: self.size_limit = size_limit if not time_limit: self.time_limit = Integer0ToMax(0) else: self.time_limit = time_limit self.attrs = '*' self.logger = logging.getLogger('ldap') if debug: self.logger.setLevel(logging.DEBUG) else: self.logger.setLevel(logging.INFO) self.url = url self.dn = dn self.secret = secret u = urlparse(url) if base: self.base = base else: self.base = u.path[1:] if u.scheme == 'ldaps': use_ssl = True else: use_ssl = False if ":" in u.hostname: u.hostname = u.hostname.split(":")[0] if dn is None: import netrc try: netrc_config = netrc.netrc() for h in netrc_config.hosts: if h.lower() == u.hostname.lower(): dn, account, secret = netrc_config.authenticators(h) break except Exception as e: logging.warning("~/.netrc ignored due to: %s" % e) self.server = ldap3.Server(host=u.hostname, port=u.port, use_ssl=use_ssl) self.conn = ldap3.Connection( self.server, auto_bind=True, # client_strategy=ldap3.STRATEGY_REUSABLE_THREADED, # .STRATEGY_SYNC, client_strategy=ldap3.STRATEGY_SYNC, user=dn, password=secret, authentication=ldap3.AUTH_SIMPLE) try: ret = self.conn.bind() except Exception as e: self.logger.error(e) else: self._connected = True
def findUser(user,group,conn,base,search): ret=conn.search(base,search,search_scope=ldap3.SUBTREE,attributes=['member']) entry=conn.entries[0] members=json.loads(entry.entry_to_json())['attributes']['member'] # print(members) for member in members: # print(member) if "User" in member: r=record(member) print(r.CN) user.append(r.CN) if "Groups" in member: print("Recurssion") r=record(member) search='(&(objectCategory=GROUP)(cn=%s))'%r.CN user=findUser(user,r.CN,conn,base,search) # print(users,r.CN,conn,base,search) return user if __name__ == "__main__": server=ldap3.Server('adserver.com') conn=ldap3.Connection(server,auto_bind=True) grp='ADGroupWithRecursiveGroup' base='dc=ad,dc='<domainname/CompanyName>",dc=com' search='(&(objectCategory=GROUP)(cn=%s))'%grp users=[] users=findUser(users,grp,conn,base,search) print(set(users)) conn.unbind()
def handle_ec2_change(event, context): # Filter out missleading events if not event['detail']['state'] in ['pending', 'terminated'] : return json.dumps({'status': 'error'}) # Initial print print("Working on instance {} - {}".format(get_instance_id(event), event['detail']['state'])) # Populate common variables directory_id = get_directory_id() desc = collect_ds_information(directory_id) if not desc: print('There is no such a directory {}'.format(directory_id)) return basedn = get_basedn() print("* Dealing with {}".format(basedn)) domain_name = desc['Name'] dns_servers = desc['DnsIpAddrs'] # Open connection Active Directory server = ldap3.Server('{}://{}'.format(get_proto(), get_ldap_host(domain_name))) conn = ldap3.Connection(server, **get_directory_credentials(directory_id, domain_name)) if not conn.bind(): print('error in bind', conn.result) # Search for EC2 instances searchState = conn.search(basedn,"(description={})".format(get_instance_id(event))) print("* LDAP search status {}: {}".format(searchState, len(conn.entries))) # * Pending # Validate whether EC2 is in AD already # Register trigger SSM document against server if event['detail']['state'] == 'pending': # Describe instance to find all tags # No instance were found if not searchState or len(conn.entries) == 0: # Validate the server should be joined print("* Describing EC2 instance {}".format(get_instance_id(event))) ec2 = boto3.client('ec2', region_name=event['region']) ds = ec2.describe_instances( InstanceIds = [ get_instance_id(event) ] ) instances = [item for sublist in ds.get('Reservations', []) for item in sublist['Instances']] fins = [x for x in instances if x['InstanceId'] == get_instance_id(event)] if len(fins) <= 0: return json.dumps({'status': 'no such instances'}) tags = {x['Key'].strip().lower(): x['Value'].strip() for x in fins[0].get('Tags', []) if tags_prefix.lower() in x['Key'].lower()} # Determine whether to join or not join_domain = tags.get('domain:join', 'false').lower() in ['true', '1', 't', 'y', 'yes', 'yeah', 'yup', 'certainly', 'uh-huh'] # print("* Server is designated for joining to domain: {}".format(join_domain)) if not join_domain: return json.dumps({'status': 'ok'}) # Run SSM Document print('* Initiating join {} for instance {}'.format(get_join_document(), get_instance_id(event))) ssm = boto3.client('ssm', region_name=event['region']) ass = ssm.create_association( Name = get_join_document(), Targets = [ { 'Key': 'InstanceIds', 'Values': [ get_instance_id(event) ] } ], Parameters = { 'directoryId': [directory_id], 'directoryName': [domain_name], 'directoryOU': [basedn], 'dnsIpAddresses': [ ' '.join(dns_servers) ] } ) print(ass) # * Termination # Remove from AD # Remove from Monitoring if event['detail']['state'] == 'terminated': # Delete From Active directory if searchState or len(conn.entries) > 0: print('Dropping following DNs:') for r in list(conn.entries): print(r.entry_dn) # Delete entry from LDAP delete_from_ldap(conn, basedn, r, get_instance_id(event)) # Dummy return return json.dumps({'status': 'ok'})
def main(): argparser = argparse.ArgumentParser() group1 = argparser.add_mutually_exclusive_group() group2 = argparser.add_mutually_exclusive_group() argparser.add_argument( "--ldapconfig", help="Path to YAML LDAP config file", default="/etc/ldap.yaml", ) argparser.add_argument("--debug", help="Turn on debug logging", action="store_true") argparser.add_argument( "--project", help="Project name to fetch LDAP users from", default="tools", ) argparser.add_argument( "--admins-only", "-a", help="Only manage admin accounts for this project", default=False, action="store_true", ) group1.add_argument("--interval", help="Seconds between between runs", default=60) group1.add_argument("--once", help="Run once and exit", action="store_true") argparser.add_argument( "--local", help="Specifies this is not running in Kubernetes (for debugging)", action="store_true", ) group2.add_argument( "--force-migrate", help=( "For full Kubernetes cluster change: switches every account ", "to use the toolforge context. Requires the --once option", ), action="store_true", ) group2.add_argument( "--gentle-mode", help=("Before general release, keep current context set to default " "while the new Kubernetes cluster is considered opt-in"), action="store_true", ) args = argparser.parse_args() if args.force_migrate and not args.once: argparser.error("--once is required when --force-migrate is set") loglvl = logging.DEBUG if args.debug else logging.INFO logging.basicConfig(format="%(message)s", level=loglvl) with open(args.ldapconfig, encoding="utf-8") as f: ldapconfig = yaml.safe_load(f) if args.local: k_config.load_kube_config() else: k_config.load_incluster_config() k8s_api = K8sAPI() api_server, ca_data = k8s_api.get_cluster_info() while True: logging.info("starting a run") # Touch a temp file for a Kubernetes liveness check to prevent hangs Path("/tmp/run.check").touch() cur_users, expiring_users = k8s_api.get_current_users(args.admins_only) servers = ldap3.ServerPool( [ ldap3.Server(s, connect_timeout=1) for s in ldapconfig["servers"] ], ldap3.ROUND_ROBIN, active=True, exhaust=True, ) with ldap3.Connection( servers, read_only=True, user=ldapconfig["user"], auto_bind=True, password=ldapconfig["password"], raise_exceptions=True, receive_timeout=60, ) as conn: tools = [] if not args.admins_only: tools = get_tools_from_ldap(conn, args.project) admins = get_admins_from_ldap(conn, args.project) # If this is just migrating all remaining users (--force-migrate) # we should short-circuit the while True loop as soon as possible to # reduce all the churn. if args.force_migrate: for tool_name in cur_users["tools"]: tools[tool_name].switch_context() break if tools: new_tools = process_new_users(tools, cur_users["tools"], k8s_api, args.gentle_mode) if expiring_users["tools"]: for tool_name in expiring_users["tools"]: tools[tool_name].pk = generate_pk() k8s_api.generate_csr(tools[tool_name].pk, tool_name) tools[tool_name].cert = k8s_api.approve_cert(tool_name) tools[tool_name].create_homedir() tools[tool_name].write_kubeconfig(api_server, ca_data, args.gentle_mode) k8s_api.update_expired_ns(tools[tool_name]) logging.info("Renewed creds for tool %s", tool_name) if admins: new_admins = process_new_users(admins, cur_users["admins"], k8s_api, args.gentle_mode) if expiring_users["admins"]: for admin_name in expiring_users["admins"]: admins[admin_name].pk = generate_pk() k8s_api.generate_csr(admins[admin_name].pk, admin_name) admins[admin_name].cert = k8s_api.approve_cert(admin_name) admins[admin_name].create_homedir() admins[admin_name].write_kubeconfig( api_server, ca_data, args.gentle_mode) k8s_api.update_expired_ns(admins[admin_name]) logging.info("Renewed creds for admin user %s", admin_name) logging.info("finished run, wrote %s new accounts", new_tools + new_admins) if args.once: break time.sleep(args.interval)
def renew_license(): ldap_properties_fn = '/etc/jans/conf/jans-ldap.properties' salt_fn = '/etc/jans/conf/salt' encode_fn = '/opt/jans/bin/encode.py' for fn in (ldap_properties_fn, salt_fn, encode_fn): if not os.path.exists(fn): print("Not found:", fn) return prop = {'bindDN': '', 'bindPassword': '', 'servers': ''} content = open(ldap_properties_fn).readlines() for l in content: ls = l.strip() if ls and not ls[0] == '#': for p in prop: if ls.startswith(p): v = ls[len(p):].strip()[1:].strip() v = v.replace('\\=', '=') v = v.replace("\\'", "'") v = v.replace('\\"', '"') prop[p] = v cmd = encode_fn + ' -D ' + prop['bindPassword'] encoded_password = os.popen(cmd).read().strip() if not encoded_password: print("Password can't be encoded") return ldap_server = prop['servers'].split(',')[0].strip() ldap_host, ldap_port = ldap_server.split(':') server = ldap3.Server(ldap_host, port=int(ldap_port), use_ssl=True) ldap_conn = ldap3.Connection(server, user=prop['bindDN'], password=encoded_password) try: ldap_conn.bind() except: print("Can't connect to ldap server") return scr_dn = 'inum=92F0-BF9E,ou=scripts,o=jans' ldap_conn.search(search_base=scr_dn, search_scope=ldap3.BASE, search_filter='(objectClass=*)', attributes=['jansConfProperty']) result = ldap_conn.response if not result: print("Can't find Super Gluu script with dn {} in ldap".format(scr_dn)) return for oxprop_s in result[0]['attributes']['jansConfProperty']: oxprop = json.loads(oxprop_s) if oxprop['value1'] == 'license_file': license_fn = oxprop['value2'] break else: print("Super gluu license file not found in ldap") return if not os.path.exists(license_fn): print("Super gluu license file {} does not exist".format(license_fn)) return with open(license_fn) as f: license_js = json.load(f) licenseId = license_js.get('licenseId') if not licenseId: print(license_fn, " does not include licenseId") return url_metadata = 'https://license.gluu.org/oxLicense/rest/metadata?licenseId=' + licenseId try: url_metadata_fd = urllib.urlopen(url_metadata) metadata_s = url_metadata_fd.read() except: print("Can't read from", url_metadata) return try: metadata = json.loads(metadata_s) except: print("Can't load json from", metadata_s) return if 'expiration_date' in metadata: expiration_date = metadata['expiration_date'] else: print("Can't get expiration_date from json", metadata) return seconds_left = int(expiration_date / 1000) - time.time() a_day = 24 * 60 * 60 if seconds_left >= a_day: print(int(seconds_left / a_day), "days left to expire. No need to renew") return url = 'https://license.gluu.org/oxLicense/rest/generate?licenseId=' + licenseId try: url_fd = urllib.urlopen(url) data_s = url_fd.read() except: print("Can't read from", url) return try: data = json.loads(data_s) except: print("Can't load json from", data) return if data and data[0].get('license', '').startswith('rO'): license_js['license'] = data[0]['license'] shutil.copyfile(license_fn, license_fn + '.back_' + time.ctime().replace(' ', '_')) with open(license_fn, 'w') as w: json.dump(license_js, w, indent=2) else: print("Data read from", url, "does not contain valid license") return print("Super Gluu license was renewed successfully")
def get_ldap_connection(host, user, password): """ Returns a connection to the LDAP host """ server = ldap3.Server(host) conn = ldap3.Connection(server, user, password, auto_bind=True) return conn