はじめに
こんにちは。
AWSアカウントでGuardDutyを有効化した際、イベント発生時にメールなどで通知させる構成を構築することが最近多かったのですが、設定する内容はそれぞれのAWSアカウントで変わるものでもないので、CDKでコード化してしまえたら今後楽になるのではないかな?と思いました。
ということで今回はAWSアカウント上でGuardDutyを有効にし、GuardDutyで発生したイベントをEventBridge経由でSNSにメッセージを送り、Eメール通知させる構成をCDKで実装していこうと思います。
GuardDutyとは
以下公式ドキュメントから引用します。
Amazon GuardDuty は継続的なセキュリティモニタリングサービスで、以下を分析して処理します。データソース: VPC フローログ、AWS CloudTrail管理イベントログ、CloudTrail S3 データイベントログ、DNS ログ。悪意のある IP アドレスやドメインのリストなどの脅威インテリジェンスフィードおよび Machine Learning を使用して、お客様の AWS 環境内での予期しない、および潜在的に未許可で悪意のあるアクティビティを識別します。
セキュリティに関するAWSサービスとなっているので、様々な案件で有効にしている場合が多いように感じます。
構成
以下の構成で実装を行っていきます。
①GuardDutyから発行されたイベントをEventBridgeにて検知。
②GuardDutyからのイベント発行をトリガーにSNSトピックを実行。
③SNSトピックで登録したメールアドレス宛にGuardDutyで発行されたイベントのメール通知を行う。
構成に関しては以下のサイトを参考にしました。
参考1:Amazon CloudWatch Events を使用した GuardDuty の結果に対するカスタムレスポンスの作成
参考2:GuardDutyの通知が重要度でフィルター可能になりました
実装
CDKコード作成
CDK実行環境の用意については以前の記事を参考にご用意お願いします。
実行環境の用意が完了したら、作業フォルダ内で以下のファイルの作成を行います。
#!/usr/bin/env python3
from aws_cdk import core
from cdk_workshop.guardduty_notifications import GuardDutyNotifications
app = core.App()
GuardDutyNotifications(app, "guardduty-notifications")
app.synth()
from aws_cdk import (
core,
aws_sns as sns,
aws_sns_subscriptions as subs,
aws_events as events,
aws_events_targets as targets,
aws_guardduty as guardduty,
aws_iam as iam,
aws_kms as kms,
)
class GuardDutyNotifications(core.Stack):
"""
GuardDuty有効化、snsトピック・EventBridgeルールの作成
"""
def __init__(self,
scope: core.Construct,
construct_id: str,
**kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
### ---GuardDuty有効化---
guardduty_enable = guardduty.CfnDetector(self, "GuardDuty",
enable=True
)
### ---KMSカスタマー管理型キー作成---
### ※SNSトピック暗号化する場合は作成
kms_custom_policy = iam.PolicyDocument(
statements=[
iam.PolicyStatement(
actions=["kms:*"],
principals=[iam.AccountRootPrincipal()],
resources=["*"]),
iam.PolicyStatement(
actions=["kms:Decrypt", "kms:GenerateDataKey"],
principals=[iam.ServicePrincipal("events.amazonaws.com")],
resources=["*"])
]
)
sns_key = kms.Key(self, "Key",
policy=kms_custom_policy,
removal_policy=core.RemovalPolicy.DESTROY #スタックが削除されたときにKMSキーも削除されるようにする
)
key_alias = kms.Alias(self, "Alias",
alias_name="alias/guardduty-notifications-key",
target_key=sns_key
)
### ---SNSトピック作成---
sns_topic=sns.Topic(self, "guardduty-notifications-snstopic",
display_name="guardduty-notifications-snstopic",
topic_name="guardduty-notifications-snstopic",
master_key=sns_key
)
sns_topic.add_subscription(subs.EmailSubscription('{メールアドレス}'))
#複数のメールアドレスを設定する場合は上の1行を下にコピーしてそれぞれ設定するアドレスを入力する。
### ---EventBridgeルール作成---
event_bridge_rule=events.Rule(self,"guardduty-notifications-eventrule",
event_pattern={
"source": ["aws.guardduty"],
"detail_type": ["GuardDuty Finding"],
"detail": {
"severity": [
{ "numeric": [ ">=", 4 ] } #重要度中・高のみ通知するようにフィルタリング
]
}
},
rule_name="guardduty-notifications-eventrule"
)
event_bridge_rule.add_target(target=targets.SnsTopic(sns_topic,
message=events.RuleTargetInput.from_multiline_text(
f"AWS {events.EventField.from_path('$.detail.accountId')} has a severity {events.EventField.from_path('$.detail.severity')} GuardDuty finding type {events.EventField.from_path('$.detail.type')} in the {events.EventField.from_path('$.region')} region.\n"
f"Resource_Type:{events.EventField.from_path('$.detail.resource.resourceType')}\n"
f"Finding Description:{events.EventField.from_path('$.detail.description')}\n"
f"For more details open the GuardDuty console at https://{events.EventField.from_path('$.region')}.console.aws.amazon.com/guardduty/home?region={events.EventField.from_path('$.region')}#/findings?search=id%3D{events.EventField.from_path('$.detail.id')}¯os=current\n"
)
)
)
※SNSトピックを暗号化する場合のコードとなります。
※※GuardDutyのイベントの重要度中・高のみ通知するようにEventBridgeの方で設定しています。
デプロイ
cdk ls
コマンドを実行し、コマンドプロンプト上でスタック名を確認しておきます。
(.venv) C:\Users\user\Documents\cdk-workshop>cdk ls
guardduty-notifications
cdk synth
コマンドを実行し、デプロイされるテンプレートが想定通りになっているか確認します。
(.venv) C:\Users\user\Documents\cdk-workshop>cdk synth guardduty-notifications
Resources:
GuardDuty:
Type: AWS::GuardDuty::Detector
Properties:
Enable: true
Metadata:
aws:cdk:path: guardduty-notifications/GuardDuty
Key961B73FD:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Statement:
- Action: kms:*
Effect: Allow
Principal:
AWS:
Fn::Join:
- ""
- - "arn:"
- Ref: AWS::Partition
- ":iam::"
- Ref: AWS::AccountId
- :root
Resource: "*"
- Action:
- kms:Decrypt
- kms:GenerateDataKey
Effect: Allow
Principal:
Service: events.amazonaws.com
Resource: "*"
Version: "2012-10-17"
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
Metadata:
aws:cdk:path: guardduty-notifications/Key/Resource
Alias325C5727:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/guardduty-notifications-key
TargetKeyId:
Fn::GetAtt:
- Key961B73FD
- Arn
Metadata:
aws:cdk:path: guardduty-notifications/Alias/Resource
guarddutynotificationssnstopic72E6F5F9:
Type: AWS::SNS::Topic
Properties:
DisplayName: guardduty-notifications-snstopic
KmsMasterKeyId:
Fn::GetAtt:
- Key961B73FD
- Arn
TopicName: guardduty-notifications-snstopic
Metadata:
aws:cdk:path: guardduty-notifications/guardduty-notifications-snstopic/Resource
guarddutynotificationssnstopic{メールアドレス}C3B8772B:
Type: AWS::SNS::Subscription
Properties:
Protocol: email
TopicArn:
Ref: guarddutynotificationssnstopic72E6F5F9
Endpoint: {メールアドレス}
Metadata:
aws:cdk:path: guardduty-notifications/guardduty-notifications-snstopic/{メールアドレス}/Resource
guarddutynotificationssnstopicPolicy062F9F6E:
Type: AWS::SNS::TopicPolicy
Properties:
PolicyDocument:
Statement:
- Action: sns:Publish
Effect: Allow
Principal:
Service: events.amazonaws.com
Resource:
Ref: guarddutynotificationssnstopic72E6F5F9
Sid: "0"
Version: "2012-10-17"
Topics:
- Ref: guarddutynotificationssnstopic72E6F5F9
Metadata:
aws:cdk:path: guardduty-notifications/guardduty-notifications-snstopic/Policy/Resource
guarddutynotificationseventrule3F18B821:
Type: AWS::Events::Rule
Properties:
EventPattern:
detail:
severity:
- numeric:
- ">="
- 4
detail-type:
- GuardDuty Finding
source:
- aws.guardduty
Name: guardduty-notifications-eventrule
State: ENABLED
Targets:
- Arn:
Ref: guarddutynotificationssnstopic72E6F5F9
Id: Target0
InputTransformer:
InputPathsMap:
detail-accountId: $.detail.accountId
detail-severity: $.detail.severity
detail-type: $.detail.type
region: $.region
detail-resource-resourceType: $.detail.resource.resourceType
detail-description: $.detail.description
detail-id: $.detail.id
InputTemplate: |-
"AWS <detail-accountId> has a severity <detail-severity> GuardDuty finding type <detail-type> in the <region> region."
"Resource_Type:<detail-resource-resourceType>"
"Finding Description:<detail-description>"
"For more details open the GuardDuty console at https://<region>.console.aws.amazon.com/guardduty/home?region=<region>#/findings?search=id%3D<detail-id>¯os=current"
""
Metadata:
aws:cdk:path: guardduty-notifications/guardduty-notifications-eventrule/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Analytics: v2:deflate64:H4sIAAAAAAAA/1VPQQ7CMAx7C/fSgRAHbiC4cZk2PjCyAGFbi9oUNFX9O80mIXGyHcdWstbrzVavFvvm45fQdkUE61DHmhvo1NEazy4Aq+PNVOhtcIDCs9ESkzVJSTDeQ+PaNvCos3lCRmDrVDd4Hc84SkLg0FPjRUwkKW+yf7EvAhnOpA5XD45eUi7TPz2tlLYnGH+JWSaFbzSc+6rQTycKppRUOfLDmmKjd/nNpydaumCYBtTVjF8Py9wrAwEAAA==
Metadata:
aws:cdk:path: guardduty-notifications/CDKMetadata/Default
Condition: CDKMetadataAvailable
Conditions:
CDKMetadataAvailable:
{~~中略~~}
テンプレートが想定通り出力されることが確認できたため、cdk deploy
コマンドを実行してAWS環境にデプロイします。
(.venv) C:\Users\user\Documents\cdk-workshop>cdk deploy guardduty-notifications
? Synthesis time: 23.18s
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:
IAM Statement Changes
┌───┬──────────────────────────────┬────────┬──────────────────────────────┬──────────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼──────────────────────────────┼────────┼──────────────────────────────┼──────────────────────────────┼───────────┤
│ + │ ${Key.Arn} │ Allow │ kms:* │ AWS:arn:${AWS::Partition}:ia │ │
│ │ │ │ │ m::${AWS::AccountId}:root │ │
│ + │ ${Key.Arn} │ Allow │ kms:Decrypt │ Service:events.amazonaws.com │ │
│ │ │ │ kms:GenerateDataKey │ │ │
├───┼──────────────────────────────┼────────┼──────────────────────────────┼──────────────────────────────┼───────────┤
│ + │ ${guardduty-notifications-sn │ Allow │ sns:Publish │ Service:events.amazonaws.com │ │
│ │ stopic} │ │ │ │ │
└───┴──────────────────────────────┴────────┴──────────────────────────────┴──────────────────────────────┴───────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Do you wish to deploy these changes (y/n)? y
guardduty-notifications: deploying...
guardduty-notifications: creating CloudFormation changeset...
guardduty-notifications | 0/9 | 19:38:24 | REVIEW_IN_PROGRESS | AWS::CloudFormation::Stack | guardduty-notifications User Initiated
guardduty-notifications | 0/9 | 19:38:29 | CREATE_IN_PROGRESS | AWS::CloudFormation::Stack | guardduty-notifications User Initiated
guardduty-notifications | 0/9 | 19:38:34 | CREATE_IN_PROGRESS | AWS::KMS::Key | Key (Key961B73FD)
guardduty-notifications | 0/9 | 19:38:34 | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata)
guardduty-notifications | 0/9 | 19:38:35 | CREATE_IN_PROGRESS | AWS::GuardDuty::Detector | GuardDuty
guardduty-notifications | 0/9 | 19:38:36 | CREATE_IN_PROGRESS | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata) Resource creation Initiated
guardduty-notifications | 1/9 | 19:38:36 | CREATE_COMPLETE | AWS::CDK::Metadata | CDKMetadata/Default (CDKMetadata)
guardduty-notifications | 1/9 | 19:38:36 | CREATE_IN_PROGRESS | AWS::KMS::Key | Key (Key961B73FD) Resource creation Initiated
guardduty-notifications | 1/9 | 19:39:03 | CREATE_IN_PROGRESS | AWS::GuardDuty::Detector | GuardDuty Resource creation Initiated
guardduty-notifications | 2/9 | 19:39:03 | CREATE_COMPLETE | AWS::GuardDuty::Detector | GuardDuty
2/9 Currently in progress: guardduty-notifications, Key961B73FD
guardduty-notifications | 3/9 | 19:40:38 | CREATE_COMPLETE | AWS::KMS::Key | Key (Key961B73FD)
guardduty-notifications | 3/9 | 19:40:41 | CREATE_IN_PROGRESS | AWS::KMS::Alias | Alias (Alias325C5727)
guardduty-notifications | 3/9 | 19:40:42 | CREATE_IN_PROGRESS | AWS::SNS::Topic | guardduty-notifications-snstopic (guarddutynotificationssnstopic72E6F5F9)
guardduty-notifications | 3/9 | 19:40:42 | CREATE_IN_PROGRESS | AWS::SNS::Topic | guardduty-notifications-snstopic (guarddutynotificationssnstopic72E6F5F9) Resource creation Initiated
guardduty-notifications | 3/9 | 19:40:43 | CREATE_IN_PROGRESS | AWS::KMS::Alias | Alias (Alias325C5727) Resource creation Initiated
guardduty-notifications | 4/9 | 19:40:53 | CREATE_COMPLETE | AWS::SNS::Topic | guardduty-notifications-snstopic (guarddutynotificationssnstopic72E6F5F9)
guardduty-notifications | 4/9 | 19:40:55 | CREATE_IN_PROGRESS | AWS::SNS::Subscription | guardduty-notifications-snstopic/{メールアドレス} (guarddutynotificationssnstopicrisasomeyaarijpcomC3B8772B)
guardduty-notifications | 4/9 | 19:40:55 | CREATE_IN_PROGRESS | AWS::SNS::TopicPolicy | guardduty-notifications-snstopic/Policy (guarddutynotificationssnstopicPolicy062F9F6E)
guardduty-notifications | 4/9 | 19:40:55 | CREATE_IN_PROGRESS | AWS::Events::Rule | guardduty-notifications-eventrule (guarddutynotificationseventrule3F18B821)
guardduty-notifications | 4/9 | 19:40:55 | CREATE_IN_PROGRESS | AWS::SNS::TopicPolicy | guardduty-notifications-snstopic/Policy (guarddutynotificationssnstopicPolicy062F9F6E) Resource creation Initiated
guardduty-notifications | 5/9 | 19:40:55 | CREATE_COMPLETE | AWS::SNS::TopicPolicy | guardduty-notifications-snstopic/Policy (guarddutynotificationssnstopicPolicy062F9F6E)
guardduty-notifications | 5/9 | 19:40:56 | CREATE_IN_PROGRESS | AWS::SNS::Subscription | guardduty-notifications-snstopic/{メールアドレス} (guarddutynotificationssnstopicrisasomeyaarijpcomC3B8772B) Resource creation Initiated
guardduty-notifications | 5/9 | 19:40:56 | CREATE_IN_PROGRESS | AWS::Events::Rule | guardduty-notifications-eventrule (guarddutynotificationseventrule3F18B821) Resource creation Initiated
guardduty-notifications | 6/9 | 19:40:56 | CREATE_COMPLETE | AWS::SNS::Subscription | guardduty-notifications-snstopic/{メールアドレス} (guarddutynotificationssnstopicrisasomeyaarijpcomC3B8772B)
6/9 Currently in progress: guardduty-notifications, Alias325C5727, guarddutynotificationseventrule3F18B821
guardduty-notifications | 7/9 | 19:41:44 | CREATE_COMPLETE | AWS::KMS::Alias | Alias (Alias325C5727)
guardduty-notifications | 8/9 | 19:41:56 | CREATE_COMPLETE | AWS::Events::Rule | guardduty-notifications-eventrule (guarddutynotificationseventrule3F18B821)
guardduty-notifications | 9/9 | 19:41:58 | CREATE_COMPLETE | AWS::CloudFormation::Stack | guardduty-notifications
? guardduty-notifications
? Deployment time: 218.23s
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:{アカウントID}:stack/guardduty-notifications/37e08ef0-9ba7-11ec-a9fa-0e64e1439df3
? Total time: 241.41s
(.venv) C:\Users\user\Documents\cdk-workshop>
正常に実行が完了しました。次に動作確認を行っていきます。
動作確認
デプロイしたことで、SNSサブスクリプション登録時の認証メールが、設定したメールアドレス宛に届きますので認証しておきます。
認証が完了しました。
GuardDutyでサンプルイベントを作成して、登録したメールアドレス宛に通知が届くかどうか確認してみます。
サンプルイベントが作成できました。
メールの方も確認してみます。
EventBridgeで設定した通りの文面でメールが届いていることが確認できました。
以上で動作確認は完了です。
さいごに
今回はGuardDutyのイベント通知に関しての構成をCDKで作成してみました。
テンプレート化してしまえば複数の案件で同様の構成を構築する際の工数が削減できるので、ご入用の方は是非使ってみてください!