def build_scan_object(self): session = cloudpassage.HaloSession(key_id, secret_key, api_host=api_hostname, api_port=api_port, integration_string="SDK-Smoke") return_obj = cloudpassage.Scan(session) return(return_obj)
def scan_all_modules(self, agent_id): scan_types = ["csm", "svm"] command_ids = [] unfinished_statuses = ['queued', 'pending'] server_module = cloudpassage.Server(self.halo_session) scan_module = cloudpassage.Scan(self.halo_session) raw_scan_results = [] # Initiate scans print "Initiating scans for agent %s" % agent_id for scan_type in scan_types: command_id = scan_module.initiate_scan(agent_id, scan_type)["id"] command_ids.append(command_id) print command_id # Wait until all are complete print "Waiting for all scan jobs to be run" while len(command_ids) > 0: time.sleep(30) for command_id in command_ids: print "Checking status of command %s" % command_id status = server_module.command_details(agent_id, command_id) if status not in unfinished_statuses: command_ids.remove(command_id) # Get results print "Getting scan results" for scan_type in scan_types: try: results = scan_module.last_scan_results(agent_id, scan_type) except CloudPassageValidation as e: message = "Error encountered: %s" % str(e) result = {"result": message} raw_scan_results.append(results) # Process and print scan results pretty = self.print_pretty_scans(raw_scan_results) return
def test_verify_and_build_status_params(self): bad_list = ["cats"] bad_string = "cats" good_list = ["running"] good_string = "running" bad_statuses = [bad_list, bad_string] good_statuses = [good_list, good_string] scanner = cloudpassage.Scan(None) for status in bad_statuses: try: accepted = scanner.verify_and_build_status_params(status) except cloudpassage.CloudPassageValidation: accepted = False assert accepted is False for status in good_statuses: accepted = scanner.verify_and_build_status_params(status) assert accepted == status def test_verify_and_build_module_params(self): bad_list = ["cats"] bad_string = "cats" good_list = ["sam"] good_string = "sam" bad_modules = [bad_list, bad_string] good_modules = [good_list, good_string] scanner = cloudpassage.Scan(None) for status in bad_modules: try: accepted = scanner.verify_and_build_module_params(status) except cloudpassage.CloudPassageValidation: accepted = False assert accepted is False for status in good_modules: accepted = scanner.verify_and_build_module_params(status) assert accepted == status
def hold_for_completion(self, scan_body): """Wait for completion and return completed scan. This function checks the status from scan_body and if the status is queued, pending, or running, we wait and re-query until the status indicates completion. We don't wait more than 6 minutes for a scan to complete, though. Args: scan_body(dict): Body of scan from API. """ wait_time = 10 scan = cloudpassage.Scan(self.halo_session) # time_waited = 0 while scan_body["status"] in ["queued", "pending", "running"]: # if time_waited >= self.scan_timeout: t_delta = Utility.iso_8601_delta(Utility.iso8601_now(), scan_body["created_at"]) if abs(t_delta.seconds) > self.scan_timeout: print("Not waiting on scan with ID %s anymore...(%s seconds)" % (scan_body["id"], abs(t_delta.seconds))) break time.sleep(wait_time) # time_waited += wait_time scan_body = scan.scan_details(scan_body["id"]) return scan_body
def test_scan_type_valid(self): valid_types = ["svm", "sva", "csm", "sca", "fim", "sam", "sv"] invalid_types = ["death_stare", "lids"] scanner = cloudpassage.Scan(None) for v in valid_types: assert scanner.scan_type_supported(v) for i in invalid_types: assert not scanner.scan_type_supported(i)
def main(): # Set up config and Halo objects. config = cloudpassage.ApiKeyManager() server_id = os.getenv("SERVER_ID") crit_threshold = int(os.getenv("CRITICAL_THRESHOLD")) non_crit_threshold = int(os.getenv("NON_CRITICAL_THRESHOLD")) halo_session = cloudpassage.HaloSession(config.key_id, config.secret_key, api_host=config.api_hostname) server_obj = cloudpassage.Server(halo_session) scan_obj = cloudpassage.Scan(halo_session) # Initiate a scan of the target server print("Initiating scan...") job_id = scan_obj.initiate_scan(server_id, 'sca')["id"] incomplete_statuses = ["queued", "pending", "running"] scan_status = "queued" while scan_status in incomplete_statuses: time.sleep(20) print("Waiting for scan %s on server %s to finish (status %s)" % (job_id, server_id, scan_status)) # NOQA scan_status = server_obj.command_details(server_id, job_id)["status"] scan_results = scan_obj.last_scan_results(server_id, 'sca')["scan"] over_threshold = False threshold_msg = "" crit_findings = scan_results["critical_findings_count"] non_crit_findings = scan_results["non_critical_findings_count"] # Test criticality against threshold if crit_findings > crit_threshold: threshold_msg += "Critical findings: %s Threshold: %s\n" % ( crit_findings, crit_threshold) # NOQA if crit_threshold != -1: over_threshold = True else: threshold_msg += "Critical threshold set to -1, not failing.\n" if non_crit_findings > non_crit_threshold: threshold_msg += "Non-critical findings: %s Threshold: %s\n" % ( non_crit_findings, non_crit_threshold) # NOQA if non_crit_threshold != -1: over_threshold = True else: threshold_msg += "Non-critical threshold set to -1, not failing.\n" # Build findings text failed_items = "" bad_findings = [ f for f in scan_results["findings"] if f["status"] == 'bad' ] # NOQA for finding in bad_findings: msg = "Critical: %s Rule: %s\n" % (str( finding["critical"]), finding["rule_name"]) failed_items += msg output_msg = "\n---\n".join([failed_items, threshold_msg]) print(output_msg) # Exit with 1 if we are over threshold. if over_threshold: print("Threshold exceeded, this job will be marked Failed") sys.exit(1) else: print("Nothing exceeds thresholds; this job will be marked Passed.")
def __init__(self): self.key = os.getenv("HALO_API_KEY") self.secret = os.getenv("HALO_API_SECRET_KEY") self.session = cloudpassage.HaloSession(self.key, self.secret) # We go ahead and grab an auth token for all abstractions to use. self.session.authenticate_client() self.servers = cloudpassage.Server(self.session) self.scans = cloudpassage.Scan(self.session) return
def get(self, scan_id): """This wraps other functions that get specific scan details""" scan = cloudpassage.Scan(self.halo_session) details = scan.scan_details(scan_id) details = self.hold_for_completion(details) if details["module"] == "fim": new_deets = self.enrich_fim(details) details["findings"] = None details["findings"] = new_deets return details
def test_bad_server_id(self): session = cloudpassage.HaloSession(key_id, secret_key, api_host=api_hostname, api_port=api_port) scanner = cloudpassage.Scan(session) scan_type = "svm" server_id = "ABC123" with pytest.raises(cloudpassage.CloudPassageResourceExistence) as e: scanner.initiate_scan(server_id, scan_type) assert server_id in str(e)
def test_bad_scan_type(self): session = cloudpassage.HaloSession(key_id, secret_key, api_host=api_hostname, api_port=api_port) scanner = cloudpassage.Scan(session) s_group = cloudpassage.ServerGroup(session) scan_type = "barfola" server_id = s_group.list_all()[0]["id"] with pytest.raises(cloudpassage.CloudPassageValidation) as e: scanner.initiate_scan(server_id, scan_type) assert 'Unsupported scan type: barfola' in str(e)
def test_scan_type_valid(self): valid_types = ["svm", "sva", "csm", "sca", "fim", "sam", "sv"] invalid_types = ["death_stare", "lids"] session = cloudpassage.HaloSession(key_id, secret_key, api_host=api_hostname, api_port=api_port) scanner = cloudpassage.Scan(session) for v in valid_types: assert scanner.scan_type_supported(v) for i in invalid_types: assert not scanner.scan_type_supported(i)
def main(): global FAIL_EXIT_CODE FAIL_EXIT_CODE = int(os.getenv("FAIL_EXIT_CODE", 2)) start_time = datetime.datetime.now() copy_tree("static", "reports/html/static") Path("reports/html/cve").mkdir(parents=True, exist_ok=True) key = os.getenv("HALO_API_KEY") secret = os.getenv("HALO_API_SECRET_KEY") module_str = os.getenv("SCAN_MODULE") modules = module_str.split(",") instance_id = os.getenv("INSTANCE_ID") timeout = os.getenv("TIMEOUT") max_cvss_threshold = float(os.getenv("MAX_CVSS", 7)) modules = [module.lower() for module in modules] modules = list(set(modules)) session = cloudpassage.HaloSession(key, secret) validate_input(session, modules, instance_id, max_cvss_threshold) server = cloudpassage.Server(session) scan = cloudpassage.Scan(session) group = cloudpassage.ServerGroup(session) scanned_server = get_server(server, instance_id, start_time, timeout) server_id = scanned_server["id"] print(f"Server ID: {server_id}") modules = validate_csm(modules, scanned_server, group) scans = {} for module in modules: while True: try: scan_output = scan.last_scan_results(server_id, module) except cloudpassage.CloudPassageResourceExistence: check_timeout(start_time, timeout) print("Waiting on {} scan to complete...".format(module)) time.sleep(30) if scan_output and "scan" in scan_output: scans[module] = scan_output["scan"] break check_timeout(start_time, timeout) print("Waiting on {} scan to complete...".format(module)) time.sleep(30) build_success = process_scan(scans, scanned_server, session) if not build_success: print("Security scan results did not meet pass criteria") sys.exit(FAIL_EXIT_CODE)
def test_sam_historical_is_unsupported(self): rejected = False session = cloudpassage.HaloSession(key_id, secret_key, api_host=api_hostname, api_port=api_port) scanner = cloudpassage.Scan(session) server = cloudpassage.Server(session) scan_type = "sam" server_id = server.list_all()[0]["id"] try: scanner.last_scan_results(server_id, scan_type) except cloudpassage.CloudPassageValidation: rejected = True assert rejected
def process_csm(csm_scan, server, session, tests): scan = cloudpassage.Scan(session) policy = cloudpassage.ConfigurationPolicy(session) scan_details = scan.scan_details(csm_scan["id"]) csm_policy_list = scan_details["policies"] csm_policies = [policy.describe(item["id"]) for item in csm_policy_list] rules_dict = {} for csm_policy in csm_policies: for rule in csm_policy["rules"]: rules_dict[rule["name"]] = rule tests["max_criticals"]["actual"] = csm_scan["critical_findings_count"] tests["max_non_criticals"]["actual"] = csm_scan["non_critical_findings_count"] if tests["max_criticals"]["actual"] > tests["max_criticals"]["threshold"]: tests["max_criticals"]["result"] = False if tests["max_non_criticals"]["actual"] > tests["max_non_criticals"]["threshold"]: tests["max_non_criticals"]["result"] = False bad_findings = [finding for finding in csm_scan["findings"] if finding['status'] == ('bad' or 'indeterminate')] scan_time = csm_scan["completed_at"] generate_csm_report(tests, bad_findings, server, scan_time, rules_dict, csm_policies) return all(v["result"] for v in tests.values())
def test_instantiation(self): session = cloudpassage.HaloSession(key_id, secret_key, api_host=api_hostname, api_port=api_port) assert cloudpassage.Scan(session)
def get_scan_data(session): mode = "w" report_directory = "reports/" report_name = "CSM_report_all_events_" report_extension = ".csv" if not os.path.exists(report_directory): os.makedirs(report_directory) out_file = "%s%s" % (report_directory, report_name) \ + time.strftime("%Y%m%d-%H%M%S") + report_extension ofile = open(out_file, mode) halo_server_list = get_halo_servers_id(session) ofile.write('AWS Account ID, Halo Server ID, AWS Instance ID,' 'CSM Rule Name, ' 'Rule Description, Expected Target Value for Check, Actual ' 'Value\n') server_count = 1 increment = 1 scan_type = "sca" total_servers = len(halo_server_list) for server in halo_server_list: print "Processing {0} of {1} servers".format(server_count, total_servers) server_count += increment # get the last csm scan cp_scan_ob = cloudpassage.Scan(session) data = cp_scan_ob.last_scan_results(server['halo_server_id'], scan_type) if 'scan' in data: current_findings = data['scan']['findings'] for finding in current_findings: if finding['status'] == 'bad': # and finding['critical'] is True: finding_details = finding['details'] for details in finding_details: if details['status'] == 'bad': expected_value = details['expected'] # strip commas if var is a str if not isinstance(expected_value, bool): expected_value = str(expected_value) expected_value = \ expected_value.replace(",", " ") # set to space if they key does not exist if 'rule_description' not in finding: rule_description = " " else: # if the key does exist then strip the returns # and newlines and commas rule_description = \ finding['rule_description'] rule_description = \ rule_description.replace(",", " ") rule_description = \ rule_description.replace("\r", "") rule_description = \ rule_description.replace("\n", "") row = "'{0}',{1},{2},{3},{4},{5},{6}\n" \ "".format(server['aws_account_id'], server['halo_server_id'], server['aws_instance_id'], finding['rule_name'], rule_description, expected_value, details['actual']) row = str(row) ofile.write(row) ofile.close()
def test_instantiation(self): assert cloudpassage.Scan(None)
def main(): # Suspect package names suspect_packages = "kubectl,kubeadm,kubelet" # Suspect processes URL procs_url = "/v2/servers?process_name=kubelet,kubeadm,kubectl,kube-proxy" # Minimum versions safe from API auth bug kube_min_ver = { "1.10": "1.10.11", "1.11": "1.11.5", "1.12": "1.12.3", "1.13": "1.13.0-rc.1" } # Get Halo auth info key = os.getenv("HALO_API_KEY") secret = os.getenv("HALO_API_SECRET_KEY") # Set up CloudPassage API abstractions session = cloudpassage.HaloSession(key, secret) servers = cloudpassage.Server(session) scans = cloudpassage.Scan(session) helper = cloudpassage.HttpHelper(session) # Get servers with suspect packages installed servers_installed_kube = [ x for x in servers.list_all(package_name=suspect_packages) ] # Get servers with suspect processes servers_running_kube = [ x for x in helper.get_paginated(procs_url, "servers", 99) ] # Print preliminary metrics print("Installation match: %s" % len(servers_installed_kube)) print("Running process match: %s" % len(servers_running_kube)) # Get a list of all servers with K8s-related procs or packages kube_servers = merge_server_lists(servers_installed_kube, servers_running_kube) # Create a dictionary of packages, processes, and metadata for each server. inventory = { x["id"]: { "packages": get_package_listing_from_scan( scans.last_scan_results(x["id"], "svm")), # NOQA "processes": servers.list_processes(x["id"]), "server_metadata": x } for x in kube_servers } # Print a report to stdout print("{}{}{}{}{}{}{}".format( "Halo ID".ljust(40), "CSP account".ljust(40), "CSP Instance".ljust(40), "kubectl version".ljust(20), "kubeadm version".ljust(20), "kubelet version".ljust(20), "kubectl vulnerable".ljust(25), "kubeadm vulnerable".ljust(25), "kubelet vulnerable".ljust(25), "k8s processes(below)")) for x in inventory.items(): server_id = x[0] server_csp_id = x[1]["server_metadata"][ "csp_account_id"] if "csp_account_id" in x[1][ "server_metadata"] else "" # NOQA server_instance_id = x[1]["server_metadata"][ "csp_instance_id"] if "csp_instance_id" in x[1][ "server_metadata"] else "" # NOQA kubectl_version = get_package_version(x[1]["packages"], "kubectl") kubeadm_version = get_package_version(x[1]["packages"], "kubeadm") kubelet_version = get_package_version(x[1]["packages"], "kubelet") kube_procs = get_kube_procs(x[1]["processes"]) kubectl_vulnerable = is_kube_vulnerable(kube_min_ver, kubectl_version) kubeadm_vulnerable = is_kube_vulnerable(kube_min_ver, kubeadm_version) kubelet_vulnerable = is_kube_vulnerable(kube_min_ver, kubelet_version) print("{}{}{}{}{}{}{}{}{}\n{}\n\n".format(server_id.ljust(40), server_csp_id.ljust(40), server_instance_id.ljust(40), kubectl_version.ljust(16), kubeadm_version.ljust(16), kubelet_version.ljust(16), kubectl_vulnerable.ljust(20), kubeadm_vulnerable.ljust(20), kubelet_vulnerable.ljust(20), kube_procs)) dump_to_csv(inventory, kube_min_ver) dump_to_json(inventory)
def __init__(self, config): session = cloudpassage.HaloSession(config.halo_key, config.halo_secret, api_host=config.halo_url) self.scan = cloudpassage.Scan(session)