GuardDuty 全リージョン分の検出結果を EventBridge で集約してからメール通知する CloudFormation テンプレートの紹介
全リージョンで有効化された 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個を全リージョンで使用します。
折りたたみ
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 ルールです。
折りたたみ
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 集約してからのメール通知の構成・実装(コード)例が見つからなかったので載せました。参考になる部分があれば幸いです。