diff --git a/files/greenbone_check.py b/files/greenbone_check.py new file mode 100644 index 0000000000000000000000000000000000000000..053146e0bfb5f405754124757e842cd4b81af752 --- /dev/null +++ b/files/greenbone_check.py @@ -0,0 +1,471 @@ +from gvm.connections import TLSConnection +from gvm.protocols.gmpv208 import Gmp, AliveTest +from gvm.transforms import EtreeTransform +from gvm.xml import pretty_print +from time import time, sleep +import logging +import json +import base64 +from sys import argv, exit +import socket +import os + +def get_version_old(): + with Gmp(connection, transform=transform) as gmp: + gmp.authenticate(auth_name, auth_passwd) + pretty_print(gmp.get_version()) + +def create_connection(): + connection_retries = 5 + retry = connection_retries + while(retry > 0): + try: + gmp = Gmp(connection, transform=transform) + gmp.authenticate(auth_name, auth_passwd) + return gmp + except: + print("WARNING: Connection error with the gmp endpoint. Remaining {retry} retries") + retry -= 1 + sleep(0.5) + raise Exception("Impossible connect to the gmp endpoint even after 5 retries") + +def get_version(): + gmp = create_connection() + res = gmp.get_version() + pretty_print(res) + +########## PORT LIST ################################## + +def create_port_list(port_list_name, ports): + gmp = create_connection() + res = gmp.create_port_list(port_list_name, ','.join(ports)) + status = res.xpath('@status')[0] + status_text = res.xpath('@status_text')[0] + if status == "201": + id = res.xpath('@id')[0] + return {'name': port_list_name, 'id': id} + else: + msg = f"ERROR during Port list creation. Status code: {status}, msg: {status_text}" + raise Exception(msg) + +def get_port_lists(filter_str="rows=-1"): + l_o = [] + gmp = create_connection() + res = gmp.get_port_lists(filter_string=filter_str) + for pl in res.xpath('port_list'): + o = dict() + o['name'] = pl.xpath('name/text()')[0] + o['id'] = pl.xpath('@id')[0] + o['in_use'] = pl.xpath('in_use/text()')[0] + l_o.append(o) + return l_o + +def delete_port_list(port_list): + gmp = create_connection() + res = gmp.delete_portlist(port_list['id']) + status = res.xpath('@status')[0] + status_text = res.xpath('@status_text')[0] + if status == "200": + print(f"Port_list with id: {port_list['id']} and name: {port_list['name']} DELETED") + else: + print(f"ERROR {status}: {status_text}") + +def get_or_create_port_list(port_list_name, ports): + res = get_port_lists(port_list_name) + if len(res) == 0: + port_list = create_port_list(port_list_name, ports) + return get_port_lists(port_list['id'])[0] + elif len(res) == 1: + return res[0] + else: + print(f"WARNING Found {len(res)} results.") + return res + +############## TARGET ################################## + +def create_target(name,ip,port_list,ovs_ssh_credential): + o = dict() + gmp = create_connection() + res = gmp.create_target( + name=name, + comment = "", + hosts=[ip], + port_list_id = port_list['id'], + ssh_credential_id = ovs_ssh_credential['id'], + alive_test=AliveTest('Consider Alive')) + status = res.xpath('@status')[0] + status_text = res.xpath('@status_text')[0] + if status == "201": + id = res.xpath('@id')[0] + return {'name': name, 'id': id} + else: + msg = f"ERROR during Target creation. Status code: {status}, msg: {status_text}" + raise Exception(msg) + +def get_targets(filter_str): + res = [] + gmp = create_connection() + targets = gmp.get_targets(filter_string=filter_str) + for target in targets.xpath('target'): + o = dict() + o['name'] = target.xpath('name/text()')[0] + o['hosts'] = target.xpath('hosts/text()')[0] + o['id'] = target.xpath('@id')[0] + o['in_use'] = target.xpath('in_use/text()')[0] + res.append(o) + return res + +def delete_target(target): + gmp = create_connection() + res = gmp.delete_target(target['id']) + status = res.xpath('@status')[0] + status_text = res.xpath('@status_text')[0] + if status == "200": + print(f"Port_list with id: {target['id']} and name: {target['name']} DELETED") + else: + print(f"ERROR {status}: {status_text}") + +def get_or_create_target(target_name,ip,port_list,ovs_ssh_credential): + res = get_targets(target_name) + if len(res) == 0: + t = create_target(target_name,ip,port_list,ovs_ssh_credential) + return get_targets(t['id'])[0] + elif len(res) == 1: + return res[0] + else: + print(f"Found {len(res)} results. Return None") + return res + +def search_and_delete_target(target_name): + targets = get_targets(target_name) + if len(targets) == 1: + delete_target(targets[0]['id']) + else: + raise("Multiple results for search") + +def search_and_delete_all_targets(target_name): + targets = get_targets(target_name) + for target in targets: + delete_target(target) + +############## TASK ################################## + +def create_task(name, config, target, scanner): + o = dict() + gmp = create_connection() + res = gmp.create_task( + name=name, + config_id=config['id'], + target_id=target['id'], + scanner_id=scanner['id']) + status = res.xpath('@status')[0] + status_text = res.xpath('@status_text')[0] + if status == "201": + id = res.xpath('@id')[0] + return {'name': name, 'id': id} + else: + msg = f"ERROR during Task creation. Status code: {status}, msg: {status_text}" + raise Exception(msg) + +def get_tasks(filter_str): + res = [] + gmp = create_connection() + tasks = gmp.get_tasks(filter_string=filter_str) + for task in tasks.xpath('task'): + o = dict() + o['name'] = task.xpath('name/text()')[0] + o['id'] = task.xpath('@id')[0] + o['progress'] = task.xpath('progress/text()')[0] + o['in_use'] = task.xpath('in_use/text()')[0] + o['status'] = task.xpath('status/text()')[0] + o['target_id'] = task.xpath('target/@id')[0] + try: + o['report_id'] = task.xpath('last_report/report/@id')[0] + except: + pass + res.append(o) + return res + +def get_or_create_task(task_name, config, target, scanner): + res = get_tasks(task_name) + if len(res) == 0: + t = create_task(task_name, config, target, scanner) + return get_tasks(t['id'])[0] + elif len(res) == 1: + return res[0] + else: + print(f"Found {len(res)} results. Return None") + return res + +def get_all_tasks(): + res = [] + gmp = create_connection() + tasks = gmp.get_tasks(filter_string="rows=-1") + for task in tasks.xpath('task'): + o = dict() + o['name'] = task.xpath('name/text()')[0] + o['id'] = task.xpath('@id')[0] + o['progress'] = task.xpath('progress/text()')[0] + o['in_use'] = task.xpath('in_use/text()')[0] + o['status'] = task.xpath('status/text()')[0] + o['target_id'] = task.xpath('target/@id')[0] + try: + o['report_id'] = task.xpath('last_report/report/@id')[0] + except: + pass + res.append(o) + return res + +def search_and_delete_all_tasks(filter_str): + tasks = get_tasks(filter_str) + for task in tasks: + delete_task(task) + +def start_task(task): + gmp = create_connection() + res = gmp.start_task(task['id']) + task['report_id'] = res.xpath('report_id/text()')[0] + return task + +def stop_task(task): + gmp = create_connection() + res = gmp.stop_task(task['id']) + pretty_print(res) + +def delete_task(task): + gmp = create_connection() + res = gmp.delete_task(task['id']) + status = res.xpath('@status')[0] + status_text = res.xpath('@status_text')[0] + if status == "200": + print(f"Target with id: {task['id']} and name: {task['name']} DELETED") + else: + print(f"ERROR {status}: {status_text}") + +############## REPORTS #####################################3 + +class report_formats: + anonymous_xml = "5057e5cc-b825-11e4-9d0e-28d24461215b" + csv_results = "c1645568-627a-11e3-a660-406186ea4fc5" + itg = "77bd6c4a-1f62-11e1-abf0-406186ea4fc5" + pdf = "c402cc3e-b531-11e1-9163-406186ea4fc5" + txt = "a3810a62-1f62-11e1-9219-406186ea4fc5" + xml = "a994b278-1f62-11e1-96ac-406186ea4fc5" + +def get_report_formats(): + gmp = create_connection() + res = gmp.get_report_formats() + for f in res.xpath('report_format'): + name = f.xpath('name/text()')[0] + id = f.xpath('@id')[0] + print(id,name) + +def get_report_format(id): + gmp = create_connection() + res = gmp.get_report_formats() + pretty_print(res) + +def get_progress(task): + task_info = get_tasks(task['id'])[0] + status = task_info['status'] # New -> Requested -> Queued -> Running -> Done + progress = int(task_info['progress'])# 0 0 0 0 -> 100 -1 + return status, progress + +def wait_for_task_completition(task, timeout=3600): + start_time = time() + while True: + status, progress = get_progress(task) + if status not in ["New","Requested","Queued","Running","Done"]: # ["Interrupted", ...] + return False + if status == "Done" and progress == -1: + return True + if time() - start_time > timeout: + print("TIMEOUT during waiting for task completition") + return False + sleep(60) + +def save_report(task,report_format_id, report_filename ): + gmp = create_connection() + res = gmp.get_report(task['report_id'], + report_format_id=report_format_id, + ignore_pagination=True, + details="1") + code = str(res.xpath('report/text()')[0]) + with open(report_filename, "wb") as fh: + fh.write(base64.b64decode(code)) + +def save_severity_report(task, severity_filename): + dict_severity = {"Log": 0, "Low": 1, "Medium": 2, "High": 3} + gmp = create_connection() + res = gmp.get_report(task['report_id'], + report_format_id=report_formats.anonymous_xml, + ignore_pagination=True, + details="1") + severities = res.xpath('report/report/ports/port/threat/text()') + old_num_severity = 0 + severity = "Log" + for sev in severities: + if dict_severity[sev] > old_num_severity: + old_num_severity = dict_severity[sev] + severity = sev + with open(severity_filename, "w") as f: + f.write(severity) + +def get_severity(task): + dict_severity = {"Log": 0, "Low": 1, "Medium": 2, "High": 3} + gmp = create_connection() + res = gmp.get_report(task['report_id'], + report_format_id=report_formats.anonymous_xml, + ignore_pagination=True, + details="1") + severities = res.xpath('report/report/ports/port/threat/text()') + old_num_severity = 0 + severity = "Log" + for sev in severities: + if dict_severity[sev] > old_num_severity: + old_num_severity = dict_severity[sev] + severity = sev + return severity + +def get_reports(filter_str="rows=-1"): + lo = [] + gmp = create_connection() + reports = gmp.get_reports(filter_string = filter_str) + for report in reports.xpath('report'): + o = dict() + o['task_name'] = report.xpath('task/name/text()')[0] + o['id'] = report.xpath('@id')[0] + lo.append(o) + return lo + +def get_numeric_severity(severity): + if severity == "Log": + return 0 + elif severity == "Low": + return 1 + elif severity == "Medium": + return 2 + elif severity == "High": + return 3 + else: + return 4 + +def get_severity_from_number(num): + if num == 0: + return "Low" + elif num == 1: + return "Low" + elif num == 2: + return "Medium" + elif num == 3: + return "High" + else: + return "Undefined" + +def process_global_severity(severities): + max_num_severity = 0 + for _,sev in severities.items(): + numeric_severity = get_numeric_severity(sev) + if numeric_severity > max_num_severity: + max_num_severity = numeric_severity + severities['global_severity'] = get_severity_from_number(max_num_severity) + if max_num_severity < 2: + severities['global'] = "OK" + else: + severities['global'] = "NOK" + return severities + +def print_pretty_json(j): + print(json.dumps(j,sort_keys=True,indent=4)) + +def import_dep_info(file_path, endpoints_to_scan): + with open(file_path) as f: + data = json.load(f) + + endpoints = dict() + for key in data['outputs'].keys(): + if key in endpoints_to_scan: + endpoint = str(data['outputs'][key]) + prefix,url = endpoint.split("://") + if ":" in url: + host,port = url.split(":") + else: + host = url + if prefix == "https": + port = '443' + elif prefix == 'http': + port = '80' + else: + raise Exception(f"Impossible to parse the endpoint port. Endpoint: {endpoint}") + print(f"Endpoint: {host}:{port}") + if host not in endpoints: + endpoints[host] = {"22"} + endpoints[host].add(port) + return endpoints + +################ MAIN ####################################### + +auth_name = os.getenv('GMP_USER') +auth_passwd = os.getenv('GMP_PASSWORD') +print(auth_name, auth_passwd) +logging.basicConfig(filename='debug.log', level=logging.DEBUG) +#local_ip = socket.gethostbyname(socket.gethostname()) +#local_ip = '127.0.0.1' +local_ip = os.getenv('HOST_IP') +connection = TLSConnection(hostname=local_ip) +transform = EtreeTransform() +config = {'id':"9866edc1-8869-4e80-acac-d15d5647b4d9"} +scanner = {'id': "08b69003-5fc2-4037-a479-93b440211c73"} +ovs_ssh_credential = {'id': "b9af5845-8b87-4378-bca4-cee39a894c17"} +wait_timeout = 3600 #1h + +if len(argv) != 4: + print("Please pass three parameters:") + print("- endpoints to scans [endpoints1,endpoint2,endpoint3,...]") + print("- dep.json path [/home/gmp/workspace/dep.json]") + print("- output directory [/home/gmp/workspace]") + exit(1) + +endpoints_to_scan = argv[1].split(',') +dep_json = argv[2] +output_dir = argv[3] +print("endpoints_to_scan", endpoints_to_scan) +print("dep_json", dep_json) +print("output_dir", output_dir) + +endpoints = import_dep_info(dep_json, endpoints_to_scan) +#print_pretty_json(endpoints) + +# test gmp connection +get_version() + +severities = dict() +for host,ports in endpoints.items(): + print(host,ports) + + target_name = f"{auth_name}_target_{host}" + task_name = f"{auth_name}_task_{host}" + port_list_name = f"{auth_name}_pl_{host}" + report_filename = f"{output_dir}/{host}-report.txt" + severity_filename = f"{output_dir}/severity.json" + + port_list = get_or_create_port_list(port_list_name,ports) + print_pretty_json(port_list) + target = get_or_create_target(target_name,host,port_list,ovs_ssh_credential) + print_pretty_json(target) + task = get_or_create_task(task_name, config, target,scanner) + print_pretty_json(task) + if task['status'] == 'New': + task = start_task(task) + if wait_for_task_completition(task,wait_timeout): + save_report(task,report_formats.txt, report_filename) + severities[host] = get_severity(task) + else: + severities[host] = f"ERROR Task: {task['id']}" + delete_task(task) + delete_target(target) + delete_port_list(port_list) + +severities = process_global_severity(severities) +with open(severity_filename, "w") as f: + f.write(json.dumps(severities)) diff --git a/jenkinsfile/Jenkinsfile b/jenkinsfile/Jenkinsfile index 96f7596636f8644ffa645044aecd6bf78a6dbd08..b0cfd5707a5728cba38e30da712361e53a0e21a5 100644 --- a/jenkinsfile/Jenkinsfile +++ b/jenkinsfile/Jenkinsfile @@ -13,8 +13,6 @@ pipeline { eval `oidc-agent-service use` oidc-add infn-cloud-ops - env - # Orchent connection test orchent depls > depls.output if cat depls.output | grep -q ERROR