main.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import json
  2. import os
  3. import sys
  4. import uuid
  5. import string
  6. import random
  7. from datetime import datetime
  8. import requests
  9. # -------------------------Env
  10. # Для того, чтобы получить токен https://proglib.io/p/telegram-bot
  11. bot_token = os.environ['BOT_TOKEN']
  12. # Для получения chat-id сначала пишем хоть одно сообление боту, далее используем https://api.telegram.org/bot<token>/getUpdates
  13. chat_id_var = os.environ['CHAT_ID']
  14. # набор типов событий, на которые алертить, без деталей
  15. temp_any_event_dict = os.environ['EVENT_DICT']
  16. # Включение detection rules with details
  17. rule_sg_on = os.environ['RULE_SG_ON']
  18. rule_bucket_on = os.environ['RULE_BUCKET_ON']
  19. rule_secret_on = os.environ['RULE_SECRET_ON']
  20. # Active Remediations
  21. del_rule_on = os.environ['DEL_RUL_ON']
  22. del_perm_secret_on = os.environ['DEL_PERM_SECRET_ON']
  23. #--------------Преобразование any_event_dict
  24. any_event_dict = temp_any_event_dict.split(",")
  25. # -------------------------
  26. def handler(event, context):
  27. # Общая функция, которую вызывает триггер вызова функции
  28. # Тригер преобразовывает исходный json передаваемый в event в dict c помощью метода json.loads.
  29. # https://cloud.yandex.ru/docs/functions/concepts/trigger/cloudlogs-trigger
  30. # https://cloud.yandex.ru/docs/functions/lang/python/handler
  31. # Вызов функции для парсинга
  32. main_parse(event)
  33. def main_parse(event):
  34. full_log = []
  35. # Пробегаемся по сообщению и формируем dict с json событий trails
  36. for item in event['messages']:
  37. for log_entry in item['details']['messages']:
  38. full_log.append(log_entry['json_payload'])
  39. # вызов функций правиил:
  40. rule_any_event(full_log) # включено всегда
  41. # Включаем эти правила в зависимости от переменных
  42. if (rule_sg_on == "True"):
  43. rule_sg(full_log)
  44. if (rule_bucket_on == "True"):
  45. rule_bucket(full_log)
  46. if (rule_secret_on == "True"):
  47. rule_secret(full_log)
  48. def prepare_for_alert(json_dict):
  49. # Функция, которая готовит словарь с данными из ивента для алерта
  50. prep_dict = {}
  51. prep_dict['🕘 timestamp'] = json_dict['event_time']
  52. prep_dict['👨 subject_name'] = json_dict['authentication']['subject_name']
  53. prep_dict['☁️ cloud_name'] = json_dict['resource_metadata']['path'][0]['resource_name']
  54. prep_dict['🗂 folder_name'] = json_dict['resource_metadata']['path'][1]['resource_name']
  55. prep_dict['subject_id'] = json_dict['authentication']['subject_id']
  56. prep_dict['subject_type'] = json_dict['authentication']['subject_type'].replace('_', '')
  57. prep_dict['folder_id'] = json_dict['resource_metadata']['path'][1]['resource_id']
  58. return prep_dict
  59. # -----------------Detection rules
  60. def rule_sg(g):
  61. print('VIZOV KAGDIY RAZ RULE_SG!!!!!!')
  62. #Правило: "Create danger, ingress ACL in SG (0.0.0.0/0)"
  63. TUMBLR = False # Переключатель срабатывания правила
  64. for json_dict in g:
  65. if (json_dict['event_type'] in ["yandex.cloud.audit.network.UpdateSecurityGroup", "yandex.cloud.audit.network.CreateSecurityGroup"]
  66. and json_dict['event_status'] != "STARTED"):
  67. print('debug infor!!!!!!')
  68. print(json_dict['event_type'])
  69. for item2 in json_dict['details']['rules']:
  70. # print(item2['direction'])
  71. if (item2['direction'] == "INGRESS" and "cidr_blocks" in item2 and item2['cidr_blocks']['v4_cidr_blocks'] == ['0.0.0.0/0']):
  72. # print(item2['cidr_blocks']['v4_cidr_blocks'])
  73. TUMBLR = True
  74. # Кастомные поля для вывода в алерт
  75. custom_dict = {}
  76. # для добавления в url
  77. folder_id = json_dict['resource_metadata']['path'][1]['resource_id']
  78. # для добавления в url
  79. security_group_id = json_dict['details']['security_group_id']
  80. custom_dict[
  81. '🔗 url_to_sec_group'] = f"https://console.cloud.yandex.ru/folders/{folder_id}/vpc/security-groups/{security_group_id}/overview"
  82. custom_dict['🕸 network_name'] = json_dict['details']['network_name']
  83. custom_dict['security_group_id'] = json_dict['details']['security_group_id']
  84. security_rule_id = json_dict['details']['rules'][0]['id']
  85. custom_dict['security_group_name'] = json_dict['details']['security_group_name']
  86. custom_dict['security_rule_id'] = json_dict['details']['rules'][0]['id']
  87. custom_dict['ports'] = json_dict['details']['rules'][0]['ports']['to_port']
  88. # Вызов функции подготовки базовых полей
  89. result_prep_f = prepare_for_alert(json_dict)
  90. # Вызов реагирования
  91. if (TUMBLR == True and del_rule_on == "True"): #and TUMBLR == True and
  92. print('debug infor!!!!!!')
  93. print('vizov function reagirovanya!!!!!!')
  94. del_rule(security_group_id, security_rule_id)
  95. custom_dict['Выполнено реагирование'] = "Опасное правило удалено"
  96. # Объединение базовых полей и кастомных
  97. sum_of_dict = {**result_prep_f, **custom_dict}
  98. # Вызов отправки в телеграм, если есть сработка
  99. event_type = json_dict['event_type']
  100. if (TUMBLR):
  101. send_message(sum_of_dict, event_type)
  102. TUMBLR = False
  103. # ----
  104. def rule_bucket(g):
  105. #Правило: "Change Bucket access to public"
  106. TUMBLR = False # Переключатель срабатывания правила
  107. for json_dict in g:
  108. if (json_dict['event_type'] == "yandex.cloud.audit.storage.BucketUpdate" and json_dict['event_status'] != "STARTED"):
  109. if ("true" in [json_dict['details']['list_access'], json_dict['details']['objects_access'], json_dict['details']['settings_read_access']]):
  110. TUMBLR = True
  111. # Кастомные поля для вывода в алерт
  112. custom_dict = {}
  113. custom_dict['🧺 bucket_name'] = json_dict['details']['bucket_id']
  114. bucket_id = json_dict['details']['bucket_id']
  115. # для добавления в url
  116. folder_id = json_dict['resource_metadata']['path'][1]['resource_id']
  117. custom_dict[
  118. '🔗 bucket_url'] = f"https://console.cloud.yandex.ru/folders/{folder_id}/storage/bucket/{bucket_id}?section=settings"
  119. # Вызов функции подготовки базовых полей
  120. result_prep_f = prepare_for_alert(json_dict)
  121. # Объединение базовых полей и кастомных
  122. sum_of_dict = {**result_prep_f, **custom_dict}
  123. # Вызов отправки в телеграм, если есть сработка
  124. event_type = json_dict['event_type']
  125. if (TUMBLR):
  126. send_message(sum_of_dict, event_type)
  127. # -------
  128. def rule_secret(g):
  129. #Правило: "Assign rights to the secret (LockBox) to some account"
  130. TUMBLR = False # Переключатель срабатывания правила
  131. for json_dict in g:
  132. if (json_dict['event_type'] in ["yandex.cloud.audit.lockbox.UpdateSecretAccessBindings"] and json_dict['event_status'] != "STARTED" and json_dict['event_status'] == "DONE"):
  133. for item2 in json_dict['details']['access_binding_deltas']:
  134. if (item2['action'] == "ADD"):
  135. TUMBLR = True
  136. # Кастомные поля для вывода в алерт
  137. custom_dict = {}
  138. # для добавления в url
  139. folder_id = json_dict['resource_metadata']['path'][1]['resource_id']
  140. # для добавления в url
  141. secret_id = json_dict['details']['secret_id']
  142. custom_dict['assigned_role'] = json_dict['details']['access_binding_deltas'][0]['access_binding']['role_id']
  143. role_id = json_dict['details']['access_binding_deltas'][0]['access_binding']['role_id']
  144. sa_id = json_dict['details']['access_binding_deltas'][0]['access_binding']['subject_id']
  145. custom_dict['assigned_subject'] = json_dict['details']['access_binding_deltas'][0]['access_binding']['subject_name']
  146. custom_dict['assigned_subject_type'] = "*" + \
  147. json_dict['details']['access_binding_deltas'][0]['access_binding']['subject_type'] + "*"
  148. custom_dict['🔐 secret_name'] = json_dict['details']['secret_name']
  149. custom_dict['🔗 url_to_secret'] = f"https://console.cloud.yandex.ru/folders/{folder_id}/lockbox/secret/{secret_id}/overview"
  150. # Вызов функции подготовки базовых полей
  151. result_prep_f = prepare_for_alert(json_dict)
  152. # Вызов реагирования
  153. if (TUMBLR == True and del_perm_secret_on == "True"):
  154. del_perm_secret(secret_id, role_id, sa_id)
  155. custom_dict['Выполнено реагирование'] = "Назначенные права удалены"
  156. # Объединение базовых полей и кастомных
  157. sum_of_dict = {**result_prep_f, **custom_dict}
  158. # Вызов отправки в телеграм, если есть сработка
  159. event_type = json_dict['event_type']
  160. if (TUMBLR):
  161. send_message(sum_of_dict, event_type)
  162. # --------------------any-event-funct
  163. #Функция для легкого срабатывания по указанным событиям (не выводит деталей, не содержит реагирования)
  164. def rule_any_event(g):
  165. #Правило: "Change Bucket access to public"
  166. TUMBLR = False # Переключатель срабатывания правила
  167. for json_dict in g:
  168. if (json_dict['event_type'] in any_event_dict and json_dict['event_status'] != "STARTED"):
  169. TUMBLR = True
  170. # Вызов функции подготовки базовых полей
  171. result_prep_f = prepare_for_alert(json_dict)
  172. # Вызов отправки в телеграм, если есть сработка
  173. event_type = json_dict['event_type']
  174. if (TUMBLR):
  175. send_message(result_prep_f, event_type)
  176. # --------Telegram
  177. def send_message(text, event_type):
  178. # Для того, чтобы получить токен https://proglib.io/p/telegram-bot
  179. # Для получения chat-id сначала пишем хоть одно сообление боту, далее используем https://api.telegram.org/bot<token>/getUpdates
  180. # На входе для функции в vars вынести chat_id, token
  181. if event_type in ["yandex.cloud.audit.network.UpdateSecurityGroup", "yandex.cloud.audit.network.CreateSecurityGroup"]:
  182. result_text = '*⛔️ Detection rule* : "Create danger, ingress ACL in SG (0.0.0.0/0)":\n\n'
  183. elif event_type in ["yandex.cloud.audit.storage.BucketUpdate"]:
  184. result_text = '*⛔️ Detection rule* : "Change Bucket access to public":\n\n'
  185. elif event_type in ["yandex.cloud.audit.lockbox.UpdateSecretAccessBindings"]:
  186. result_text = '*⛔️ Detection rule* : "Assign rights to the secret (LockBox) to some account":\n\n'
  187. else:
  188. result_text = f'*⛔️ Detection rule on event* : "{event_type}":\n\n'
  189. for item in text:
  190. result_text = result_text + '*' + item + '*' + ': ' + text[item] + '\n'
  191. print(result_text)
  192. token = bot_token
  193. chat_id = chat_id_var
  194. url_req = "https://api.telegram.org/bot" + token + "/sendMessage" + \
  195. "?chat_id=" + chat_id + "&text=" + result_text + "&parse_mode=Markdown"
  196. results = requests.get(url_req)
  197. print(results.json())
  198. # -----------------------------#Active remediation
  199. # Get-token
  200. def get_token():
  201. response = requests.get(
  202. 'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token', headers={"Metadata-Flavor": "Google"})
  203. return response.json().get('access_token')
  204. # ----------
  205. # Удаление sg правила
  206. def del_rule(sg_id, sg_rule_id):
  207. token = get_token()
  208. request_json_data = {"deletionRuleIds": [f"{sg_rule_id}"]}
  209. response = requests.patch('https://vpc.api.cloud.yandex.net/vpc/v1/securityGroups/'+sg_id+'/rules', data=json.dumps(request_json_data), headers={"Accept": "application/json", "Authorization": "Bearer "+token})
  210. print("START DEBUG--------------------------")
  211. #print(response)
  212. #print(request_json_data)
  213. #print(token)
  214. #print(response.request.url)
  215. #print(response.request.body)
  216. #print(response.request.headers)
  217. #return response
  218. print("STOP DEBUG----------------")
  219. # ----------
  220. # Удаление назначенных прав на секрет
  221. def del_perm_secret(secret_id, role_id, sa_id):
  222. token = get_token()
  223. request_json_data = {"accessBindingDeltas": [{"action": "REMOVE", "accessBinding": {
  224. "roleId": f"{role_id}", "subject": {"id": f"{sa_id}", "type": "serviceAccount"}}}]}
  225. response = requests.post('https://lockbox.api.cloud.yandex.net/lockbox/v1/secrets/'+secret_id+':updateAccessBindings',
  226. data=json.dumps(request_json_data), headers={"Accept": "application/json", "Authorization": "Bearer "+token})
  227. print("START DEBUG--------------------------")
  228. print(response)
  229. print(request_json_data)
  230. print(token)
  231. print(response.request.url)
  232. print(response.request.body)
  233. print(response.request.headers)
  234. return response
  235. print("STOP DEBUG----------------")
  236. # -----------------------------
  237. # Отладочная загрузка файла json руками, в случае вызова cloud-functions json файл сам передается в handler
  238. '''
  239. with open("test.json", "r") as read_file:
  240. data = json.load(read_file)
  241. handler(data, "d")
  242. '''