はじめに
Security Hubのアラートを Microsoft Teams に通知する仕組みを CloudFormation テンプレートで作成しました。
以前、Security Hub で検知した内容を Amazon EventBridge 経由で Microsoft Teams に通知する仕組みの構築方法を執筆しました。
今回は、通知する仕組みをCloudFormationテンプレートで構築できるようにしましたので、紹介します。
通知される画面は、以下の通りです。
個人的にEventBridge API送信先やEventBridge 接続をテンプレートで作成することがなかったので、勉強になりました。
構成図
構成は以下の通りです。
作成されるリソース
CloudFormationで作成するリソースは、以下の通りです。
- EventBridge
- API送信先
- 接続
- ルール
- IAMロール
- IAMポリシー
テンプレートで、EventBridge API 送信先とEventBridge 接続を作成すると、以下の2点のリソースが自動作成されます
- AWS Secrets Managerのシークレット
- サービスリンクロール(
AWSServiceRoleForAmazonEventBridgeApiDestinations
)
1点目は、EventBridge 接続の情報を保存するSecrets Managerのシークレットが自動作成されます。
マネジメントコンソール上では、EventBridge 接続から自動作成されたSecrets Managerのシークレットに遷移できます。
2点目は、AWSServiceRoleForAmazonEventBridgeApiDestinations
というサービスリンクロールが自動作成されます。
EventBridgeが、Secrets Managerのシークレットを作成して保存するために、サービスリンクされたロールを使用します。
ロールには、AmazonEventBridgeApiDestinationsServiceRolePolicy
というポリシーがアタッチされています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:CreateSecret",
"secretsmanager:UpdateSecret",
"secretsmanager:DescribeSecret",
"secretsmanager:DeleteSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue"
],
"Resource": "arn:aws:secretsmanager:*:*:secret:events!connection/*"
}
]
}
テンプレート
テンプレート内容は以下の通りです
コード (クリックすると展開します)
AWSTemplateFormatVersion: '2010-09-09'
Description: ''
Parameters:
SystemPrefix:
Type: String
EnvPrefix:
Type: String
WebhookURL:
Type: String
MentionedUserMailAddress:
Description: xxx@example.com
Type: String
MentionedUserName:
Type: String
Resources:
IAMManagedPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: EventBridgePolicyForSecurityHubNotifytoTeams
Path: /service-role/
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- events:InvokeApiDestination
Resource:
- !GetAtt EventsApiDestination.Arn
IAMRole:
Type: AWS::IAM::Role
Properties:
Path: /service-role/
RoleName: !Sub ${SystemPrefix}-${EnvPrefix}-eventbridge-teams-api-dest-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sts:AssumeRole
MaxSessionDuration: 3600
ManagedPolicyArns:
- !Ref IAMManagedPolicy
EventsRule:
Type: AWS::Events::Rule
Properties:
Name: !Sub ${SystemPrefix}-${EnvPrefix}-securityhub-notify-teams
EventPattern:
detail-type:
- Security Hub Findings - Imported
source:
- aws.securityhub
detail:
findings:
Severity:
Label:
- HIGH
- CRITICAL
ProductName:
- Security Hub
State: ENABLED
Targets:
- Arn: !GetAtt EventsApiDestination.Arn
HttpParameters:
HeaderParameters: {}
QueryStringParameters: {}
Id: EventsRuleName
InputTransformer:
InputPathsMap:
AwsAccountId: '$.detail.findings[0].AwsAccountId'
FirstObservedAt: '$.detail.findings[0].FirstObservedAt'
LastObservedAt: '$.detail.findings[0].LastObservedAt'
RecommendationUrl: '$.detail.findings[0].ProductFields.RecommendationUrl'
Region: '$.detail.findings[0].Resources[0].Region'
ResourceId: '$.detail.findings[0].Resources[0].Id'
ResourceType: '$.detail.findings[0].Resources[0].Type'
SeverityLabel: '$.detail.findings[0].Severity.Label'
Title: '$.detail.findings[0].Title'
InputTemplate: !Sub |
{
"type": "message",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"text": "\u003cat\u003e${MentionedUserName}\u003c/at\u003e",
"weight": "bolder",
"size": "medium"
},
{
"type": "TextBlock",
"text": "SecurityHubで重大度<SeverityLabel>のアラートを検知しました",
"size": "Large",
"weight": "Bolder"
},
{
"type": "Table",
"columns": [
{
"width": 1
},
{
"width": 2
}
],
"rows": [
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "タイトル",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "<Title>"
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "重要度",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "<SeverityLabel>"
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "修復手順",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "[こちらをクリック](<RecommendationUrl>)"
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "アカウントID",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "<AwsAccountId>"
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "リージョン",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "<Region>"
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "リソースタイプ",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "<ResourceType>"
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "リソースID",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "<ResourceId>"
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "初回検出日時",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "<FirstObservedAt>"
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "最終検出日時",
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "<LastObservedAt>"
}
]
}
]
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"msteams": {
"width": "full",
"entities": [
{
"type": "mention",
"text": "\u003cat\u003e${MentionedUserName}\u003c/at\u003e",
"mentioned": {
"id": "${MentionedUserMailAddress}",
"name": "${MentionedUserName}"
}
}
]
}
}
}
]
}
RoleArn: !GetAtt IAMRole.Arn
EventBusName: default
EventsConnection:
Type: AWS::Events::Connection
Properties:
Name: !Sub ${SystemPrefix}-${EnvPrefix}-teams-conn
AuthorizationType: API_KEY
AuthParameters:
ApiKeyAuthParameters:
ApiKeyName: Content-Type
ApiKeyValue: application/json
EventsApiDestination:
Type: AWS::Events::ApiDestination
Properties:
Name: !Sub ${SystemPrefix}-${EnvPrefix}-teams-api-dest
ConnectionArn: !GetAtt EventsConnection.Arn
InvocationEndpoint: !Ref WebhookURL
HttpMethod: POST
InvocationRateLimitPerSecond: 300
今回のテンプレートでは、Teamsに通知される画面は表形式です。
表形式ではなく箇条書きにしたい場合、前回の記事でEventBridgeの入力トランスフォーマーのテンプレートや入力パスを様々紹介していますので、ご参照ください。
CloudFormation スタック作成
パラメータは、以下の値を入れます。
- SystemPrefix
- システム名
- EnvPrefix
- 環境名
- WebhookURL
- TeamsのWebhookURL(取得方法は、こちらのブログをご参照ください。)
- MentionedUserMailAddress
- メンション先のメールアドレス
- MentionedUserName
- メンション先の名前
1分半でデプロイが完了しました。
通知の確認
Security Hubのアラートは、以下のように通知されました。
ただし、数回試したところ、同じ通知が重複することが確認されました。
これは、 EventBridgeで起こり得るとAWSドキュメントに記載がありました。
まれに、単一のイベントまたはスケジュールされた期間に対して同じルールが複数回実行されたり、トリガーされる特定のルールに対して同じターゲットを複数回呼び出されたりする場合があります。 引用
重複させない方法もあるようですが、通知の仕組み自体は、重複してもさして問題ないと思います。気になる方は以下もご確認ください。
最後に
本記事では、Security Hubのアラートを Microsoft Teams に通知する仕組みを CloudFormation テンプレートで作成する方法を紹介しました。
複数のアカウントに通知する場合や、アカウントごとにメンション先を変更する必要がある場合に参考になれば幸いです。