diff --git a/Network-Security/Integration/aws-python-amazon-inspector-integration/README.md b/Network-Security/Integration/aws-python-amazon-inspector-integration/README.md new file mode 100644 index 00000000..f9958f59 --- /dev/null +++ b/Network-Security/Integration/aws-python-amazon-inspector-integration/README.md @@ -0,0 +1,48 @@ + +# Amazon Inspector integration with Cloud One Network Security + +This script will deploy a lambda functions that facilitate the integration. + +Click the below to launch the CloudFormation template. + +[![Launch Stack](https://cdn.rawgit.com/buildkite/cloudformation-launch-stack-button-svg/master/launch-stack.svg)](https://console.aws.amazon.com/cloudformation/home#/stacks/new?stackName=c1ns-inspector-findings-protection&templateURL=https://cloudone-community.s3.amazonaws.com/latest/Network-Security/Integration/aws-python-amazon-inspector-integration/c1ns-inspector-findings-template.yaml) + + +Pre-Requisites + +1. You must enable [Amazon Inspector](https://docs.aws.amazon.com/inspector/latest/user/getting_started_tutorial.html) in your AWS account + + +2. You need [to verify the email](https://docs.aws.amazon.com/ses/latest/dg/creating-identities.html) you to send the reports to (both the Intrusion Prevention Filtering report and the updated policy report) in Amazon SES + +3. Generate the [API Key](https://cloudone.trendmicro.com/docs/identity-and-account-management/c1-api-key/) from Cloud One + + +4. For more information about cron expression [Click Here](https://www.designcise.com/web/tutorial/how-to-fix-parameter-scheduleexpression-is-not-valid-serverless-error) + + +## Purpose and Objective + +The goal of this integration is to be able to show the CVEs (findings in Amazon Inspector) that Network Security can protect and make sure we have the appropriate filters updated in the policy to (mitigate the Inspector Findings) enforced this protection. + +## Deployment + +The CloudFormation Template ```c1ns-inspector-findings-template.yaml``` will deploy both lambda functions. One will send out an email which contains a csv file which is a report (the list) of the vulnerabilities ([CVEs](https://www.cve.org/About/Overview)) in the AWS account that Cloud One Network Security is protecting against. The other one will update the policy. A report of the updated policy containing the Intrusion Prevention Filtering will be sent out (the assignment configuration) to a designated email. +Both functions can be set up to run periodically. +Use cron configuration to schedule the run of your lambda functions. The default {cron(0 12 ? * WED *)} is every Wednesday. + +## Stack Parameters + +Fill out the followings when creating your stack: + +- **Stack name**: Specify the name of the stack. The default is ```c1ns-inspector-findings-protection ``` + +- **Sender Email**: The email that is registered in Amazon SES to send out notification. + +- **Reciever Email**: The email where the reports need to be send to. + +- **Action Set**: the recommended ActionSet you would like apply to the Intrusion Prevention Filtering. + +- **Cloud One Region**: Your Cloud One Region. + +- **Cron Schedule**: This will help define the period you will like your lambda functions to be running, the default is every Wednesday. \ No newline at end of file diff --git a/Network-Security/Integration/aws-python-amazon-inspector-integration/python/lambda/FindingsReportLambda.py b/Network-Security/Integration/aws-python-amazon-inspector-integration/python/lambda/FindingsReportLambda.py new file mode 100644 index 00000000..f49d20a7 --- /dev/null +++ b/Network-Security/Integration/aws-python-amazon-inspector-integration/python/lambda/FindingsReportLambda.py @@ -0,0 +1,293 @@ +import os +import sys, warnings +import csv +import json +from typing import List +from pprint import pprint +from tempfile import TemporaryFile +import boto3 +from botocore.exceptions import ClientError +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.mime.application import MIMEApplication +from urllib.request import Request, urlopen +from urllib.parse import urlencode + +API_VERSION = "v1" +AWS_REGION = os.environ.get("awsregion") +SENDER = os.environ.get("sender") +RECIPIENTS = os.environ.get("recipients") +API_KEY = os.environ.get("c1_api") +HOST = f"https://network.{os.environ.get('cloudoneregion')}.cloudone.trendmicro.com/api/policies" + +######################################## Uncomment the following if you running on your local machine +# AWS_REGION = "us-east-1" ##You input the aws region you want +# SENDER = "sender@email.com" +# RECIPIENTS = "recipient@email.com" +# API_KEY = "Your ApiKey" +# HOST = "https://network.us-1.cloudone.trendmicro.com/api/policies/" + +secrets = boto3.client('secretsmanager').get_secret_value(SecretId=API_KEY) +sm_data = json.loads(secrets["SecretString"]) +new_api_format = sm_data["ApiKey"] +HEADERS = {"api-version": API_VERSION, "Authorization": f"ApiKey {new_api_format}"} +## initialize the variables +inspector = boto3.client("inspector2", region_name="us-east-1") +# Create a new SES resource and specify a region. +ses = boto3.client("ses", region_name=AWS_REGION) +sts = boto3.client("sts") + +def get_account_id(): + return sts.get_caller_identity()["Account"] + +def send_email(sender, recipients, subject, html_body, attachment_details): + print("sending email ...") + + msg = MIMEMultipart() + text_part = MIMEText(html_body, _subtype="html") + msg.attach(text_part) + + msg["To"] = recipients + msg["From"] = sender + msg["Subject"] = subject ## + + for item in attachment_details: + filename = item["filename"] + attachment = item["attachment"] + + part = MIMEApplication(attachment.read(), filename) + part.add_header("Content-Disposition", "attachment", filename=filename) + msg.attach(part) + + ses.send_raw_email(RawMessage={"Data": msg.as_bytes()}) + + +def cves_from_instance(instance_id, inspector): + cves = list() + result = list() + # print('instance', instance_id) + resp = inspector.list_findings( + filterCriteria={ + "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}], + "resourceId": [{"comparison": "EQUALS", "value": instance_id}], + } + ) + result.extend(resp["findings"]) + next_token = resp.get("nextToken") + while next_token: + resp = inspector.list_findings( + filterCriteria={ + "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}], + "resourceId": [{"comparison": "EQUALS", "value": instance_id}], + }, + nextToken=next_token, + ) + result.extend(resp["findings"]) + next_token = resp.get("nextToken") + + for finding in result: + cve = finding.get("packageVulnerabilityDetails", {}).get("vulnerabilityId") + if cve: + cves.append(cve) + # print(cve) + return cves + + +def all_instances_details_from_inspector(inspector): + instances = list() + result = list() + findings = inspector.list_finding_aggregations( + aggregationType="AWS_EC2_INSTANCE", + # aggregationRequest='AWS_EC2_INSTANCE', + ) + result.extend(findings["responses"]) + next_token = findings.get("nextToken") + while next_token: + findings = inspector.list_finding_aggregations( + aggregationType="AWS_EC2_INSTANCE", + # aggregationRequest='AWS_EC2_INSTANCE', + nextToken=next_token, + ) + result.extend(findings["responses"]) + next_token = findings.get("nextToken") + + for finding in result: + instance_details = { + "id": finding["ec2InstanceAggregation"]["instanceId"], + "name": finding["ec2InstanceAggregation"] + .get("instanceTags", {}) + .get("Name"), + } + instances.append(instance_details) + print("Found total instances =", len(instances)) + return instances + + +def findings_from_inspector(inspector, all_findings=[], next_token=None): + print("curent inspector findings count = ", len(all_findings)) + params = {} + if next_token: + params = {"nextToken": next_token} + response = inspector.list_findings(**params) + all_findings.extend(response["findings"]) + next_token = response.get("nextToken") + if next_token: + findings_from_inspector(inspector, all_findings, next_token) + print("total inspector findings count = ", len(all_findings)) + return all_findings + + +def filter_findings_with_cves(all_findings): + findings_with_cves = [] + for finding in all_findings: + if finding.get("packageVulnerabilityDetails", {}).get("vulnerabilityId"): + finding_info = { + "title": finding["title"], + "cve": finding["packageVulnerabilityDetails"]["vulnerabilityId"], + } + findings_with_cves.append(finding_info) + print("findings with cve count = ", len(findings_with_cves)) + return findings_with_cves + + +def get_all_policies(all_policies=[], next_token=None, limit=1000): + params = {"limit": limit} + if next_token: + params['cursor'] = next_token + # use the 'headers' parameter to set the HTTP headers: + req = Request(HOST + '?' + urlencode(params)) + for item in HEADERS.items(): + req.add_header(item[0], item[1]) + response = json.loads(urlopen(req).read()) + all_policies.extend(response["policies"]) + + print(f'Fetched {len(all_policies)}/{response["totalCount"]}') + next_token = response.get("next") + if next_token: + get_all_policies(all_policies, next_token) + return all_policies + + +def filter_policies_with_cves(all_policies): + policies_with_cves = [] + for policy in all_policies: + for ref in policy.get("signatureReferences"): + if ref["type"] == "cve": + policy_details = { + "id": policy["id"], + "name": policy["name"], + "severity": policy["severity"], + "cve": ref["value"], + "uuid": policy["uuid"] + } + policies_with_cves.append(policy_details) + print("policies with cve count = ", len(policies_with_cves)) + return policies_with_cves + + +def lambda_handler(event, context): + + # findings = filter_findings_with_cves(findings_from_inspector(inspector)) + policies = filter_policies_with_cves(get_all_policies()) + + csv_rows_unprotected_cves = list() + csv_rows_found_policies = list() + + all_instance_details = all_instances_details_from_inspector(inspector) + for instance_details in all_instance_details: + instance_id = instance_details["id"] + instance_name = instance_details["name"] + cves = cves_from_instance(instance_id, inspector) + for cve in cves: + policy_found = False + for policy in policies: + if policy["cve"] == cve: + print( + f"foung policy for {cve} on instance {instance_name} - {policy}" + ) + print (policy) + csv_rows_found_policies.append( + [instance_id, instance_name, cve, policy["name"]] + ) + policy_found = True + if not policy_found: + csv_rows_unprotected_cves.append([instance_id, instance_name, cve]) + + print(csv_rows_found_policies) + # print(csv_rows_unprotected_cves) + + tmp_csv_file_unprotected_cves = TemporaryFile(mode="w+", newline="") + writer = csv.writer(tmp_csv_file_unprotected_cves) + csv_header_unprotected_cves = ["instance id", "instance name", "cve"] + writer.writerow(csv_header_unprotected_cves) + for row in csv_rows_unprotected_cves: + writer.writerow(row) + tmp_csv_file_unprotected_cves.seek(0) + + # The subject line for the email. + subject = f"Vulnerability Report (CVEs) from Cloud One Network Security - {get_account_id()}, {AWS_REGION}" + + unprotected_cves_html = '' + "\n" + # write headers + unprotected_cves_html += "" + "\n" + for col in csv_header_unprotected_cves: + unprotected_cves_html += f"" + "\n" + unprotected_cves_html += "" + "\n" + # write rows + for row in csv_rows_unprotected_cves: + unprotected_cves_html += "" + "\n" + for col in row: + unprotected_cves_html += f"" + "\n" + unprotected_cves_html += "" + "\n" + unprotected_cves_html += "
{col}
{col}
" + + tmp_csv_file_found_policies = TemporaryFile(mode="w+", newline="") + writer = csv.writer(tmp_csv_file_found_policies) + csv_header_found_policies = ["instance_id", "instance name", "cve", "Intrusion Prevention Filtering"] + writer.writerow(csv_header_found_policies) + for row in csv_rows_found_policies: + writer.writerow(row) + tmp_csv_file_found_policies.seek(0) + + found_policies_html = '' + "\n" + # write headers + found_policies_html += "" + "\n" + for col in csv_header_found_policies: + found_policies_html += f"" + "\n" + found_policies_html += "" + "\n" + # write rows + for row in csv_rows_found_policies: + found_policies_html += "" + "\n" + for col in row: + found_policies_html += f"" + "\n" + found_policies_html += "" + "\n" + found_policies_html += "
{col}
{col}
" + + # The HTML body of the email. + body_html = f""" + + +

Found Policies for CVE

+ {found_policies_html} +

Unprotected CVEs

+ {unprotected_cves_html} + + + """ + + send_email( + SENDER, + RECIPIENTS, + subject, + body_html, + [ + { + "filename": "unprotected_instances.csv", + "attachment": tmp_csv_file_unprotected_cves, + }, + { + "filename": "protected_instances.csv", + "attachment": tmp_csv_file_found_policies, + }, + ], + ) \ No newline at end of file diff --git a/Network-Security/Integration/aws-python-amazon-inspector-integration/python/lambda/PolicyUpdateLambda.py b/Network-Security/Integration/aws-python-amazon-inspector-integration/python/lambda/PolicyUpdateLambda.py new file mode 100644 index 00000000..cfdbbb09 --- /dev/null +++ b/Network-Security/Integration/aws-python-amazon-inspector-integration/python/lambda/PolicyUpdateLambda.py @@ -0,0 +1,413 @@ +import os +import sys, warnings +import csv +import json +from typing import List +from pprint import pprint +from tempfile import TemporaryFile +import boto3 +from botocore.exceptions import ClientError +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.mime.application import MIMEApplication +from urllib.request import Request, urlopen +from urllib.parse import urlencode + +API_VERSION = "v1" +AWS_REGION = os.environ.get("awsregion") +SENDER = os.environ.get("sender") +RECIPIENTS = os.environ.get("recipients") +API_KEY = os.environ.get("c1_api") +API_HOST = f"https://network.{os.environ.get('cloudoneregion')}.cloudone.trendmicro.com/api" +ACTION_SET = os.environ.get("actionset") +PROFILE_NAME = os.environ.get("profilename") +FINDINGS_REPORT_LAMBDA_FUNCTION = os.getenv("findings_report_lambda_function_name") + +######################################## Uncomment the following if you running on your local machine +# AWS_REGION = "us-east-1" ##You can input the aws region you want here +# SENDER = "sender@email.com" +# RECIPIENTS = "recipient@email.com" +# API_KEY = "Your ApiKey" +# API_HOST = "https://network.us-1.cloudone.trendmicro.com/api/policies/" +# PROFILE_NAME = 'Default-Profile' +# ACTION_SET = 'Permit + Notify' #Choose the action set you want to for the intrusion prevention filtering + +secrets = boto3.client('secretsmanager').get_secret_value(SecretId=API_KEY) +sm_data = json.loads(secrets["SecretString"]) +new_api_format = sm_data["ApiKey"] +HEADERS = {"api-version": API_VERSION, "Authorization": f"ApiKey {new_api_format}"} +## initialize the variables +inspector = boto3.client("inspector2", region_name="us-east-1") +# Create a new SES resource and specify a region. +ses = boto3.client("ses", region_name=AWS_REGION) +lambda_client = boto3.client('lambda') +sts = boto3.client("sts") + + +def get_account_id(): + return sts.get_caller_identity()["Account"] + + +def send_email(sender, recipients, subject, html_body, attachment_details=None): + if not attachment_details: + attachment_details = [] + print("sending email ...") + + msg = MIMEMultipart() + text_part = MIMEText(html_body, _subtype="html") + msg.attach(text_part) + + msg["To"] = recipients + msg["From"] = sender + msg["Subject"] = subject + + for item in attachment_details: + filename = item["filename"] + attachment = item["attachment"] + + part = MIMEApplication(attachment.read(), filename) + part.add_header("Content-Disposition", "attachment", filename=filename) + msg.attach(part) + + ses.send_raw_email(RawMessage={"Data": msg.as_bytes()}) + + +def cves_from_instance(instance_id, inspector): + cves = list() + result = list() + # print('instance', instance_id) + resp = inspector.list_findings( + filterCriteria={ + "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}], + "resourceId": [{"comparison": "EQUALS", "value": instance_id}], + } + ) + result.extend(resp["findings"]) + next_token = resp.get("nextToken") + while next_token: + resp = inspector.list_findings( + filterCriteria={ + "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}], + "resourceId": [{"comparison": "EQUALS", "value": instance_id}], + }, + nextToken=next_token, + ) + result.extend(resp["findings"]) + next_token = resp.get("nextToken") + + for finding in result: + cve = finding.get("packageVulnerabilityDetails", {}).get("vulnerabilityId") + if cve: + cves.append(cve) + # print(cve) + return cves + + +def all_instances_details_from_inspector(inspector): + instances = list() + result = list() + findings = inspector.list_finding_aggregations( + aggregationType="AWS_EC2_INSTANCE", + # aggregationRequest='AWS_EC2_INSTANCE', + ) + result.extend(findings["responses"]) + next_token = findings.get("nextToken") + while next_token: + findings = inspector.list_finding_aggregations( + aggregationType="AWS_EC2_INSTANCE", + # aggregationRequest='AWS_EC2_INSTANCE', + nextToken=next_token, + ) + result.extend(findings["responses"]) + next_token = findings.get("nextToken") + + for finding in result: + instance_details = { + "id": finding["ec2InstanceAggregation"]["instanceId"], + "name": finding["ec2InstanceAggregation"] + .get("instanceTags", {}) + .get("Name"), + } + instances.append(instance_details) + print("Found total instances =", len(instances)) + return instances + + +def findings_from_inspector(inspector, all_findings=[], next_token=None): + print("curent inspector findings count = ", len(all_findings)) + params = {} + if next_token: + params = {"nextToken": next_token} + response = inspector.list_findings(**params) + all_findings.extend(response["findings"]) + next_token = response.get("nextToken") + if next_token: + findings_from_inspector(inspector, all_findings, next_token) + print("total inspector findings count = ", len(all_findings)) + return all_findings + + +def filter_findings_with_cves(all_findings): + findings_with_cves = [] + for finding in all_findings: + if finding.get("packageVulnerabilityDetails", {}).get("vulnerabilityId"): + finding_info = { + "title": finding["title"], + "cve": finding["packageVulnerabilityDetails"]["vulnerabilityId"], + } + findings_with_cves.append(finding_info) + print("findings with cve count = ", len(findings_with_cves)) + return findings_with_cves + + +def get_all_policies(all_policies=[], next_token=None, limit=1000): + params = {"limit": limit} + if next_token: + params['cursor'] = next_token + # use the 'headers' parameter to set the HTTP headers: + req = Request(API_HOST + '/policies/?' + urlencode(params)) + for item in HEADERS.items(): + req.add_header(item[0], item[1]) + response = json.loads(urlopen(req).read()) + all_policies.extend(response["policies"]) + + print(f'Fetched {len(all_policies)}/{response["totalCount"]}') + next_token = response.get("next") + if next_token: + get_all_policies(all_policies, next_token) + return all_policies + + +def filter_policies_with_cves(all_policies): + policies_with_cves = [] + for policy in all_policies: + for ref in policy.get("signatureReferences"): + if ref["type"] == "cve": + policy_details = { + "id": policy["id"], + "name": policy["name"], + "severity": policy["severity"], + "cve": ref["value"], + "uuid": policy["uuid"] + } + policies_with_cves.append(policy_details) + print("policies with cve count = ", len(policies_with_cves)) + return policies_with_cves + + +def get_response(url, headers=None, method='GET', data=None): + if data: + data = json.dumps(data) + # Convert to String + data = str(data) + # Convert string to byte + data = data.encode('utf-8') + # Post Method is invoked if data != None + req = Request(url, method=method, data=data) + # Response + else: + req = Request(url) + if headers: + for item in headers.items(): + req.add_header(item[0], item[1]) + response = json.loads(urlopen(req).read()) + return response + + +def get_action_set_id_from_name(action_set_name): + # To GET the Actionset ID + # https://network.us-1.cloudone.trendmicro.com/api/actionsets + + action_set_url = API_HOST + '/actionsets' + response = get_response(url=action_set_url, headers=HEADERS) + + for action_set in response['actionsets']: + # print(action_set) + if action_set['name'] == action_set_name: + block_and_notify_action_set_id = action_set['id'] + print(f"Found actionset id for '{action_set_name}': {block_and_notify_action_set_id}") + return block_and_notify_action_set_id + print(f"Error: coudn't find action set matching '{action_set_name}'") + + +def get_profile_id_from_name(profile_name): + # To GET the profile Id + # https://network.us-1.cloudone.trendmicro.com/api/profiles + profile_url = API_HOST + '/profiles' + response = get_response(url=profile_url, headers=HEADERS) + + for profile in response['profiles']: + if profile['name'] == profile_name: + profile_id = profile['id'] + print(f"Found profile id for '{profile_name}' : {profile_id}") + return profile_id + print(f"Error: coudn't find profile id matching '{profile_name}'") + + +def get_distribution_history(): + # To GET distribution history + # https://network.us-1.cloudone.trendmicro.com/api/appliancedistributions?type=profile + distribution_history_url = API_HOST + '/appliancedistributions?type=profile' + response = get_response(url=distribution_history_url, headers=HEADERS) + print(response) + +def get_appliance_ids(): + # To GET the appliances Id + # https://network.us-1.cloudone.trendmicro.com/api/appliances + appliance_ids = list() + appliance_url = API_HOST + '/appliances' + response = get_response(url=appliance_url, headers=HEADERS) + for appliance in response['appliances']: + appliance_ids.append(appliance['ID']) + return appliance_ids + + +def update_policy(profile_id, action_set_id, policy_signature_uuid): + # To POST the Overrides to the filter/Policy: + # https://network.us-1.cloudone.trendmicro.com/api/profiles/1/policyoverrides + policy_override_api = f'{API_HOST}/profiles/{profile_id}/policyoverrides' + body = { + "signatureUuids": [ + policy_signature_uuid + ], + "actionSetId": int(action_set_id), + "toEnable": True +} + response = get_response(url=policy_override_api, headers=HEADERS, data=body, method='PUT') + return response + + +def distribute_policy(appliance_id, profile_id): + # To POST / distribute the policy to the appliances: + # https://network.us-1.cloudone.trendmicro.com/api/appliancedistributions + distribute_policy_api = API_HOST + '/appliancedistributions' + body = { + "applianceId": appliance_id, + "type": "profile", + "profile": {"id": profile_id} + } + response = get_response(url=distribute_policy_api, headers=HEADERS, data=body) + return response + + +def lambda_handler(event, context): + table_rows_updated_filters = list() + failed_table_rows_updated_filters = list() + # get the action set id + action_set_id = get_action_set_id_from_name(ACTION_SET) + # get the profile id + profile_id = get_profile_id_from_name(PROFILE_NAME) + + # findings = filter_findings_with_cves(findings_from_inspector(inspector)) + policies = filter_policies_with_cves(get_all_policies()) + + policies_to_update = list() + + all_instance_details = all_instances_details_from_inspector(inspector) + for instance_details in all_instance_details: + instance_id = instance_details["id"] + instance_name = instance_details["name"] + cves = cves_from_instance(instance_id, inspector) + for cve in cves: + for policy in policies: + if policy["cve"] == cve: + print( + f"foung policy for {cve} on instance {instance_name} - {policy}" + ) + policies_to_update.append([cve, policy]) + + updated_policies = [] + for cve, policy in policies_to_update: + if policy['uuid'] not in updated_policies: + try: + print(update_policy(profile_id=profile_id, action_set_id=action_set_id, policy_signature_uuid=str(policy['uuid']))) + updated_policies.append(policy['uuid']) + table_rows_updated_filters.append([ACTION_SET, cve, policy['name']]) + except Exception as e: + print(e) + failed_table_rows_updated_filters.append([policy['name'], e]) + + + # print('test policy update') + # print(update_policy(profile_id, action_set_id, '00000001-0001-0001-0001-000000041752')) + + + # get_distribution_history() + appliance_ids = get_appliance_ids() + # print(appliance_ids) + for appliance in appliance_ids: + try: + distribute_policy(appliance_id=appliance, profile_id=profile_id) + except Exception as e: + print(e) + + + # The subject line for the email. + subject = f"Intrusion Prevention Filtering Update from Network Security - {get_account_id()}, {AWS_REGION}" + + # check if any intrusion prevention filters were updated or not and send email + no_filters_to_update_html_body = '

No Intrusion Prevention Filters to update

' + + updated_filters_html_body = '' + if table_rows_updated_filters: + updated_filters_html = '' + "\n" + # write headers + updated_filters_html += "" + "\n" + for col in ['action id', 'CVE', 'Intrusion Prevention Filtering']: + updated_filters_html += f"" + "\n" + updated_filters_html += "" + "\n" + # write rows + for row in table_rows_updated_filters: + updated_filters_html += "" + "\n" + for col in row: + updated_filters_html += f"" + "\n" + updated_filters_html += "" + "\n" + updated_filters_html += "
{col}
{col}
" + updated_filters_html_body = f"""

Updated Intrusion Prevention Filters

+ {updated_filters_html} + """ + no_filters_to_update_html_body = '' + + failed_updated_filters_html_body = '' + if failed_table_rows_updated_filters: + failed_updated_filters_html = '' + "\n" + # write headers + failed_updated_filters_html += "" + "\n" + for col in ['Intrusion Prevention Filtering', 'Reason']: + failed_updated_filters_html += f"" + "\n" + failed_updated_filters_html += "" + "\n" + # write rows + for row in failed_table_rows_updated_filters: + failed_updated_filters_html += "" + "\n" + for col in row: + failed_updated_filters_html += f"" + "\n" + failed_updated_filters_html += "" + "\n" + failed_updated_filters_html += "
{col}
{col}
" + failed_updated_filters_html_body = f"""Failures in updating Intrusion Prevention Filters + {failed_updated_filters_html} + """ + no_filters_to_update_html_body = '' + + html_body = updated_filters_html_body + failed_updated_filters_html_body + no_filters_to_update_html_body + + # The HTML body of the email. + body_html = f""" + + + {html_body} + + + """ + + send_email( + SENDER, + RECIPIENTS, + subject, + body_html, + ) + + lambda_payload = b'' + lambda_client.invoke(FunctionName=FINDINGS_REPORT_LAMBDA_FUNCTION, + InvocationType='Event', + Payload=lambda_payload) + print('triggered findings report lambda function') \ No newline at end of file diff --git a/Network-Security/Integration/aws-python-amazon-inspector-integration/template/c1ns-inspector-findings-template.yaml b/Network-Security/Integration/aws-python-amazon-inspector-integration/template/c1ns-inspector-findings-template.yaml new file mode 100644 index 00000000..50d516c2 --- /dev/null +++ b/Network-Security/Integration/aws-python-amazon-inspector-integration/template/c1ns-inspector-findings-template.yaml @@ -0,0 +1,975 @@ +AWSTemplateFormatVersion: 2010-09-09 +Parameters: + AwsRegion: + Type: String + Description: Please enter the region in which the lambda will be executed + Default : us-east-1 + AllowedValues: + - us-east-1 + - us-east-2 + - us-east-1 + - us-west-1 + - us-west-2 + - af-south-1 + - ap-east-1 + - ap-southeast-3 + - ap-south-1 + - ap-northeast-3 + - ap-northeast-2 + - ap-southeast-1 + - ap-southeast-2 + - ap-northeast-1 + - ca-central-1 + - eu-central-1 + - eu-west-1 + - eu-west-2 + - eu-south-1 + - eu-west-3 + - eu-north-1 + - me-south-1 + - me-central-1 + - sa-east-1 + ApiKey: + Type: String + NoEcho: "true" + Description: Please enter your Cloud One API Key + EmailSender: + Type: String + Description: Email address to be used for sending report + EmailRecipients: + Type: String + Description: Comma separated (without spaces) email addresses for recieving report + ActionSet: + Type: String + Description: Choose the recommended ActionSet you would like apply to the Intrusion Prevention Filtering + AllowedValues: + - Block + - Permit + - Trust + Default: Block + ProfileName: + Type: String + Default: Default-Profile + Description: Profile name for Appliances + CronSchedule: + Type: String + Description: Schedule the time you would like the lambda function to run in the cron format (Default is every wednesday) + Default: cron(0 12 ? * WED *) + CloudOneRegion: + Type: String + Description: Enter your Cloud One Region + Default: us-1 + AllowedValues: + - trend-us-1 + - us-1 + - in-1 + - gb-1 + - jp-1 + - de-1 + - au-1 + - ca-1 + - sg-1 + +Mappings: + ActionToFlowControl: + key: + Block: "Block + Notify" + Permit: "Permit + Notify" + Trust: "Trust" + +Resources: + LambdaRole: + Type: "AWS::IAM::Role" + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: + - "lambda.amazonaws.com" + Action: + - "sts:AssumeRole" + ManagedPolicyArns: + - arn:aws:iam::aws:policy/AmazonVPCCrossAccountNetworkInterfaceOperations + - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole + - arn:aws:iam::aws:policy/AmazonInspector2ReadOnlyAccess + - arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy + - !Ref LambdaSESWritePolicy + - !Ref LambdaInvokePolicy + - !Ref LambdaSecretAcessPolicy + + LambdaInvokePolicy: + Type: AWS::IAM::ManagedPolicy + Properties: + PolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: VisualEditor0 + Effect: Allow + Action: + - lambda:InvokeFunction + - lambda:InvokeAsync + Resource: + - !Sub arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:PolicyUpdateLambda + - !Sub arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:FindingsReportLambda + LambdaSESWritePolicy: + Type: AWS::IAM::ManagedPolicy + Properties: + PolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: VisualEditor0 + Effect: Allow + Action: + - ses:CreateReceiptRule + - ses:UpdateConfigurationSetEventDestination + - ses:SetIdentityMailFromDomain + - ses:DeleteReceiptFilter + - ses:VerifyEmailIdentity + - ses:DeleteCustomVerificationEmailTemplate + - ses:TestRenderTemplate + - ses:CreateReceiptFilter + - ses:DeleteReceiptRule + - ses:DeleteConfigurationSet + - ses:CreateConfigurationSetTrackingOptions + - ses:UpdateAccountSendingEnabled + - ses:DeleteConfigurationSetEventDestination + - ses:VerifyDomainDkim + - ses:DeleteVerifiedEmailAddress + - ses:VerifyDomainIdentity + - ses:CloneReceiptRuleSet + - ses:SetIdentityHeadersInNotificationsEnabled + - ses:SendEmail + - ses:SendTemplatedEmail + - ses:SendCustomVerificationEmail + - ses:UpdateTemplate + - ses:DeleteConfigurationSetTrackingOptions + - ses:UpdateConfigurationSetTrackingOptions + - ses:SetIdentityNotificationTopic + - ses:SetIdentityDkimEnabled + - ses:PutConfigurationSetDeliveryOptions + - ses:VerifyEmailAddress + - ses:UpdateReceiptRule + - ses:CreateConfigurationSet + - ses:UpdateConfigurationSetReputationMetricsEnabled + - ses:DeleteReceiptRuleSet + - ses:CreateTemplate + - ses:SendRawEmail + - ses:ReorderReceiptRuleSet + - ses:SendBounce + - ses:UpdateConfigurationSetSendingEnabled + - ses:SetActiveReceiptRuleSet + - ses:CreateCustomVerificationEmailTemplate + - ses:UpdateCustomVerificationEmailTemplate + - ses:DeleteTemplate + - ses:CreateReceiptRuleSet + - ses:SetReceiptRulePosition + - ses:CreateConfigurationSetEventDestination + - ses:SendBulkTemplatedEmail + - ses:DeleteIdentity + - ses:SetIdentityFeedbackForwardingEnabled + Resource: !Sub arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/${EmailRecipients} + + LambdaSecretAcessPolicy: + Type: AWS::IAM::ManagedPolicy + Properties: + PolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: VisualEditor0 + Effect: Allow + Action: + - "secretsmanager:GetSecretValue" + - "secretsmanager:DescribeSecret" + - "sts:AssumeRole" + Resource: !Ref ApiKeySecret + + # This secret holds the Trend Micro Cloud One API key. + ApiKeySecret: + Type: AWS::SecretsManager::Secret + Properties: + Name: "TrendMicro/CloudOne/ApiKey" + SecretString: !Sub | + { + "ApiKey": "${ApiKey}" + } + + FindingsReportLambda: + Type: "AWS::Lambda::Function" + Properties: + Environment: + Variables: + awsregion: !Ref AwsRegion + c1_api: !Ref ApiKeySecret + sender: !Ref EmailSender + recipients: !Ref EmailRecipients + cloudoneregion: !Ref CloudOneRegion + + Code: + ZipFile: | + import os + import sys, warnings + import csv + import json + from typing import List + from pprint import pprint + from tempfile import TemporaryFile + import boto3 + from botocore.exceptions import ClientError + from email.mime.multipart import MIMEMultipart + from email.mime.text import MIMEText + from email.mime.application import MIMEApplication + from urllib.request import Request, urlopen + from urllib.parse import urlencode + + API_VERSION = "v1" + AWS_REGION = os.environ.get("awsregion") + SENDER = os.environ.get("sender") + RECIPIENTS = os.environ.get("recipients") + API_KEY = os.environ.get("c1_api") + HOST = f"https://network.{os.environ.get('cloudoneregion')}.cloudone.trendmicro.com/api/policies" + + ######################################## Uncomment the following if you running on your local machine + # AWS_REGION = "us-east-1" ##You input the aws region you want + # SENDER = "sender@email.com" + # RECIPIENTS = "recipient@email.com" + # API_KEY = "Your ApiKey" + # HOST = "https://network.us-1.cloudone.trendmicro.com/api/policies/" + + secrets = boto3.client("secretsmanager").get_secret_value(SecretId=API_KEY) + sm_data = json.loads(secrets["SecretString"]) + new_api_format = sm_data["ApiKey"] + HEADERS = {"api-version": API_VERSION, "Authorization": f"ApiKey {new_api_format}"} + ## initialize the variables + inspector = boto3.client("inspector2", region_name="us-east-1") + # Create a new SES resource and specify a region. + ses = boto3.client("ses", region_name=AWS_REGION) + sts = boto3.client("sts") + + + def get_account_id(): + return sts.get_caller_identity()["Account"] + + + def send_email(sender, recipients, subject, html_body, attachment_details): + print("sending email ...") + + msg = MIMEMultipart() + text_part = MIMEText(html_body, _subtype="html") + msg.attach(text_part) + + msg["To"] = recipients + msg["From"] = sender + msg["Subject"] = subject ## + + for item in attachment_details: + filename = item["filename"] + attachment = item["attachment"] + + part = MIMEApplication(attachment.read(), filename) + part.add_header("Content-Disposition", "attachment", filename=filename) + msg.attach(part) + + ses.send_raw_email(RawMessage={"Data": msg.as_bytes()}) + + + def cves_from_instance(instance_id, inspector): + cves = list() + result = list() + # print('instance', instance_id) + resp = inspector.list_findings( + filterCriteria={ + "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}], + "resourceId": [{"comparison": "EQUALS", "value": instance_id}], + } + ) + result.extend(resp["findings"]) + next_token = resp.get("nextToken") + while next_token: + resp = inspector.list_findings( + filterCriteria={ + "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}], + "resourceId": [{"comparison": "EQUALS", "value": instance_id}], + }, + nextToken=next_token, + ) + result.extend(resp["findings"]) + next_token = resp.get("nextToken") + + for finding in result: + cve = finding.get("packageVulnerabilityDetails", {}).get("vulnerabilityId") + if cve: + cves.append(cve) + # print(cve) + return cves + + + def all_instances_details_from_inspector(inspector): + instances = list() + result = list() + findings = inspector.list_finding_aggregations( + aggregationType="AWS_EC2_INSTANCE", + # aggregationRequest='AWS_EC2_INSTANCE', + ) + result.extend(findings["responses"]) + next_token = findings.get("nextToken") + while next_token: + findings = inspector.list_finding_aggregations( + aggregationType="AWS_EC2_INSTANCE", + # aggregationRequest='AWS_EC2_INSTANCE', + nextToken=next_token, + ) + result.extend(findings["responses"]) + next_token = findings.get("nextToken") + + for finding in result: + instance_details = { + "id": finding["ec2InstanceAggregation"]["instanceId"], + "name": finding["ec2InstanceAggregation"] + .get("instanceTags", {}) + .get("Name"), + } + instances.append(instance_details) + print("Found total instances =", len(instances)) + return instances + + + def findings_from_inspector(inspector, all_findings=[], next_token=None): + print("curent inspector findings count = ", len(all_findings)) + params = {} + if next_token: + params = {"nextToken": next_token} + response = inspector.list_findings(**params) + all_findings.extend(response["findings"]) + next_token = response.get("nextToken") + if next_token: + findings_from_inspector(inspector, all_findings, next_token) + print("total inspector findings count = ", len(all_findings)) + return all_findings + + + def filter_findings_with_cves(all_findings): + findings_with_cves = [] + for finding in all_findings: + if finding.get("packageVulnerabilityDetails", {}).get("vulnerabilityId"): + finding_info = { + "title": finding["title"], + "cve": finding["packageVulnerabilityDetails"]["vulnerabilityId"], + } + findings_with_cves.append(finding_info) + print("findings with cve count = ", len(findings_with_cves)) + return findings_with_cves + + + def get_all_policies(all_policies=[], next_token=None, limit=1000): + params = {"limit": limit} + if next_token: + params["cursor"] = next_token + # use the 'headers' parameter to set the HTTP headers: + req = Request(HOST + "?" + urlencode(params)) + for item in HEADERS.items(): + req.add_header(item[0], item[1]) + response = json.loads(urlopen(req).read()) + all_policies.extend(response["policies"]) + + print(f'Fetched {len(all_policies)}/{response["totalCount"]}') + next_token = response.get("next") + if next_token: + get_all_policies(all_policies, next_token) + return all_policies + + + def filter_policies_with_cves(all_policies): + policies_with_cves = [] + for policy in all_policies: + for ref in policy.get("signatureReferences"): + if ref["type"] == "cve": + policy_details = { + "id": policy["id"], + "name": policy["name"], + "severity": policy["severity"], + "cve": ref["value"], + "uuid": policy["uuid"], + } + policies_with_cves.append(policy_details) + print("policies with cve count = ", len(policies_with_cves)) + return policies_with_cves + + + def lambda_handler(event, context): + # findings = filter_findings_with_cves(findings_from_inspector(inspector)) + policies = filter_policies_with_cves(get_all_policies()) + + csv_rows_unprotected_cves = list() + csv_rows_found_policies = list() + + all_instance_details = all_instances_details_from_inspector(inspector) + for instance_details in all_instance_details: + instance_id = instance_details["id"] + instance_name = instance_details["name"] + cves = cves_from_instance(instance_id, inspector) + for cve in cves: + policy_found = False + for policy in policies: + if policy["cve"] == cve: + print( + f"foung policy for {cve} on instance {instance_name} - {policy}" + ) + print(policy) + csv_rows_found_policies.append( + [instance_id, instance_name, cve, policy["name"]] + ) + policy_found = True + if not policy_found: + csv_rows_unprotected_cves.append([instance_id, instance_name, cve]) + + print(csv_rows_found_policies) + # print(csv_rows_unprotected_cves) + + tmp_csv_file_unprotected_cves = TemporaryFile(mode="w+", newline="") + writer = csv.writer(tmp_csv_file_unprotected_cves) + csv_header_unprotected_cves = ["instance id", "instance name", "cve"] + writer.writerow(csv_header_unprotected_cves) + for row in csv_rows_unprotected_cves: + writer.writerow(row) + tmp_csv_file_unprotected_cves.seek(0) + + # The subject line for the email. + subject = f"Vulnerability Report (CVEs) from Cloud One Network Security - {get_account_id()}, {AWS_REGION}" + + unprotected_cves_html = '' + "\n" + # write headers + unprotected_cves_html += "" + "\n" + for col in csv_header_unprotected_cves: + unprotected_cves_html += f"" + "\n" + unprotected_cves_html += "" + "\n" + # write rows + for row in csv_rows_unprotected_cves: + unprotected_cves_html += "" + "\n" + for col in row: + unprotected_cves_html += f"" + "\n" + unprotected_cves_html += "" + "\n" + unprotected_cves_html += "
{col}
{col}
" + + tmp_csv_file_found_policies = TemporaryFile(mode="w+", newline="") + writer = csv.writer(tmp_csv_file_found_policies) + csv_header_found_policies = [ + "instance_id", + "instance name", + "cve", + "Intrusion Prevention Filtering", + ] + writer.writerow(csv_header_found_policies) + for row in csv_rows_found_policies: + writer.writerow(row) + tmp_csv_file_found_policies.seek(0) + + found_policies_html = '' + "\n" + # write headers + found_policies_html += "" + "\n" + for col in csv_header_found_policies: + found_policies_html += f"" + "\n" + found_policies_html += "" + "\n" + # write rows + for row in csv_rows_found_policies: + found_policies_html += "" + "\n" + for col in row: + found_policies_html += f"" + "\n" + found_policies_html += "" + "\n" + found_policies_html += "
{col}
{col}
" + + # The HTML body of the email. + body_html = f""" + + +

Found Policies for CVE

+ {found_policies_html} +

Unprotected CVEs

+ {unprotected_cves_html} + + + """ + + send_email( + SENDER, + RECIPIENTS, + subject, + body_html, + [ + { + "filename": "unprotected_instances.csv", + "attachment": tmp_csv_file_unprotected_cves, + }, + { + "filename": "protected_instances.csv", + "attachment": tmp_csv_file_found_policies, + }, + ], + ) + FunctionName: "FindingsReportLambda" + Handler: "index.lambda_handler" + Runtime: python3.9 + Timeout: 600 + MemorySize: 512 + Role: !GetAtt LambdaRole.Arn + + PolicyUpdateLambda: + Type: "AWS::Lambda::Function" + Properties: + Environment: + Variables: + region: !Ref AwsRegion + c1_api: !Ref ApiKeySecret + sender: !Ref EmailSender + recipients: !Ref EmailRecipients + cloudoneregion: !Ref CloudOneRegion + actionset: !FindInMap [ActionToFlowControl, key, !Ref ActionSet] + profilename: !Ref ProfileName + findings_report_lambda_function_name: FindingsReportLambda + Code: + ZipFile: | + import os + import sys, warnings + import csv + import json + from typing import List + from pprint import pprint + from tempfile import TemporaryFile + import boto3 + from botocore.exceptions import ClientError + from email.mime.multipart import MIMEMultipart + from email.mime.text import MIMEText + from email.mime.application import MIMEApplication + from urllib.request import Request, urlopen + from urllib.parse import urlencode + + API_VERSION = "v1" + AWS_REGION = os.environ.get("awsregion") + SENDER = os.environ.get("sender") + RECIPIENTS = os.environ.get("recipients") + API_KEY = os.environ.get("c1_api") + API_HOST = f"https://network.{os.environ.get('cloudoneregion')}.cloudone.trendmicro.com/api" + ACTION_SET = os.environ.get("actionset") + PROFILE_NAME = os.environ.get("profilename") + FINDINGS_REPORT_LAMBDA_FUNCTION = os.getenv("findings_report_lambda_function_name") + + ######################################## Uncomment the following if you running on your local machine + # AWS_REGION = "us-east-1" ##You can input the aws region you want here + # SENDER = "sender@email.com" + # RECIPIENTS = "recipient@email.com" + # API_KEY = "Your ApiKey" + # API_HOST = "https://network.us-1.cloudone.trendmicro.com/api/policies/" + # PROFILE_NAME = 'Default-Profile' + # ACTION_SET = 'Permit + Notify' #Choose the action set you want to for the intrusion prevention filtering + + secrets = boto3.client('secretsmanager').get_secret_value(SecretId=API_KEY) + sm_data = json.loads(secrets["SecretString"]) + new_api_format = sm_data["ApiKey"] + HEADERS = {"api-version": API_VERSION, "Authorization": f"ApiKey {new_api_format}"} + ## initialize the variables + inspector = boto3.client("inspector2", region_name="us-east-1") + # Create a new SES resource and specify a region. + ses = boto3.client("ses", region_name=AWS_REGION) + lambda_client = boto3.client('lambda') + sts = boto3.client("sts") + + + def get_account_id(): + return sts.get_caller_identity()["Account"] + + + def send_email(sender, recipients, subject, html_body, attachment_details=None): + if not attachment_details: + attachment_details = [] + print("sending email ...") + + msg = MIMEMultipart() + text_part = MIMEText(html_body, _subtype="html") + msg.attach(text_part) + + msg["To"] = recipients + msg["From"] = sender + msg["Subject"] = subject + + for item in attachment_details: + filename = item["filename"] + attachment = item["attachment"] + + part = MIMEApplication(attachment.read(), filename) + part.add_header("Content-Disposition", "attachment", filename=filename) + msg.attach(part) + + ses.send_raw_email(RawMessage={"Data": msg.as_bytes()}) + + + def cves_from_instance(instance_id, inspector): + cves = list() + result = list() + # print('instance', instance_id) + resp = inspector.list_findings( + filterCriteria={ + "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}], + "resourceId": [{"comparison": "EQUALS", "value": instance_id}], + } + ) + result.extend(resp["findings"]) + next_token = resp.get("nextToken") + while next_token: + resp = inspector.list_findings( + filterCriteria={ + "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}], + "resourceId": [{"comparison": "EQUALS", "value": instance_id}], + }, + nextToken=next_token, + ) + result.extend(resp["findings"]) + next_token = resp.get("nextToken") + + for finding in result: + cve = finding.get("packageVulnerabilityDetails", {}).get("vulnerabilityId") + if cve: + cves.append(cve) + # print(cve) + return cves + + + def all_instances_details_from_inspector(inspector): + instances = list() + result = list() + findings = inspector.list_finding_aggregations( + aggregationType="AWS_EC2_INSTANCE", + # aggregationRequest='AWS_EC2_INSTANCE', + ) + result.extend(findings["responses"]) + next_token = findings.get("nextToken") + while next_token: + findings = inspector.list_finding_aggregations( + aggregationType="AWS_EC2_INSTANCE", + # aggregationRequest='AWS_EC2_INSTANCE', + nextToken=next_token, + ) + result.extend(findings["responses"]) + next_token = findings.get("nextToken") + + for finding in result: + instance_details = { + "id": finding["ec2InstanceAggregation"]["instanceId"], + "name": finding["ec2InstanceAggregation"] + .get("instanceTags", {}) + .get("Name"), + } + instances.append(instance_details) + print("Found total instances =", len(instances)) + return instances + + + def findings_from_inspector(inspector, all_findings=[], next_token=None): + print("curent inspector findings count = ", len(all_findings)) + params = {} + if next_token: + params = {"nextToken": next_token} + response = inspector.list_findings(**params) + all_findings.extend(response["findings"]) + next_token = response.get("nextToken") + if next_token: + findings_from_inspector(inspector, all_findings, next_token) + print("total inspector findings count = ", len(all_findings)) + return all_findings + + + def filter_findings_with_cves(all_findings): + findings_with_cves = [] + for finding in all_findings: + if finding.get("packageVulnerabilityDetails", {}).get("vulnerabilityId"): + finding_info = { + "title": finding["title"], + "cve": finding["packageVulnerabilityDetails"]["vulnerabilityId"], + } + findings_with_cves.append(finding_info) + print("findings with cve count = ", len(findings_with_cves)) + return findings_with_cves + + + def get_all_policies(all_policies=[], next_token=None, limit=1000): + params = {"limit": limit} + if next_token: + params['cursor'] = next_token + # use the 'headers' parameter to set the HTTP headers: + req = Request(API_HOST + '/policies/?' + urlencode(params)) + for item in HEADERS.items(): + req.add_header(item[0], item[1]) + response = json.loads(urlopen(req).read()) + all_policies.extend(response["policies"]) + + print(f'Fetched {len(all_policies)}/{response["totalCount"]}') + next_token = response.get("next") + if next_token: + get_all_policies(all_policies, next_token) + return all_policies + + + def filter_policies_with_cves(all_policies): + policies_with_cves = [] + for policy in all_policies: + for ref in policy.get("signatureReferences"): + if ref["type"] == "cve": + policy_details = { + "id": policy["id"], + "name": policy["name"], + "severity": policy["severity"], + "cve": ref["value"], + "uuid": policy["uuid"] + } + policies_with_cves.append(policy_details) + print("policies with cve count = ", len(policies_with_cves)) + return policies_with_cves + + + def get_response(url, headers=None, method='GET', data=None): + if data: + data = json.dumps(data) + # Convert to String + data = str(data) + # Convert string to byte + data = data.encode('utf-8') + # Post Method is invoked if data != None + req = Request(url, method=method, data=data) + # Response + else: + req = Request(url) + if headers: + for item in headers.items(): + req.add_header(item[0], item[1]) + response = json.loads(urlopen(req).read()) + return response + + + def get_action_set_id_from_name(action_set_name): + # To GET the Actionset ID + # https://network.us-1.cloudone.trendmicro.com/api/actionsets + + action_set_url = API_HOST + '/actionsets' + response = get_response(url=action_set_url, headers=HEADERS) + + for action_set in response['actionsets']: + # print(action_set) + if action_set['name'] == action_set_name: + block_and_notify_action_set_id = action_set['id'] + print(f"Found actionset id for '{action_set_name}': {block_and_notify_action_set_id}") + return block_and_notify_action_set_id + print(f"Error: coudn't find action set matching '{action_set_name}'") + + + def get_profile_id_from_name(profile_name): + # To GET the profile Id + # https://network.us-1.cloudone.trendmicro.com/api/profiles + profile_url = API_HOST + '/profiles' + response = get_response(url=profile_url, headers=HEADERS) + + for profile in response['profiles']: + if profile['name'] == profile_name: + profile_id = profile['id'] + print(f"Found profile id for '{profile_name}' : {profile_id}") + return profile_id + print(f"Error: coudn't find profile id matching '{profile_name}'") + + + def get_distribution_history(): + # To GET distribution history + # https://network.us-1.cloudone.trendmicro.com/api/appliancedistributions?type=profile + distribution_history_url = API_HOST + '/appliancedistributions?type=profile' + response = get_response(url=distribution_history_url, headers=HEADERS) + print(response) + + def get_appliance_ids(): + # To GET the appliances Id + # https://network.us-1.cloudone.trendmicro.com/api/appliances + appliance_ids = list() + appliance_url = API_HOST + '/appliances' + response = get_response(url=appliance_url, headers=HEADERS) + for appliance in response['appliances']: + appliance_ids.append(appliance['ID']) + return appliance_ids + + + def update_policy(profile_id, action_set_id, policy_signature_uuid): + # To POST the Overrides to the filter/Policy: + # https://network.us-1.cloudone.trendmicro.com/api/profiles/1/policyoverrides + policy_override_api = f'{API_HOST}/profiles/{profile_id}/policyoverrides' + body = { + "signatureUuids": [ + policy_signature_uuid + ], + "actionSetId": int(action_set_id), + "toEnable": True + } + response = get_response(url=policy_override_api, headers=HEADERS, data=body, method='PUT') + return response + + + def distribute_policy(appliance_id, profile_id): + # To POST / distribute the policy to the appliances: + # https://network.us-1.cloudone.trendmicro.com/api/appliancedistributions + distribute_policy_api = API_HOST + '/appliancedistributions' + body = { + "applianceId": appliance_id, + "type": "profile", + "profile": {"id": profile_id} + } + response = get_response(url=distribute_policy_api, headers=HEADERS, data=body) + return response + + + def lambda_handler(event, context): + table_rows_updated_filters = list() + failed_table_rows_updated_filters = list() + # get the action set id + action_set_id = get_action_set_id_from_name(ACTION_SET) + # get the profile id + profile_id = get_profile_id_from_name(PROFILE_NAME) + + # findings = filter_findings_with_cves(findings_from_inspector(inspector)) + policies = filter_policies_with_cves(get_all_policies()) + + policies_to_update = list() + + all_instance_details = all_instances_details_from_inspector(inspector) + for instance_details in all_instance_details: + instance_id = instance_details["id"] + instance_name = instance_details["name"] + cves = cves_from_instance(instance_id, inspector) + for cve in cves: + for policy in policies: + if policy["cve"] == cve: + print( + f"foung policy for {cve} on instance {instance_name} - {policy}" + ) + policies_to_update.append([cve, policy]) + + updated_policies = [] + for cve, policy in policies_to_update: + if policy['uuid'] not in updated_policies: + try: + print(update_policy(profile_id=profile_id, action_set_id=action_set_id, policy_signature_uuid=str(policy['uuid']))) + updated_policies.append(policy['uuid']) + table_rows_updated_filters.append([ACTION_SET, cve, policy['name']]) + except Exception as e: + print(e) + failed_table_rows_updated_filters.append([policy['name'], e]) + + + # print('test policy update') + # print(update_policy(profile_id, action_set_id, '00000001-0001-0001-0001-000000041752')) + + + # get_distribution_history() + appliance_ids = get_appliance_ids() + # print(appliance_ids) + for appliance in appliance_ids: + try: + distribute_policy(appliance_id=appliance, profile_id=profile_id) + except Exception as e: + print(e) + + + # The subject line for the email. + subject = f"Intrusion Prevention Filtering Update from Network Security - {get_account_id()}, {AWS_REGION}" + + # check if any intrusion prevention filters were updated or not and send email + no_filters_to_update_html_body = '

No Intrusion Prevention Filters to update

' + + updated_filters_html_body = '' + if table_rows_updated_filters: + updated_filters_html = '' + "\n" + # write headers + updated_filters_html += "" + "\n" + for col in ['action id', 'CVE', 'Intrusion Prevention Filtering']: + updated_filters_html += f"" + "\n" + updated_filters_html += "" + "\n" + # write rows + for row in table_rows_updated_filters: + updated_filters_html += "" + "\n" + for col in row: + updated_filters_html += f"" + "\n" + updated_filters_html += "" + "\n" + updated_filters_html += "
{col}
{col}
" + updated_filters_html_body = f"""

Updated Intrusion Prevention Filters

+ {updated_filters_html} + """ + no_filters_to_update_html_body = '' + + failed_updated_filters_html_body = '' + if failed_table_rows_updated_filters: + failed_updated_filters_html = '' + "\n" + # write headers + failed_updated_filters_html += "" + "\n" + for col in ['Intrusion Prevention Filtering', 'Reason']: + failed_updated_filters_html += f"" + "\n" + failed_updated_filters_html += "" + "\n" + # write rows + for row in failed_table_rows_updated_filters: + failed_updated_filters_html += "" + "\n" + for col in row: + failed_updated_filters_html += f"" + "\n" + failed_updated_filters_html += "" + "\n" + failed_updated_filters_html += "
{col}
{col}
" + failed_updated_filters_html_body = f"""Failures in updating Intrusion Prevention Filters + {failed_updated_filters_html} + """ + no_filters_to_update_html_body = '' + + html_body = updated_filters_html_body + failed_updated_filters_html_body + no_filters_to_update_html_body + + # The HTML body of the email. + body_html = f""" + + + {html_body} + + + """ + + send_email( + SENDER, + RECIPIENTS, + subject, + body_html, + ) + + lambda_payload = b'' + lambda_client.invoke(FunctionName=FINDINGS_REPORT_LAMBDA_FUNCTION, + InvocationType='Event', + Payload=lambda_payload) + print('triggered findings report lambda function') + FunctionName: "PolicyUpdateLambda" + Handler: "index.lambda_handler" + Runtime: python3.9 + Timeout: 600 + MemorySize: 512 + Role: !GetAtt LambdaRole.Arn + + ScheduledRule: + Type: AWS::Events::Rule + Properties: + Description: "ScheduledRule" + ScheduleExpression: !Sub ${CronSchedule} + State: "ENABLED" + Targets: + - + Arn: + Fn::GetAtt: + - "PolicyUpdateLambda" + - "Arn" + Id: "TargetFunctionV1" + + PermissionForEventsToInvokeLambda: + Type: AWS::Lambda::Permission + Properties: + FunctionName: !Ref "PolicyUpdateLambda" + Action: "lambda:InvokeFunction" + Principal: "events.amazonaws.com" + SourceArn: + Fn::GetAtt: + - "ScheduledRule" + - "Arn" \ No newline at end of file