graylog-group-mapping 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #!/usr/bin/python3
  2. # graylog group mapping script
  3. # Copyright (C) 2022 Philipp Fromme
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. import argparse
  18. import configparser
  19. import logging
  20. import json
  21. import requests
  22. import ldap
  23. general_config_path = "/etc/graylog-api-scripts.conf"
  24. general_config = configparser.ConfigParser()
  25. general_config.read(general_config_path)
  26. api_token = general_config['DEFAULTS']['token']
  27. api_token_password = "token"
  28. api_url_base = "http://127.0.0.1:9000/api/"
  29. headers = {"Content-Type": "application/json", "X-Requested-By": "cli"}
  30. server_uri = general_config['LDAP']['server_uri']
  31. bind_dn = general_config['LDAP']['bind_dn']
  32. bind_passwd = general_config['LDAP']['bind_passwd']
  33. search_base_dn = general_config['LDAP']['search_base_dn']
  34. ldap_group_search = general_config['LDAP']['ldap_group_search']
  35. search_attribute = general_config['LDAP']['search_attribute']
  36. logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%b %d %H:%M:%S',
  37. level='WARNING')
  38. LOGGER = logging.getLogger()
  39. def connect_to_ldap(server_uri):
  40. try:
  41. conn = ldap.initialize(server_uri)
  42. conn.protocol_version = ldap.VERSION3
  43. conn.set_option(ldap.OPT_REFERRALS, 0)
  44. conn.set_option(ldap.OPT_DEBUG_LEVEL, 255)
  45. conn.simple_bind_s(bind_dn, bind_passwd)
  46. return conn
  47. except ldap.LDAPError as e:
  48. raise Exception("Failed to connet to server {}: {}".format(server_uri, e))
  49. def get_ldap_groups(conn):
  50. ldap_result_id = conn.search(search_base_dn, ldap.SCOPE_SUBTREE, ldap_group_search, [search_attribute])
  51. result_type, result_data = conn.result(ldap_result_id, 1)
  52. return result_data
  53. def get_users():
  54. api_url = '{}users'.format(api_url_base)
  55. response = requests.get(api_url, headers=headers, auth=(api_token, api_token_password))
  56. if response.status_code == 200:
  57. return json.loads(response.content.decode('utf-8'))
  58. else:
  59. return None
  60. def change_user_roles(user_id, roles):
  61. api_url = '{}users/{}'.format(api_url_base, user_id)
  62. json_data = {"roles": roles}
  63. response = requests.put(api_url, json=json_data, headers=headers, auth=(api_token, api_token_password))
  64. if response.status_code == 204:
  65. return True
  66. else:
  67. return False
  68. def delete_user(user_id):
  69. api_url = '{}users/id/{}'.format(api_url_base, user_id)
  70. response = requests.delete(api_url, headers=headers, auth=(api_token, api_token_password))
  71. if response.status_code == 204:
  72. return True
  73. else:
  74. return False
  75. def main():
  76. parser = argparse.ArgumentParser(description="Map LDAP Groups to Graylog Groups")
  77. parser.add_argument("--level", "-l", help="Set the log level", default="WARNING")
  78. args = parser.parse_args()
  79. LOGGER.setLevel(args.level)
  80. config_path = "/etc/graylog-group-mapping.conf"
  81. config = configparser.ConfigParser()
  82. config.read(config_path)
  83. default_role = config['DEFAULTS']['default-role']
  84. role_mapping = {}
  85. # create a mapping from the config file to later give users their new_roles
  86. for mapping in config['GROUP-MAPPING']:
  87. role_mapping[mapping] = config['GROUP-MAPPING'][mapping]
  88. groupMembers = {}
  89. conn = connect_to_ldap(server_uri)
  90. groups = get_ldap_groups(conn)
  91. # sort what we found in ldap by throwing away everything
  92. # besides groups and who is a member in them
  93. for group in groups:
  94. name = group[0].split(",")[0].split("=")[1]
  95. members = group[1]
  96. groupMembers[name] = []
  97. if search_attribute in members:
  98. members = members[search_attribute]
  99. for member in members:
  100. groupMembers[name].append(member.decode().split(",")[0].split("=")[1])
  101. # get users in graylog and iterate over them
  102. user_list = get_users()
  103. if user_list is not None:
  104. for user in user_list['users']:
  105. if user['external'] == False:
  106. continue
  107. user_id = user['id']
  108. username = user['username']
  109. roles = user['roles']
  110. # check first if user is member of any specified group
  111. in_config_group = False
  112. for group in groupMembers:
  113. if username in groupMembers[group]:
  114. in_config_group = True
  115. break
  116. if in_config_group:
  117. new_roles = [default_role]
  118. for group in role_mapping:
  119. if username in groupMembers[group]:
  120. new_roles.append(role_mapping[group])
  121. new_roles = set(new_roles)
  122. if new_roles != set(roles):
  123. new_roles = list(new_roles)
  124. LOGGER.warning("%s has roles %s and gets new roles %s", username, roles, new_roles)
  125. change_user_roles(user_id, new_roles)
  126. else:
  127. LOGGER.info("%s: nothing changed", username)
  128. else:
  129. LOGGER.warning("%s not in any config group, therefore deleting this graylog user", username)
  130. delete_user(user_id)
  131. if __name__ == "__main__":
  132. main()