この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
全リージョンで有効化された GuardDuty の検出結果を EventBridge で集約してからメール通知する機会がありました。 メールアドレスの登録とメッセージ内容の整形、検出結果の重要度フィルタリングを一箇所にまとめたかったため、EventBridge で GuardDuty の検出結果を集約してからメール通知するテンプレートを紹介します。
今回構築したい通知構成
検知時に届くメール本文
今回作成した Cloudformation テンプレートは以下に置いてあります。
bigmuramura/cfn-guardduty-findings-sns
従来の一般的な通知構成
各リージョンに SNS の設定が必要でした。また、通知レベルを重要度でフィルタリングしたい場合も各リージョン毎に設定が必要でした。
2021年4月 EventBridge のアップデートにより今回構築したい通知構成を取れるようになりました。
カスタムイベントバスのイベントルールを挟むことで各リージョンに散っていた設定を、単一リージョンに集約することができます。
テンプレートの実装範囲
通知に関する部分のみを構築します。
GuardDuty の有効化は以下のリンクを参考にしてください。
SNS へメールアドレスの登録までは含んでおりませんが、ご利用される環境によっては以下の対応もご検討ください。
GuardDuty の通知類似構成
本構成以外の通知実装例です。Slack 通知など実装したい構成があれば以下のリンクを参考にしてください。
- 一発でGuardDutyを全リージョン有効化して通知設定するテンプレート作った | DevelopersIO
- CloudFormation一撃でGuardDutyを有効にして、通知をLambdaでイイ感じに編集してSNSへ通知するテンプレート作った | DevelopersIO
- [CDK]全リージョンのGuardDutyを有効にしてSNS+AWS ChatbotでSlack通知する構成 | DevelopersIO
- 【全リージョン対応】EventBridge + SNS + Chatbotで GuardDutyの結果を Slackチャンネルに通知する | DevelopersIO
本構成のテンプレート紹介
以下の要素を組み合わせテンプレートになっています。各要素を確認したい場合は以下のリンクを参考にしてください。
- EventBridge で通知を集約
- GuardDuty のメッセージ整形
- GuardDuty 重要度フィルター設定
テンプレート
1リージョンに展開し EventBridge で集約してから SNS へ通知と、GuardDuty のメッセージの整形、重要度フィルター設定込みテンプレートです。イベントルールに設定する IAM ロールは便宜上、個別に図に記しましたが、実際は同じ IAM ロールを1個を全リージョンで使用します。
折りたたみ
1-eventbridge-receiver-with-sns.yml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
ProjectName:
Description: Project Name
Type: String
Default: unnamed
Resources:
# ------------------------------------------------------------#
# EventBridge
# ------------------------------------------------------------#
# Custom Event Bus
EventsEventBus1:
Type: "AWS::Events::EventBus"
Properties:
Name: !Sub ${ProjectName}-eventbus-receiver
# Event Rule
EventsRule1:
Type: "AWS::Events::Rule"
Properties:
Name: !Sub ${ProjectName}-receiver-rule
EventBusName: !Ref EventsEventBus1
Targets:
- Arn: !Ref SNSTopic1
Id: "NotificationDestinationTopic"
InputTransformer:
InputPathsMap:
Account_ID: "$.detail.accountId"
Finding_ID: "$.detail.id"
Finding_Type: "$.detail.type"
Finding_description: "$.detail.description"
Resource_Type: "$.detail.resource.resourceType"
region: "$.region"
severity: "$.detail.severity"
InputTemplate: |
"AWS <Account_ID> has a severity <severity> GuardDuty finding type <Finding_Type> in the <region> region."
"Resource_Type:<Resource_Type>"
"Finding Description:"
"<Finding_description>. "
"For more details open the GuardDuty console at https://<region>.console.aws.amazon.com/guardduty/home?region=<region>#/findings?search=id%3D<Finding_ID>¯os=current"
EventPattern: |
{
"source": [
"aws.guardduty"
],
"detail-type": [
"GuardDuty Finding"
],
"detail": {
"severity": [
{ "numeric": [ ">=", 0 ] }
]
}
}
# ------------------------------------------------------------#
# SNS
# ------------------------------------------------------------#
SNSTopic1:
Type: "AWS::SNS::Topic"
Properties:
DisplayName: !Sub ${ProjectName}-receiver-topic
TopicName: !Sub ${ProjectName}-receiver-topic
SNSTopicPolicy1:
Type: "AWS::SNS::TopicPolicy"
Properties:
Topics:
- !Ref SNSTopic1
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "EventBridgeRule"
Effect: "Allow"
Principal:
Service: "events.amazonaws.com"
Action: "sns:Publish"
Resource: !Ref SNSTopic1
# ------------------------------------------------------------#
# Sender for IAM Role
# ------------------------------------------------------------#
IAMRole1:
Type: "AWS::IAM::Role"
Properties:
Path: "/service-role/"
RoleName: !Sub ${ProjectName}-eventbridge-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "events.amazonaws.com"
Action: "sts:AssumeRole"
MaxSessionDuration: 3600
ManagedPolicyArns:
- !Ref IAMManagedPolicy1
IAMManagedPolicy1:
Type: "AWS::IAM::ManagedPolicy"
Properties:
ManagedPolicyName: !Sub ${ProjectName}-eventbridge-policy
Path: "/service-role/"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "events:PutEvents"
Resource:
- !GetAtt EventsEventBus1.Arn
Outputs:
ReceiverCustomEventBusARNOutput:
Description: "This value is used in the stack set parameters."
Value: !GetAtt EventsEventBus1.Arn
Export:
Name: !Sub ${AWS::StackName}-ReceiverCustomEventBusARN
SenderRoleARNOutput:
Description: "This value is used in the stack set parameters."
Value: !GetAtt IAMRole1.Arn
Export:
Name: !Sub ${AWS::StackName}-SenderRoleARN
構築されるリソース
GuardDuty が有効な全リージョンに展開し、集約する側の EventBridge へ GuardDuty の検知情報をすべて転送する EventBridge ルールです。
折りたたみ
2-eventbridge-sender.yml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
ProjectName:
Description: Project Name
Type: String
Default: unnamed
ReceiverCustomEventBusARN:
Description: "Enter the value of the output of the receiver stack"
Type: String
Default: arn:aws:events:ap-northeast-1:123456789012:event-bus/project-eventbus-receiver
SenderRoleARN:
Description: "Enter the value of the output of the receiver stack"
Type: String
Default: arn:aws:iam::123456789012:role/service-role/project-eventbridge-role
Resources:
# ------------------------------------------------------------#
# EventBridge
# ------------------------------------------------------------#
# Event Rule
EventsRule1:
Type: "AWS::Events::Rule"
Properties:
Name: !Sub ${ProjectName}-sender-rule
EventBusName: "default"
Targets:
- Arn: !Ref ReceiverCustomEventBusARN
Id: "EventBridgeSender"
RoleArn: !Ref SenderRoleARN
EventPattern: |
{
"source": [
"aws.guardduty"
],
"detail-type": [
"GuardDuty Finding"
]
}
構築されるリソース
全リージョンに EventBridge のルールをデプロイ
全リージョンにデプロイするテンプレート(2-eventbridge-sender.yml
)はスタックセットを利用します。実行方法は以下で説明されておりますのでご確認ください。
1個目のテンプレート(1-eventbridge-receiver-with-sns.yml
)の実行結果をパラメータとして2個目のテンプレート(2-eventbridge-sender.yml
)に与えてスタックセットで実行することを想定しています。
通知結果
SNS にトピックにはプロトコル Eメール指定でメールアドレスをを登録します。
通知テストのために GuardDuty のサンプルイベントを生成しました。以下の様なメールが届けば成功です。
おわりに
GuardDuty + EventBridge 集約してからのメール通知の構成・実装(コード)例が見つからなかったので載せました。参考になる部分があれば幸いです。