#!/usr/bin/python3 # graylog group mapping script # Copyright (C) 2022 Philipp Fromme # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import argparse import configparser import logging import json import requests import ldap general_config_path = "/etc/graylog-api-scripts.conf" general_config = configparser.ConfigParser() general_config.read(general_config_path) api_token = general_config['DEFAULTS']['token'] api_token_password = "token" api_url_base = "http://127.0.0.1:9000/api/" headers = {"Content-Type": "application/json", "X-Requested-By": "cli"} server_uri = general_config['LDAP']['server_uri'] bind_dn = general_config['LDAP']['bind_dn'] bind_passwd = general_config['LDAP']['bind_passwd'] search_base_dn = general_config['LDAP']['search_base_dn'] ldap_group_search = general_config['LDAP']['ldap_group_search'] search_attribute = general_config['LDAP']['search_attribute'] logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%b %d %H:%M:%S', level='WARNING') LOGGER = logging.getLogger() def connect_to_ldap(server_uri): try: conn = ldap.initialize(server_uri) conn.protocol_version = ldap.VERSION3 conn.set_option(ldap.OPT_REFERRALS, 0) conn.set_option(ldap.OPT_DEBUG_LEVEL, 255) conn.simple_bind_s(bind_dn, bind_passwd) return conn except ldap.LDAPError as e: raise Exception("Failed to connet to server {}: {}".format(server_uri, e)) def get_ldap_groups(conn): ldap_result_id = conn.search(search_base_dn, ldap.SCOPE_SUBTREE, ldap_group_search, [search_attribute]) result_type, result_data = conn.result(ldap_result_id, 1) return result_data def get_users(): api_url = '{}users'.format(api_url_base) response = requests.get(api_url, headers=headers, auth=(api_token, api_token_password)) if response.status_code == 200: return json.loads(response.content.decode('utf-8')) else: return None def change_user_roles(user_id, roles): api_url = '{}users/{}'.format(api_url_base, user_id) json_data = {"roles": roles} response = requests.put(api_url, json=json_data, headers=headers, auth=(api_token, api_token_password)) if response.status_code == 204: return True else: return False def delete_user(user_id): api_url = '{}users/id/{}'.format(api_url_base, user_id) response = requests.delete(api_url, headers=headers, auth=(api_token, api_token_password)) if response.status_code == 204: return True else: return False def main(): parser = argparse.ArgumentParser(description="Map LDAP Groups to Graylog Groups") parser.add_argument("--level", "-l", help="Set the log level", default="WARNING") args = parser.parse_args() LOGGER.setLevel(args.level) config_path = "/etc/graylog-group-mapping.conf" config = configparser.ConfigParser() config.read(config_path) default_role = config['DEFAULTS']['default-role'] role_mapping = {} # create a mapping from the config file to later give users their new_roles for mapping in config['GROUP-MAPPING']: role_mapping[mapping] = config['GROUP-MAPPING'][mapping] groupMembers = {} conn = connect_to_ldap(server_uri) groups = get_ldap_groups(conn) # sort what we found in ldap by throwing away everything # besides groups and who is a member in them for group in groups: name = group[0].split(",")[0].split("=")[1] members = group[1] groupMembers[name] = [] if search_attribute in members: members = members[search_attribute] for member in members: groupMembers[name].append(member.decode().split(",")[0].split("=")[1]) # get users in graylog and iterate over them user_list = get_users() if user_list is not None: for user in user_list['users']: if user['external'] == False: continue user_id = user['id'] username = user['username'] roles = user['roles'] # check first if user is member of any specified group in_config_group = False for group in groupMembers: if username in groupMembers[group]: in_config_group = True break if in_config_group: new_roles = [default_role] for group in role_mapping: if username in groupMembers[group]: new_roles.append(role_mapping[group]) new_roles = set(new_roles) if new_roles != set(roles): new_roles = list(new_roles) LOGGER.warning("%s has roles %s and gets new roles %s", username, roles, new_roles) change_user_roles(user_id, new_roles) else: LOGGER.info("%s: nothing changed", username) else: LOGGER.warning("%s not in any config group, therefore deleting this graylog user", username) delete_user(user_id) if __name__ == "__main__": main()