From 948a38e1efc406bb2e5134337fe13790318f5184 Mon Sep 17 00:00:00 2001 From: Hulto <7121375+hulto@users.noreply.github.com> Date: Sun, 4 Feb 2024 12:47:17 -0500 Subject: [PATCH] Add python script to auto generate tags. (#545) --- bin/ccdc-team-mappings/create_tags.py | 154 ++++++++++++++++++++++ bin/ccdc-team-mappings/neccdc_mapping.py | 158 +++++++++++++++++++++++ bin/ccdc-team-mappings/requirements.txt | 1 + 3 files changed, 313 insertions(+) create mode 100644 bin/ccdc-team-mappings/create_tags.py create mode 100644 bin/ccdc-team-mappings/neccdc_mapping.py create mode 100644 bin/ccdc-team-mappings/requirements.txt diff --git a/bin/ccdc-team-mappings/create_tags.py b/bin/ccdc-team-mappings/create_tags.py new file mode 100644 index 00000000..5e86c7e7 --- /dev/null +++ b/bin/ccdc-team-mappings/create_tags.py @@ -0,0 +1,154 @@ +import argparse +import os +import re +import requests +from pprint import pprint +import json +from neccdc_mapping import tag_profiles +from dataclasses import dataclass + + +@dataclass +class TagBuilder: + graphql_url: str + auth_session: str + auth_session: str + + + def make_graphql_request(self, query, variables): + headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } + + data = {"query": query, "variables": variables} + cookies = { + "auth-session": self.auth_session, + } + + response = requests.post(self.graphql_url, json=data, headers=headers, cookies=cookies) + if response.status_code == 200: + return response.json() + else: + print(f"Error {response.status_code}: {response.text}") + return None + + def get_hosts(self): + graphql_query = """ +query getHosts($where:HostWhereInput){ + hosts(where:$where) { + id + primaryIP + name + } +} """ + + graphql_variables = {"where":{}} + res = self.make_graphql_request(graphql_query, graphql_variables) + if 'errors' in res: + return -1 + else: + return res + + def get_tag(self, tag_name): + graphql_query = """ +query getTag($input:TagWhereInput){ + tags(where:$input) { + id + } +} """ + + graphql_variables = {"input":{"name":tag_name}} + res = self.make_graphql_request(graphql_query, graphql_variables) + if 'errors' in res: + return -1 + else: + return res['data']['tags'][0]['id'] + + def create_tag(self, tag_name: str, tag_kind: str): + print(f"{tag_name}:{tag_kind}") + res = self.get_tag(tag_name) + if res != -1: + return res + + graphql_query = """ +mutation createTag($input:CreateTagInput!){ + createTag(input:$input) { + id + } +} + """ + + graphql_variables = {"input":{"name":tag_name, "kind":tag_kind}} + res = self.make_graphql_request(graphql_query, graphql_variables) + if 'errors' in res: + pprint(res) + return -1 + else: + return res['data']['createTag']['id'] + + def add_hosts(self, tag_id: str, hosts: list): + graphql_query = """ +mutation updateTag($input:UpdateTagInput!, $tagid:ID!){ + updateTag(input:$input, tagID:$tagid) { + id + } +} """ + print(hosts) + graphql_variables = {"input":{"addHostIDs":hosts},"tagid":tag_id} + res = self.make_graphql_request(graphql_query, graphql_variables) + if 'errors' in res: + pprint(res) + return -1 + else: + return res['data']['updateTag']['id'] + + def run(self): + service_map = { } + data = self.get_hosts() + + for tag_profile in tag_profiles: + tag_id = self.create_tag( + tag_profile['name'], + tag_profile['kind'] + ) + service_map[tag_profile['name']] = { + "hosts":[], + "id": tag_id, + } + + for row in data["data"]["hosts"]: + for tag_profile in tag_profiles: + re_match = None + if 'ip_regex' in tag_profile: + re_match = re.search(tag_profile["ip_regex"], row["primaryIP"]) + if 'hostname_regex' in tag_profile: + re_match = re.search(tag_profile["hostname_regex"], row["name"]) + if re_match is not None: + self.add_hosts( + service_map[tag_profile['name']]['id'], + [row["id"]] + ) + service_map[tag_profile['name']]['hosts'].append(row["id"]) + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + prog="CCDC Tag generator", + description="Based on a list of regex filters create tags", + ) + + parser.add_argument("tavern_url") + + args = parser.parse_args() + + auth_session = os.environ.get("TAVERN_AUTH_SESSION") + + if auth_session is None: + print( + "No auth-session cookie found. Please set it using the environment variable TAVERN_AUTH_SESSION" + ) + exit(1) + + graphql_url = f"{args.tavern_url}/graphql" + tagger = TagBuilder(graphql_url, auth_session) + tagger.run() \ No newline at end of file diff --git a/bin/ccdc-team-mappings/neccdc_mapping.py b/bin/ccdc-team-mappings/neccdc_mapping.py new file mode 100644 index 00000000..59ca434a --- /dev/null +++ b/bin/ccdc-team-mappings/neccdc_mapping.py @@ -0,0 +1,158 @@ +""" +Profiles can match on IP or hostname. +It's not recommended to use both in the same profile but it will do an +inclusive or if the host matches either. +""" + +tag_profiles = [ + { + "name": "idm", + "kind": "service", + "ip_regex": "^10\\.0\\.[0-9]{1,2}\\.132$", + }, + { + "name": "controller", + "kind": "service", + "hostname_regex": "^controller$", + }, + { + "name": "wazuh", + "kind": "service", + "ip_regex": "^10\\.0\\.[0-9]{1,2}\\.180$", + }, + { + "name": "kube_01", + "kind": "service", + "ip_regex": "^10\\.0\\.[0-9]{1,2}\\.200$", + }, + { + "name": "kube_02", + "kind": "service", + "ip_regex": "^10\\.0\\.[0-9]{1,2}\\.210$", + }, + { + "name": "kube_03", + "kind": "service", + "ip_regex": "^10\\.0\\.[0-9]{1,2}\\.220$", + }, + { + "name": "dc_01", + "kind": "service", + "ip_regex": "^10\\.0\\.[0-9]{1,2}\\.16$", + }, + { + "name": "ca", + "kind": "service", + "ip_regex": "^10\\.0\\.[0-9]{1,2}\\.32$", + }, + { + "name": "win_01", + "kind": "service", + "host_regex": "^win-01$", + }, + { + "name": "win_02", + "kind": "service", + "ip_regex": "^10\\.0\\.[0-9]{1,2}\\.110$", + }, + { + "name": "team01", + "kind": "group", + "ip_regex": "^10\\.0\\.1\\.[0-9]{1,3}$" + }, + { + "name": "team02", + "kind": "group", + "ip_regex": "^10\\.0\\.2\\.[0-9]{1,3}$" + }, + { + "name": "team03", + "kind": "group", + "ip_regex": "^10\\.0\\.3\\.[0-9]{1,3}$" + }, + { + "name": "team04", + "kind": "group", + "ip_regex": "^10\\.0\\.4\\.[0-9]{1,3}$" + }, + { + "name": "team05", + "kind": "group", + "ip_regex": "^10\\.0\\.5\\.[0-9]{1,3}$" + }, + { + "name": "team06", + "kind": "group", + "ip_regex": "^10\\.0\\.6\\.[0-9]{1,3}$" + }, + { + "name": "team07", + "kind": "group", + "ip_regex": "^10\\.0\\.7\\.[0-9]{1,3}$" + }, + { + "name": "team08", + "kind": "group", + "ip_regex": "^10\\.0\\.8\\.[0-9]{1,3}$" + }, + { + "name": "team09", + "kind": "group", + "ip_regex": "^10\\.0\\.9\\.[0-9]{1,3}$" + }, + { + "name": "team10", + "kind": "group", + "ip_regex": "^10\\.0\\.10\\.[0-9]{1,3}$" + }, + { + "name": "team11", + "kind": "group", + "ip_regex": "^10\\.0\\.11\\.[0-9]{1,3}$" + }, + { + "name": "team12", + "kind": "group", + "ip_regex": "^10\\.0\\.12\\.[0-9]{1,3}$" + }, + { + "name": "team13", + "kind": "group", + "ip_regex": "^10\\.0\\.13\\.[0-9]{1,3}$" + }, + { + "name": "team14", + "kind": "group", + "ip_regex": "^10\\.0\\.14\\.[0-9]{1,3}$" + }, + { + "name": "team15", + "kind": "group", + "ip_regex": "^10\\.0\\.15\\.[0-9]{1,3}$" + }, + { + "name": "team16", + "kind": "group", + "ip_regex": "^10\\.0\\.16\\.[0-9]{1,3}$" + }, + { + "name": "team17", + "kind": "group", + "ip_regex": "^10\\.0\\.17\\.[0-9]{1,3}$" + }, + { + "name": "team18", + "kind": "group", + "ip_regex": "^10\\.0\\.18\\.[0-9]{1,3}$" + }, + { + "name": "team19", + "kind": "group", + "ip_regex": "^10\\.0\\.19\\.[0-9]{1,3}$" + }, + { + "name": "team20", + "kind": "group", + "ip_regex": "^10\\.0\\.20\\.[0-9]{1,3}$" + }, +] diff --git a/bin/ccdc-team-mappings/requirements.txt b/bin/ccdc-team-mappings/requirements.txt new file mode 100644 index 00000000..077c95d8 --- /dev/null +++ b/bin/ccdc-team-mappings/requirements.txt @@ -0,0 +1 @@ +requests==2.31.0 \ No newline at end of file