注意:
- 本教程需要访问 Oracle Cloud。要注册免费账户,请参阅开始使用 Oracle Cloud Infrastructure 免费套餐。
- 它对 Oracle Cloud Infrastructure 身份证明、租户和区间使用示例值。完成实验室后,请使用特定于云环境的那些值替换这些值。
使用 Oracle Cloud Infrastructure Functions 通过电子邮件发送 Oracle Cloud Guard 问题
简介
如今,各行各业的网络安全团队面临着在处理云环境动态性的同时保持稳固的安全态势的复杂挑战。凭借无数的资源、配置和不断发展的威胁,及时跟踪和解决问题至关重要。在 Oracle,我们认识到为组织提供工具来简化安全管理,帮助他们积极应对这些挑战的重要性。Oracle Cloud Guard 提供了强大的功能,可帮助企业简化云安全管理,从仅仅识别漏洞到在团队之间自动分配问题以及时解决问题。
本教程概述了利用 OCI 原生服务的自动化解决方案,使安全团队能够直接通过电子邮件接收和共享详细的 Oracle Cloud Guard 安全报告。这种方法减少了手动干预,促进了持续监控,并确保了云安全性的积极立场。
目标
- 通过电子邮件接收所有 Oracle Cloud Guard 未解决的问题,并将这些问题分发给相应的团队,从而简化解决方法。
先决条件
-
访问 Oracle Cloud Infrastructure (OCI) 租户。
-
管理 Oracle Cloud Guard、OCI Functions、OCI Email Delivery 服务、OCI Vault 和 OCI Resource Scheduler 的权限。
任务 1:设置必需的策略和 Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) 权限
此解决方案的每个组件都必须有权访问与之交互的 OCI 资源。
-
资源调度程序策略:允许调度程序按定义的调度运行函数。有关更多信息,请参见 Example Policies 。
-
函数策略:授予从 OCI Vault 读取密钥和检索 Oracle Cloud Guard 问题的功能权限。有关详细信息,请参阅函数的详细信息。
任务 2:配置 OCI 电子邮件传送服务
-
通过创建用于发送报告的批准的发件人电子邮件地址来设置 OCI 电子邮件传送服务。有关更多信息,请参阅创建批准的发件人。
-
通过创建具有有限 SMTP 访问权限的专用用户,为电子邮件帐户生成简单邮件传输协议 (Simple Mail Transfer Protocol,SMTP) 凭证。
-
创建 OCI IAM 用户。有关详细信息,请参阅创建用户。
-
要编辑用户功能,请参见 Editing a User’s Capabilities 。
-
要创建 SMTP 身份证明,请参见 Creating SMTP Credentials 。
-
任务 3:在 OCI Vault 中存储 SMTP 身份证明
使用 OCI Vault 安全地存储 SMTP 凭据。这通过将敏感信息保留在功能之外增加了额外的安全层。
-
设置 OCI Vault。有关详细信息,请参阅创建 Vault 。
-
要创建主加密密钥,请参见 Creating a Master Encryption Key 。
-
要创建密钥,请参见 Creating a Secret in a Vault 。
任务 4:开发和部署函数
此函数提取过去一年中检测到的未解决的安全态势问题。它根据风险级别和检测时间等参数将数据格式化为结构化报告,然后通过电子邮件将其作为逗号分隔值 (CSV) 附件发送。有关详细信息,请参阅部署函数。
以下是核心函数步骤的概述:
- 从 Oracle Cloud Guard 检索未解决的问题。
- 根据严重性组织和设置问题格式。
- 生成 CSV 报告并设置报告的格式。
- 从 Vault 检索 SMTP 凭证。
- 将电子邮件发送给以 CSV 作为附件的指定收件人。
通过使用 OCI 的无服务器功能,您可以自定义此报告,尽可能明确网络安全团队的关键安全领域。
-
func.py
:import oci import io import smtplib import logging import base64 from oci.pagination import list_call_get_all_results from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase from email import encoders from email.mime.text import MIMEText from openpyxl import Workbook from datetime import datetime, timedelta, timezone from openpyxl.styles import Font # Retrieve secrets from OCI Vault def get_secret_value(secret_ocid): print(f"Retrieving secret for OCID: {secret_ocid}") signer = oci.auth.signers.get_resource_principals_signer() secrets_client = oci.secrets.SecretsClient({}, signer=signer) try: secret_content = secrets_client.get_secret_bundle(secret_ocid) secret_data = secret_content.data.secret_bundle_content.content secret_value = base64.b64decode(secret_data).decode('utf-8') return secret_value except Exception as e: print(f"Failed to retrieve secret: {e}") raise # Format the CSV def get_font_style(risk_level, time_condition): color_map = { "CRITICAL": "8B0000", "HIGH": "FF8C00", "MEDIUM": "006400", "LOW": "00008B", "MINOR": "000000" } font_color = color_map.get(risk_level, "000000") is_bold = time_condition return Font(color=font_color, bold=is_bold) def get_clients_with_resource_principal(): signer = oci.auth.signers.get_resource_principals_signer() cloud_guard_client = oci.cloud_guard.CloudGuardClient({}, signer=signer) return cloud_guard_client def send_email_via_oci(subject, body, email, attachment, username_secret_ocid, password_secret_ocid, from_email, smtp_endpoint, smtp_port): msg = MIMEMultipart() msg['Subject'] = subject msg['From'] = from_email msg['To'] = email msg.attach(MIMEText(body, 'plain')) today_date = datetime.today().strftime('%Y-%m-%d') filename = f"CloudGuardReport_{today_date}.xlsx" part = MIMEBase('application', "octet-stream") part.set_payload(attachment) encoders.encode_base64(part) part.add_header('Content-Disposition', f"attachment; filename={filename}") msg.attach(part) try: username = get_secret_value(username_secret_ocid) password = get_secret_value(password_secret_ocid) server = smtplib.SMTP(smtp_endpoint, smtp_port) server.starttls() server.login(username, password) server.sendmail(msg['From'], [msg['To']], msg.as_string()) server.quit() print("Email sent successfully.") except Exception as e: print(f"Failed to send email: {e}") raise def handler(ctx, data: io.BytesIO=None): try: cfg = ctx.Config() compartment_id = cfg["compartment-id"] from_email = cfg["from-email"] email = cfg["to-email"] smtp_endpoint = cfg["smtp-endpoint"] smtp_port = cfg["smtp-port"] username_secret_ocid = cfg["smtp-username-secret-ocid"] password_secret_ocid = cfg["smtp-password-secret-ocid"] cg_access_level = cfg["cloudguard-access-level"] cloud_guard_client = get_clients_with_resource_principal() current_time = datetime.now(timezone.utc) time_last_7_days = current_time - timedelta(days=7) formatted_time_one_year_ago = (current_time - timedelta(days=365)).strftime("%Y-%m-%dT%H:%M:%S.%fZ") formatted_time_last_7_days = time_last_7_days.strftime("%Y-%m-%dT%H:%M:%S.%fZ") # List problems problem_response = list_call_get_all_results( cloud_guard_client.list_problems, compartment_id, compartment_id_in_subtree=True, access_level=cg_access_level, time_first_detected_greater_than_or_equal_to=formatted_time_one_year_ago, time_last_detected_greater_than_or_equal_to=formatted_time_last_7_days ).data if not problem_response: logging.info("No open problems found.") return {"statusCode": 200, "body": "No open problems found."} print(f"Found {len(problem_response)} problems") # Sort problems by Risk Level risk_level_order = {"CRITICAL": 1, "HIGH": 2, "MEDIUM": 3, "LOW": 4, "MINOR": 5} problem_response.sort(key=lambda x: risk_level_order.get(x.risk_level, 6)) # Create a workbook and select active worksheet wb = Workbook() ws = wb.active ws.title = "Cloud Guard Problems" ws.append([ "Region", "ID", "Resource Type", "Resource Name", "Risk Level", "Detector", "Rule Display Name", "Rule Description", "Rule Recommendation", "Resource ID", "Time First Detected", "Time Last Detected" ]) # Iterate through the problems for problem in problem_response: try: detector_rule_response = cloud_guard_client.get_detector_rule(problem.detector_id, problem.detector_rule_id).data rule_description = detector_rule_response.description detector = detector_rule_response.detector rule_display_name = detector_rule_response.display_name rule_recommendation = detector_rule_response.recommendation except Exception as e: logging.error(f"Error fetching detector rule details for problem {problem.id}: {str(e)}") rule_description, detector, rule_display_name, rule_recommendation = "N/A", "N/A", "N/A", "N/A" problem_details = [ problem.region, problem.id, problem.resource_type, problem.resource_name, problem.risk_level, detector, rule_display_name, rule_description, rule_recommendation, problem.resource_id, problem.time_first_detected.replace(tzinfo=None) if problem.time_first_detected else None, problem.time_last_detected.replace(tzinfo=None) if problem.time_last_detected else None ] # Add row to worksheet ws.append(problem_details) # Apply format time_condition = problem.time_last_detected > problem.time_first_detected if problem.time_last_detected and problem.time_first_detected else False font_style = get_font_style(problem.risk_level, time_condition) row_number = ws.max_row for cell in ws[row_number]: cell.font = font_style virtual_workbook = io.BytesIO() wb.save(virtual_workbook) virtual_workbook.seek(0) # Send email send_email_via_oci("OCI Cloud Guard Report", "Find attached the OCI Cloud Guard Report.", email, virtual_workbook.getvalue(), username_secret_ocid, password_secret_ocid, from_email, smtp_endpoint, smtp_port) return {"statusCode": 200, "body": "Email sent successfully."} except Exception as e: logging.error(f"Error: {str(e)}") return {"statusCode": 500, "body": f"Error: {str(e)}"}
-
func.yaml
:schema_version: 20180708 name: oci-cloud-guard-problems version: 0.0.1 runtime: python build_image: fnproject/python:3.9-dev run_image: fnproject/python:3.9 entrypoint: /python/bin/fdk /function/func.py handler memory: 256 config: compartment-id: Provide a compartment ID here to-email: Provide a valid email ID here from-email: Provide the Approved Sender Email ID created in the Email Delivery Service smtp-username-secret-ocid: Provide the SMTP Username Secret OCID smtp-password-secret-ocid: Provide the SMTP Password Secret OCID smtp-endpoint: Provide the SMTP endpoint from the Email Delivery Service smtp-port: 587 cloudguard-access-level: ACCESSIBLE or RESTRICTED
-
requirements.txt
:fdk oci openpyxl
任务 5:使用 OCI 资源调度程序调度函数
最后一项任务是使用 OCI 资源调度器自动执行函数。此配置可以按周或按定制时间间隔运行,从而确保及时、一致的安全报告。有关更多信息,请参阅创建计划。
-
要运行函数,请输入以下必需信息。
在部署函数之前,请收集以下配置详细信息。这些输入将在函数配置文件 (
func.yaml
) 中提供,或者直接在 OCI 控制台中提供,具体取决于您的部署方法。- 区间 ID:这是启用了 Oracle Cloud Guard 的租户 ID。
- 发件人电子邮件:在 OCI 电子邮件传送服务中创建的已批准发件人电子邮件。
- 收件人电子邮件:将向其发送报表的电子邮件地址。
- SMTP 端点:要查找它,请转到电子邮件传送和配置。
- SMTP 端口:通常为 587 或 25,在 OCI 电子邮件传送服务配置中指定。
- SMTP 用户名密钥 OCID: SMTP 用户名的 OCID,安全地存储在 OCI Vault 中。
- SMTP 密码密钥 OCID: SMTP 密码的 OCID,安全地存储在 OCI Vault 中。
- Cloud Guard 访问级别:定义资源的访问级别(
ACCESSIBLE
或RESTRICTED
)。默认值为ACCESSIBLE
。
-
我们将获得一个可操作的 Oracle Cloud Guard 报告示例输出。
此 Oracle Cloud Guard 报告提供 OCI 环境中未解决的安全问题的结构化概览,按风险级别分类。通过区域、资源类型、严重性和检测时间戳等关键详细信息,网络安全团队可以评估操作并确定操作优先级。
后续步骤
Oracle Cloud Guard 的自动化报告为网络安全团队提供了一种简化、主动的方法来管理云安全风险。组织可以通过一次性配置接收有关未解决的安全问题的每周报告,按严重性和风险级别进行分类。此解决方案可解决云安全管理的复杂手动任务,使团队能够专注于及时解决问题,而不是跟踪问题。有关使用 Oracle Cloud Guard 及其 CNAPP 功能的更多信息,请与 Oracle 代表联系或参见云安全服务。
确认
- 作者 — Aneel Kanuri(杰出云架构师)
更多学习资源
浏览 docs.oracle.com/learn 上的其他实验室,或者访问 Oracle Learning YouTube 渠道上的更多免费学习内容。此外,请访问 education.oracle.com/learning-explorer 成为 Oracle Learning Explorer。
有关产品文档,请访问 Oracle 帮助中心。
Send Oracle Cloud Guard Problems through Email using Oracle Cloud Infrastructure Functions
G23230-01
December 2024