如何使用 Terraform 为 Amazon GuardDuty 发现配置自动事件响应

在本教程中,您将学习如何使用 Terraform 配置亚马逊云科技安全解决方案。您将使用 Amazon GuardDuty、Amazon SNS、Amazon Lambda 和 Amazon EvenBridge 服务。
发布时间:2023 年 4 月 20 日
基础设施即代码
Terraform
GuardDuty
威胁检测
事件响应
安全
VPC
VPC 流量日志
S3
SNS
EventBridge
Lambda
教程
亚马逊云科技

对于许多组织来说,威胁检测和事件响应 (TDIR) 可能是非常耗时的手动过程。这会导致响应过程不一致、安全结果不一致以及风险增加。在本教程中,您将学习如何自动通过威胁检测发现并自动执行事件响应流程,从而缩短威胁响应时间。鉴于许多组织倾向于使用标准的基础设施即代码 (IaC) 工具,以实现跨供应商的一致性配置,本教程将展示如何使用 Terraform 配置此解决方案。

Olawale Olaleye
亚马逊云科技使用经验
200 - 中级
完成所需时间
60 分钟
所需费用
低于 2.00 美元
前提条件

注册 / 登录 亚马逊云科技账户

代码
上次更新时间
2023 年 4 月 20 日

关于 Amazon GuardDuty

在深入研究教程内容之前,了解我们将使用的一些工具的基本功能会很有帮助。Amazon GuardDuty 提供了威胁检测功能,能够持续监控和保护您的亚马逊云科技账户、工作负载以及存储在 Amazon Simple Storage Service (S3) 中的数据。GuardDuty 可以分析从您的账户以及 Amazon CloudTrail 事件、Amazon 虚拟私有云 (VPC) 流量日志和域名系统 (DNS) 日志中生成的连续元数据流。GuardDuty 还可以利用集成的威胁智能(例如已知恶意 IP 地址)、异常检测和机器学习 (ML) 来更准确地识别威胁。

GuardDuty 完全独立于您的资源运行,因此不存在影响工作负载性能或可用性的风险。这是一项完全托管的服务,并且集成了威胁智能、异常检测和机器学习。Amazon GuardDuty 提供了详尽且实用的警报,能轻松与现有的事件管理和工作流系统集成。您无需预付任何费用,只需为分析的事件付费,而无需部署其他软件或订阅威胁情报源。

关于 Terraform 和 Cloud9

Terraform 是由 Hashicorp 开发的一款基础设施即代码 (IaC) 工具,让您能够通过配置文件管理基础设施,而非依赖用户界面。借助 Terraform,您可以使用易于阅读的声明式配置文件来构建、更改和销毁您的基础设施。没错,您也可以使用 Terraform 来构建、更改和销毁亚马逊云科技基础设施——这需要通过名为 Provider 的 Terraform 插件来实现。Amazon Provider 使 Terraform 能够与 Amazon API 交互。

本教程将使用 Amazon Cloud9 来执行 Terraform 配置。Amazon Cloud9 是基于云的集成开发环境 (IDE),让您只需使用浏览器即可编写、运行和调试代码。它包含了代码编辑器、调试器和终端。Cloud9 预装了多种流行编程语言的必备工具,包括 JavaScript、Python、PHP 和 Terraform,因此您无需安装文件或配置开发机器即可启动此实操项目。Cloud9 会在启动此实训项目时为您创建的 EC2 实例上运行。

学习目标

我们假设发生了如下事件:

  1. 一台“恶意”主机与一台“受入侵”主机进行交互,导致 GuardDuty 报告一次发现。
  2. 系统会将该发现与您使用 Terraform 创建的一个 EventBridge 规则匹配。该 EventBridge 规则会执行以下两项操作:
    1. 首先,它会触发您使用 Terraform 创建的一个 SNS 规则。该 SNS 规则将向指定的管理员发送电子邮件,以易于阅读的文本说明发现的问题。
    2. 其次,它会触发您使用 Terraform 创建的一个 Lambda 函数。该 Lambda 函数会将受入侵主机迁至取证安全组中,在隔离状态下开展进一步调查。

前提条件

完成本教程需要亚马逊云科技账户。如果您还没有亚马逊云科技账户,请先新建一个亚马逊云科技账户。您还需要下载上文中提供的示例代码。

步骤 1:加载初始配置

本教程利用 Amazon CloudFormation 模板来预配初始资源。目的是让您专注于使用 Terraform 配置您的安全解决方案。该 CloudFormation 模板会创建一个堆栈,其中包含一个 Amazon Cloud9 IDE 实例。我们使用该实例是为了让每个学习本教程的用户都获得一致的编辑及开发体验。此外,当您在 US-WEST-2 区域中创建该堆栈时,应确保该区域有 t3.small 实例可分配。如果在其他区域部署该模板,则在 t3.small 不可用的情况下可能需要修改模板以使用其他实例类型。

1.1.前往亚马逊云科技管理控制台中的 Amazon CloudFormation,然后点击 create stack 按钮创建堆栈。

1.2.点击 Upload a template file(上传模板文件),然后从上文提供的示例代码存储库中上传 gd-iac-initial.yml 文件。然后点击 Next(下一步)。

1.3.输入堆栈名称,然后点击 Next(下一步)。

1.4.在 Configure stack options(配置堆栈选项)页面上,点击 Next(下一步)。

1.5.在 Review(查看)页面上,滚动至页面底部并点击复选框,以确认 Amazon CloudFormation 可能会创建 IAM 资源,然后点击 Next(下一步)。

1.6.留意该堆栈是否处于 CREATE_COMPLETE(创建完成)状态。

您已经部署了本教程所需的初始资源,接下来您可以独立地按教程步骤操作。在下一个步骤中,您将访问由该堆栈创建的 Cloud9 实例并初始化 Terraform。

步骤 2:访问 Cloud9 并初始化 Terraform

2.1.在亚马逊云科技管理控制台中打开 Amazon Cloud9,然后在 Cloud9 IDE 中打开该环境。

2.2.在 Cloud9 首选项中,禁用亚马逊云科技托管临时凭据的使用。

2.3.从您的 Cloud9 实例的终端克隆初始代码存储库。

git clone https://github.com/build-on-aws/automating-amazon-guardduty-with-iac.git

2.4.切换至 automating-amazon-guardduty-with-iac 目录,依次执行 terraform initterraform plan 和 terraform apply 命令。


Terraform 将在 US-WEST-2 区域应用配置,而 CloudFormation 模板是在 US-WEST-1 区域创建了 Cloud9 实例——这是有意为之,旨在让您在进行验证时只看到使用 Terraform 创建的内容。

 

成功应用配置后的输出应如下所示:

2.5.验证是否存在两个新的 EC2 实例,其中一个名为 IAC Tutorial: Compromised Instance,另一个名为 IAC Tutorial: Malicious Instance

此时,您已部署一个 VPC 和两个 EC2 实例。这两个 EC2 实例会相互通信,稍后如果将其中一个 EC2 实例的弹性 IP 地址添加至威胁列表,会导致 GuardDuty 生成发现。接下来,您将创建构成实际安全解决方案的各项资源。

步骤 3:创建一个 S3 存储桶来存储威胁列表

GuardDuty 可以引用两种类型的列表:受信任 IP 列表威胁 IP 列表。GuardDuty 不会针对包含在受信任 IP 列表中的 IP 地址生成发现,但它确实会针对包含在威胁 IP 列表中的 IP 地址生成发现。由于我们希望在本教程中强制生成发现,因此我们将使用威胁列表。

3.1.首先在 modules/s3/variables.tf 中为 vpc_id 创建一个变量。

variable "vpc_id" {

}

3.2.接下来,在 modules/s3/main.tf 文件中,获取当前亚马逊云科技用户账号并创建 S3 存储桶资源。


以下代码将创建两个 S3 存储桶,其中一个存储桶将存储流日志。我们在本教程中不会使用这两个存储桶,但仍会生成它们,可供您自行探索。GuardDuty 会使用流日志数据,但是您不必启用流日志,GuardDuty 也可以使用这些数据。我们之所以在这里启用这些功能,是为了便于您查看。我们创建的另一个存储桶将用于存储威胁列表。

# GET CURRENT AWS ACCOUNT NUMBER
 
data "aws_caller_identity" "current" {}

# CREATE TWO S3 BUCKETS

resource "aws_s3_bucket" "bucket" {
 bucket = "guardduty-example-${data.aws_caller_identity.current.account_id}-us-east-1"
 force_destroy = true
}

resource "aws_s3_bucket" "flow-log-bucket" {
 bucket = "vpc-flow-logs-${data.aws_caller_identity.current.account_id}-us-east-1"
 force_destroy = true
}

3.3.接下来,启用传输至 S3 存储桶的 VPC 流日志。虽然这一步不是必需的,但它可以让我们查看 GuardDuty 所检测到的日志。

# VPC FLOW LOGS
 resource "aws_flow_log" "flow_log_example" {
 log_destination = aws_s3_bucket.flow-log-bucket.arn
 log_destination_type = "s3"
 traffic_type = "ALL"
 vpc_id = var.vpc_id
 }

3.4.最后,输出 modules/s3/outputs.tf 文件中的 bucket_id 和 bucket_arn 值。

# S3 Bucket id
 output "bucket_id" {
 value = aws_s3_bucket.bucket.id
 description = "Output of s3 bucket id."
 }

 # S3 Bucket arn
 output "bucket_arn" {
 value = aws_s3_bucket.bucket.arn
 description = "Output of s3 bucket arn."
 }

3.5.现在,返回至 root/main.tf 文件并添加 S3 存储桶。

# CREATES S3 BUCKET
module "s3_bucket" {
 source = "./modules/s3"
 vpc_id = module.iac_vpc.vpc_attributes.id
}

为什么要创建 S3 存储桶?创建 S3 存储桶主要是为了存储 GuardDuty 将会引用的文本文件。如前所述,GuardDuty 可以使用两种类型的列表:受信任 IP 列表和威胁 IP 列表。我们的 S3 存储桶将存储威胁 IP 列表。次要原因是为了存储 VPC 流日志。GuardDuty 会使用流日志数据,但是您不需要启用流日志,GuardDuty 也可以使用这些数据。在这里,我们启用 VPC 流日志是为了以便日后如果需要,也可以利用这些数据在其他工具中使用。
 

至此,您共创建了两个 S3 存储桶。一个存储桶用于存储 VPC 流日志,供您自行探索。另一个将用于存储您在后面步骤中创建的威胁列表。

步骤 4:创建 GuardDuty Terraform 模块

4.1.GuardDuty 模块文件已经创建完毕,但和 S3 文件相同,它们目前是空的。从 modules/guardduty/variables.tf 文件开始。在这里,您需要创建两个变量。第一个变量名为 bucket,将用于定义 S3 存储桶中威胁列表的详细信息。第二个变量将应用于已添加至存储桶的恶意 IP。

variable "bucket" {

}

variable "malicious_ip" {

}

4.2.接下来,转至 modules/guardduty/main.tf 文件。

您需要在此文件中添加三个资源。第一个资源是 GuardDuty 检测器。阅读 Amazon Provider 文档时,您会注意到所有选项都是可选的——除了声明相应的资源之外,没有任何必选项。但是,我们的示例中会将 enabled 值设置为 true,还会将 finding_publishing_frequency 更改为 15 分钟。默认值为 1 小时。

# ENABLE THE DETECTOR
resource "aws_guardduty_detector" "gd-tutorial" {
 enable = true
 finding_publishing_frequency = "FIFTEEN_MINUTES"
}

4.3.接下来,我们将一个文件上传至上一个步骤中创建的 S3 存储桶。这不是必需的,但出于演示目的,我们需要使用其中一个 EC2 的 IP 地址来确保在本教程中生成发现。在下面的代码中,我们将一个文本文件上传至 S3 存储桶,称之为 MyThreatIntelSet,文件内容是在变量 var.malicious_ip 中找到的 IP 地址。

# ADD THE EIP/MALICIOUS IP TO THE BUCKET AS A TEXT FILE.
resource "aws_s3_object" "MyThreatIntelSet" {
 content = var.malicious_ip
 bucket = var.bucket
 key = "MyThreatIntelSet"
}

4.4.最后,我们创建 aws_guardduty_threatintelset,该资源会告诉 GuardDuty 应使用(这是 activate = true 时的情况)位于定义的 location 的文件。

# HAVE GUARDDUTY LOOK AT THE TEXT FILE IN S3
resource "aws_guardduty_threatintelset" "Example-Threat-List" {
 activate = true
 detector_id = aws_guardduty_detector.gd-tutorial.id
 format = "TXT"
 location = "https://s3.amazonaws.com/${aws_s3_object.MyThreatIntelSet.bucket}/${aws_s3_object.MyThreatIntelSet.key}"
 name = "MyThreatIntelSet"
}

对于 GuardDuty,我们此时不需要输出任何信息。

4.5.接下来,转至 root/main.tf 文件并调用 GuardDuty 模块。我们需要提供存储桶 ID 和恶意 IP。您可以看到这些信息来自 S3 模块和计算模块。

# Enable GuardDuty
module "guardduty" {
 source = "./modules/guardduty"
 bucket = module.s3_bucket.bucket_id
 malicious_ip = module.compute.malicious_ip
}

在本节中,您启用了 GuardDuty,创建了威胁列表,并在该列表中添加了恶意 EC2 实例的弹性 IP 地址。接下来,我们要创建一个 SNS 规则。

步骤 5:创建 SNS Terraform 模块

在本节中,我们将使用 Terraform 来创建一个 SNS 规则。Amazon Simple Notification Service (SNS) 使您能够在满足特定条件时发送通知。SNS 本身并不会与发送消息的操作进行匹配,我们会使用 EventBridge 来完成匹配。但是,由于 EventBridge 需要根据规则来发送通知,我们首先必须创建 SNS 规则。

5.1.从 modules/sns/variables.tf 文件开始,您需要创建两个变量:

  1. sns_name 用于命名我们创建的 SNS 主题。
  2. email 用于存储我们用于订阅通知的电子邮件地址。

下面是用于 SNS 的变量示例。

variable "sns_name" {
 description = "Name of the SNS Topic to be created"
 default = "GuardDuty-Example"
}

variable "email" {
 description = "Email address for SNS"
}

5.2.接下来我们将在 modules/sns/main.tf 文件中创建 SNS 主题和订阅。

首先创建主题资源。

# Create the SNS topic
 
 resource "aws_sns_topic" "gd_sns_topic" {
 name = var.sns_name
 }

上面的代码将创建一个资源,Terraform 将其命名为 gd_sns_topic。在亚马逊云科技控制台中,该资源名为 GuardDuty-Example。这是因为我们将调用变量 var.sns_name,而该变量默认设置为 GuardDuty-Example。

5.3.接下来创建 SNS 策略资源。所需的值为 arn 和 policy。此处创建的策略是一个亚马逊云科技 IAM 策略文档。此策略文档将授权服务主体 events.amazonaws.com 发布信息至该主题。

resource "aws_sns_topic_policy" "gd_sns_topic_policy" {
 arn = aws_sns_topic.gd_sns_topic.arn
 policy = jsonencode(
 {
 Id = "ID-GD-Topic-Policy"
 Statement = [
 {
 Action = "sns:Publish"
 Effect = "Allow"
 Principal = {
 Service = "events.amazonaws.com"
 }
 Resource = aws_sns_topic.gd_sns_topic.arn
 Sid = "SID-GD-Example"
 },
 ]
 Version = "2012-10-17"
 }
 )
 }

5.4.接下来,您将创建主题订阅。主题订阅会调用 ARN、设置要使用的协议(在本例中为电子邮件协议),并设置接收通知的电子邮件地址。本例中将在代码中固定使用此电子邮件地址,但是您可以将其配置为在应用 Terraform 后提示输入电子邮件地址。此外,将 endpoint_auto_confirm 设置为 false 意味着电子邮件所有者将收到一封包含订阅链接的邮件,点击该链接才能完成通知订阅。

# Create the topic subscription
 
 resource "aws_sns_topic_subscription" "user_updates_sqs_target" {
 topic_arn = aws_sns_topic.gd_sns_topic.arn
 protocol = "email"
 endpoint = var.email
 endpoint_auto_confirms = false
 }

5.5.接下来,在 modules/sns/outputs.tf 文件中,我们需要输出该主题的 ARN,以便在稍后执行的 EventBridge 配置中引用该 ARN。

output "sns_topic_arn" {
 value = aws_sns_topic.gd_sns_topic.arn
 description = "Output of ARN to call in the eventbridge rule."
}

5.6.最后,返回至 root/main.tf 文件并添加 SNS 主题。在这里,您将设置要订阅的电子邮件地址。


请注意,您需要在下面的代码中输入您的电子邮件地址。

# Creates an SNS Topic

 module "guardduty_sns_topic" {
 source = "./modules/sns"
 email = "youremailaddress@domain.com"
 }

在本节中,您创建了 SNS 主题,以便在生成特定 GuardDuty 发现时接收电子邮件。

步骤 6:创建 EventBridge Terraform 模块

在本节中,您将使用 Terraform 来创建一个 EventBridge 规则。该 EventBridge 规则会将此解决方案的两个要素串联起来。

EventBridge 的工作原理EventBridge 机制的基本原理是接收到一个事件,即环境变更的标志,然后遵循一定规则将事件转发至目标。规则会根据事件结构(又称事件模式)或时间表,将事件与目标进行匹配。在本例中,一旦发现发生变化,GuardDuty 就会为 Amazon EventBridge 创建一个事件。该事件会将一个 Amazon EventBridge 规则与一个目标进行匹配。在本例中,目标是一个 SNS 规则。该 SNS 规则会根据发现数据生成电子邮件通知,已订阅的用户会收到该通知。

6.1.EventBridge 需要有关 GuardDuty 和 SNS 的信息。首先创建一个可用于 SNS 主题 ARN 的变量。请在 modules/eventbridge/variables.tf 文件中完成这一操作。

variable "sns_topic_arn" {

}

6.2.接下来,在 modules/eventbridge/main.tf 文件中创建一个事件规则资源。您需要定义我们要寻找的事件来源和事件类型。

# EVENT RULE RESOURCE
resource "aws_cloudwatch_event_rule" "GuardDuty-Event-EC2-MaliciousIPCaller" {
 name = "GuardDuty-Event-EC2-MaliciousIPCaller"
 description = "GuardDuty Event: UnauthorizedAccess:EC2/MaliciousIPCaller.Custom"

 event_pattern = <<EOF
{
 "source": ["aws.guardduty"],
 "detail": {
 "type": ["UnauthorizedAccess:EC2/MaliciousIPCaller.Custom"]
 }
}
 EOF
}

上面定义的事件模式会检查来自来源服务(在本例中为 GuardDuty)的事件,在其中寻找详情类型为 UnauthorizedAccess:EC2/MaliciousIPCaller.Custom 的发现。
 

6.3.接下来,定义事件目标资源。创建该资源时,您可以通过定义 Input Transformer(输入转换器)来提高电子邮件通知的易读性。这一操作可以自定义 EventBridge 传递至事件目标的信息。在下面的代码中,我们将获取 GuardDuty ID、区域和 EC2 实例 ID,并将创建一个输入模板来详细说明消息内容。您在下面可以看到,我们已经创建了一个输入模板,该模板可以利用 GuardDuty 发现中的详细信息来生成发送的电子邮件消息内容。

# EVENT TARGET RESOURCE FOR SNS NOTIFICATIONS
 resource "aws_cloudwatch_event_target" "sns" {

 rule = aws_cloudwatch_event_rule.GuardDuty-Event-EC2-MaliciousIPCaller.name
 target_id = "GuardDuty-Example"
 arn = var.sns_topic_arn

 input_transformer {
 input_paths = {
 gdid = "$.detail.id",
 region = "$.detail.region",
 instanceid = "$.detail.resource.instanceDetails.instanceId"
 }
 input_template = "\"First GuardDuty Finding for the GuardDuty-IAC tutorial. | ID:<gdid> | The EC2 instance: <instanceid>, may be compromised and should be investigated. Go to https://console.aws.amazon.com/guardduty/home?region=<region>#/findings?macros=current&fId=<gdid>\""
 }
 }

6.4.我们创建的第一个事件规则会寻找 GuardDuty-Event-EC2-MaliciousIPCaller 事件。请创建第二个规则,用于寻找 GuardDuty-Event-IAMUser-MaliciousIPCaller 发现,以及发送相应的电子邮件通知。


这个发现不会中途出现,而是会事先为您准备好,主要用来练习创建资源。
 
# EVENT RULE RESOURCE
 resource "aws_cloudwatch_event_rule" "GuardDuty-Event-IAMUser-MaliciousIPCaller" {
 name = "GuardDuty-Event-IAMUser-MaliciousIPCaller"
 description = "GuardDuty Event: UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom"
 event_pattern = <<EOF
 {
 "source": ["aws.guardduty"],
 "detail": {
 "type": ["UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom", "Discovery:S3/MaliciousIPCaller.Custom"]
 }
 }
 EOF
 }

 #EVENT TARGET RESOURCE FOR SNS NOTIFICATIONS
 resource "aws_cloudwatch_event_target" "iam-sns" {

 rule = aws_cloudwatch_event_rule.GuardDuty-Event-IAMUser-MaliciousIPCaller.name
 target_id = "GuardDuty-Example"
 arn = var.sns_topic_arn

 input_transformer {
 input_paths = {
 gdid = "$.detail.id",
 region = "$.detail.region",
 userName = "$.detail.resource.accessKeyDetails.userName"
 } 
 input_template = "\"Second GuardDuty Finding for the GuardDuty-IAC tutorial. | ID:<gdid> | AWS Region:<region>. An AWS API operation was invoked (userName: <userName>) from an IP address that is included on your threat list and should be investigated.Go to https://console.aws.amazon.com/guardduty/home?region=<region>#/findings?macros=current&fId=<gdid>\""
 }
 }

6.5.在该模块中创建相应的资源后,回到 root/main.tf 文件并添加 EventBridge 规则。

# Create the EventBridge rule

module "guardduty_eventbridge_rule" {
 source = "./modules/eventbridge"
 sns_topic_arn = module.guardduty_sns_topic.sns_topic_arn
}

在本节中,您创建了一个 EventBridge 规则,该规则会在成功匹配 GuardDuty 发现时,使用您创建的 SNS 主题来发送电子邮件。在下一节中,您将使用 Lambda 来增强上述功能。

步骤 7:创建 Lambda Terraform 模块

在本节中,我们将使用 Terraform 来创建一个 Lambda 函数,用于执行环境的修复操作。在本教程中,我们希望将受入侵主机迁至新的安全组中。类似于 EventBridge 使用 SNS 来生成电子邮件的方式,EventBridge 需要 Lambda 函数。

注意,要实现这一点有很多方法可供选择。有关更多信息,请参阅关于利用 Amazon CloudWatch 事件来创建对 GuardDuty 发现的自定义响应以及利用 Amazon Network Firewall(网络防火墙)和 Amazon GuardDuty 来自动阻止可疑流量的文档。

在开始之前,我们先看看为此需要完成哪些操作。

目前,我们的 GuardDuty 发现会通过 EventBridge 规则与目标进行匹配,当前规则为能够发送电子邮件的 SNS 规则。为了增强此功能,我们会让 EventBridge 使用 Amazon Lambda 作为目标。

由于要让 Amazon Lambda 代表我们访问其他资源,因此我们需要创建一个 IAM 角色来对该服务进行授权。该角色称为服务角色,而 Amazon Lambda 会在更改 EC2 实例的安全组时代入该角色。

下图说明了前三个代码块如何组合在一起,让 Amazon Lambda 服务能够代入一个角色,该角色有权限对 EC2 实例分配的安全组进行更改。

7.1.首先,在 modules/lambda/main.tf 创建 IAM 策略。以下是用于该策略的信任关系。

data "aws_iam_policy_document" "GD-EC2MaliciousIPCaller-policy-document" {
 statement {
 effect = "Allow"
 actions = ["sts:AssumeRole"]
 principals {
 type = "Service"
 identifiers = ["lambda.amazonaws.com"]
 }
 }
}

7.2.接下来,创建将应用于 Amazon Lambda 所代入角色的内联策略。

resource "aws_iam_role_policy" "GD-EC2MaliciousIPCaller-inline-role-policy" {
 name = "GD-EC2MaliciousIPCaller-inline-role-policy"
 role = aws_iam_role.GD-Lambda-EC2MaliciousIPCaller-role.id

 policy = jsonencode({
 "Version" : "2012-10-17",
 "Statement" : [
 {
 "Action" : [
 "ssm:PutParameter",
 "ec2:AuthorizeSecurityGroupEgress",
 "ec2:AuthorizeSecurityGroupIngress",
 "ec2:CreateSecurityGroup",
 "ec2:DescribeSecurityGroups",
 "ec2:RevokeSecurityGroupEgress",
 "ec2:RevokeSecurityGroupIngress",
 "ec2:UpdateSecurityGroupRuleDescriptionsEgress",
 "ec2:UpdateSecurityGroupRuleDescriptionsIngress",
 "ec2:DescribeInstances",
 "ec2:UpdateSecurityGroupRuleDescriptionsIngress",
 "ec2:DescribeVpcs",
 "ec2:ModifyInstanceAttribute",
 "lambda:InvokeFunction",
 "cloudwatch:PutMetricData",
 "xray:PutTraceSegments",
 "xray:PutTelemetryRecords"
 ],
 "Resource" : "*",
 "Effect" : "Allow"
 },
 {
 "Action" : [
 "logs:*"
 ],
 "Resource" : "arn:aws:logs:*:*:*",
 "Effect" : "Allow"
 },
 {
 "Action" : [
 "sns:Publish"
 ],
 "Resource" : var.sns_topic_arn,
 "Effect" : "Allow"
 } 
 ]
 })
}

7.3.现在,创建 Amazon Lambda 将代入的 IAM 角色。

resource "aws_iam_role" "GD-Lambda-EC2MaliciousIPCaller-role" {
 name = "GD-Lambda-EC2MaliciousIPCaller-role1"
 assume_role_policy = data.aws_iam_policy_document.GD-EC2MaliciousIPCaller-policy-document.json
}

用于该 Lambda 函数的 Python 代码已经为您创建好了。在 /modules/lambda/code 中可以找到这段代码,名为 index.py。
 

7.4.创建指向这段代码的数据资源。

data "archive_file" "python_lambda_package" {
 type = "zip"
 source_file = "${path.module}/code/index.py"
 output_path = "index.zip"
}

7.5.接下来,我们需要授予 EventBridge 访问 Lambda 的权限。

resource "aws_lambda_permission" "GuardDuty-Hands-On-RemediationLambda" {
 statement_id = "GuardDutyTerraformRemediationLambdaEC2InvokePermissions"
 action = "lambda:InvokeFunction"
 function_name = aws_lambda_function.GuardDuty-Example-Remediation-EC2MaliciousIPCaller.function_name
 principal = "events.amazonaws.com"
}

上面的代码块将授权 EventBridge 调用 Lambda 函数。

7.6.最后,创建 Lambda 函数资源。为此,我们需要创建几个变量,以便通过它们传递一些信息。使用以下变量 modules/lambda/variables.tf 编辑文件:

variable "sns_topic_arn" {

}

variable "compromised_instance_id" {

}

variable "forensic_sg_id" {

}

7.7.接下来,返回至 modules/lambda/main.tf 文件并创建 Lambda 函数资源。请注意,下面的代码块中将使用 Python 3.9。此外,我们还将引用 index.zip 中压缩的 Python 代码。最后,我们将设置该资源中的几个环境变量:INSTANCE_ID、FORENSICS_SG 和 TOPIC_ARN。这些值将从我们创建的变量传递至我们的 Lambda 函数环境中。

# Create the Lambda function Resource

resource "aws_lambda_function" "GuardDuty-Example-Remediation-EC2MaliciousIPCaller" {
 function_name = "GuardDuty-Example-Remediation-EC2MaliciousIPCaller"
 filename = "index.zip"
 source_code_hash = data.archive_file.python_lambda_package.output_base64sha256
 role = aws_iam_role.GD-Lambda-EC2MaliciousIPCaller-role.arn
 runtime = "python3.9"
 handler = "index.handler"
 timeout = 10
 environment {
 variables = {
 INSTANCE_ID = var.compromised_instance_id
 FORENSICS_SG = var.forensic_sg_id
 TOPIC_ARN = var.sns_topic_arn
 }
 }
}

7.8.在 root/main.tf 文件中调用 Lambda 模块,设置 SNS 主题 ARN、失陷实例 ID 和取证安全组。请注意,这些值将来自 GuardDuty 模块、计算模块和 VPC 模块。

# CREATE THE LAMBDA FUNCTION
module "lambda" {
 source = "./modules/lambda"
 sns_topic_arn = module.guardduty_sns_topic.sns_topic_arn
 compromised_instance_id = module.compute.compromised_instance_id
 forensic_sg_id = module.forensic-security-group.security_group_id
}

7.9.现在尚未创建取证安全组。请添加用于取证的安全组。

# CREATES THE FORENSICS_SG SECURITY GROUP
module "forensic-security-group" {
 source = "terraform-aws-modules/security-group/aws"
 version = "4.17.1"

 name = "FORENSIC_SG"
 description = "Forensic Security group "
 vpc_id = module.iac_vpc.vpc_attributes.id
}

7.10.为了访问 forensic-security-group,我们需要输出它。在 root/outputs.tf 文件中输出安全组 ID。

output "forensic_sg_id" {
 value = module.forensic-security-group.security_group_id
 description = "Output of forensic sg id created - to place the EC2 instance(s)."
 }

7.11.现在,我们需要调整 EventBridge 规则,将发现数据发送至 Lambda。我们将在下一节中完成这一操作。返回至 modules/eventbridge/main.tf 文件。为 Lambda 函数添加一个事件目标资源,该资源会检查 aws_cloudwatch_event_rule.GuardDuty-Event-EC2-MaliciousIPCaller.name 规则,并将目标 ID 设置为 GuardDuty-Example-Remediation。此处需要 Lambda 函数的 ARN,这可以从 Lambda 模块输出。

#EVENT TARGET RESOURCE FOR LAMBDA REMEDIATION FUNCTION

resource "aws_cloudwatch_event_target" "lambda_function" {

 rule = aws_cloudwatch_event_rule.GuardDuty-Event-EC2-MaliciousIPCaller.name
 target_id = "GuardDuty-Example-Remediation"
 arn = var.lambda_remediation_function_arn
}

7.12.将该输出添加至 Lambda 模块 (modules/lambda/outputs.tf)。

output "lambda_remediation_function_arn" {
 value = aws_lambda_function.GuardDuty-Example-Remediation-EC2MaliciousIPCaller.arn
}

7.13.此外,在 EventBridge module 模块 (modules/eventbridge/variables.tf) 中也需要应用该变量。

variable "lambda_remediation_function_arn" {

}

7.14.最后,将 lambda_remediation_function_arn 添加至 root/main.tf 文件。该变量会进入已创建的 EventBridge 规则。下面的输出是完整的代码块,其中有部分代码已经存在。请务必仅将 lambda_remediation_function_arn = module.lambda.lambda_remediation_function_arn 代码添加至已有的代码块。

module "guardduty_eventbridge_rule" {
 source = "./modules/eventbridge"
 sns_topic_arn = module.guardduty_sns_topic.sns_topic_arn
 lambda_remediation_function_arn = module.lambda.lambda_remediation_function_arn
}

在本节中,您创建了一个 Lambda 函数,该 Lambda 函数会将受入侵主机隔离到另一个安全组中。EventBridge 规则会在成功匹配 GuardDuty 发现时调用此 Lambda 函数。在下一节中,您将应用整个配置。

步骤 8:将配置应用于您的亚马逊云科技账户

8.1.执行 terraform init。此操作将初始化本教程中您已向其添加代码的所有模块。输出应该如下所示:


请注意,如果已在您的账户中启用 GuardDuty,则应用会失败。如果发生这一情况,可以禁用 GuardDuty,然后再次运行 Terraform 应用。

8.2.执行 terraform plan。

8.3.执行 terraform apply,以将变更推送至亚马逊云科技。应用后,输出应该如下所示:

在本节中,您已将 Terraform 应用于您的亚马逊云科技账户。此时,您有两个 EC2 实例,它们彼此之间可以通信。其中一个实例是恶意实例,其 IP 地址已被添加至我们的威胁 IP 列表。GuardDuty 如果发现该 IP 与失陷实例通信,就会生成发现。我们有一个 EventBridge 规则会对该发现进行匹配,然后完成以下两个操作:第一个操作是发送电子邮件,告知我们发生的情况;第二个操作是调用 Lambda 函数,以更改受入侵主机的安全组。在下一节中,我们将在亚马逊云科技控制台中验证该配置。

步骤 9:在亚马逊云科技管理控制台中验证解决方案

在本节中,我们将在亚马逊云科技控制台中全面检验整个解决方案,并在 GuardDuty 显示发现结果以及 EventBridge 触发 Lambda 函数后,验证安全组是否已经被迁移。

您应该还收到了用于确认您已订阅的电子邮件。请记住,您必须订阅,才能接收已配置的通知。

订阅后,前往亚马逊云科技管理控制台,确认是否存在两个 EC2 实例,以及它们是否都位于初始安全组中。


根据等待时间长短,如果已应用修复 Lambda 函数,您可能不会在同一安全组中看到这两个实例。

首先检查受入侵主机。

接下来检查恶意主机。

接下来需要确保 GuardDuty 能够报告发现。

现在进行检查,确认 EventBridge 是否会寻找该发现。

接下来,检查 EventBridge 规则的目标。您应该会看到一个 SNS 目标和一个 Lambda 目标。

检查 SNS 规则以了解其作用。它应该会向您设置的地址发送电子邮件。

接下来,检查 Lambda 函数。您可以从 EventBridge 规则前往相应的位置进行检查,也可直接前往。

最后进行检查,确认 Lambda 函数已将受入侵主机迁至新的安全组中。

根据等待时间长短,如果您的配置与上述屏幕截图一致,则说明您已成功使用 Terraform 创建了一个完整的亚马逊云科技安全解决方案。恭喜您!

在下一节中,我们将清理我们的环境,并分享更多资源。

清理资源

本教程至此,您可能会每 15 分钟就会收到一封关于 GuardDuty 发现情况的电子邮件。如果您希望保留配置以供稍后参考,但不想再接收电子邮件通知,则只需取消订阅该主题即可。请查看下面的示例,了解如何删除订阅。

为了移除整个配置,我们将使用 Cloud9 实例 CLI 中的 terraform destroy 命令。完成后,输出应该如下所示:

接下来,您需要清空为 Cloud9 实例创建的 S3 存储桶。

最后,前往 CloudFormation 并删除您创建的 CloudFormation 堆栈。请务必删除此实训项目中先前由您命名的那个堆栈。当您删除父堆栈时,所创建的两个堆栈都会被删除。

总结

如本教程所示,当 GuardDuty 报告发现时,我们可以使用 Amazon EventBridge 和 Amazon Lambda 实现许多自动化处理。如需了解更多关于如何实现事件响应自动化的思路,请参阅文章《如何利用 Amazon GuardDuty 和亚马逊云科技 Web 应用程序防火墙来自动阻止可疑主机》以及《利用 Amazon Network Firewall(网络防火墙)和 Amazon GuardDuty 来自动阻止可疑流量》