Security Hub の通知を EventBridge ルールの入力トランスフォーマーで変換して SNS トピックでメール通知する構成を AWS CDK で実装してみた
こんにちは、製造ビジネステクノロジー部の若槻です。
Amazon EventBridge ルールでは、入力トランスフォーマーを利用することにより、ターゲットにイベントを送信する前にイベントの内容を変換することができます。
入力トランスフォーマーの用途としては AWS Security Hub の検出結果の通知の整形があります。検出結果のイベントは次のような Json 形式で送信されるため通知で受ける際には情報が過多となっており、また SNS トピック経由でメール送信する場合は未整形の1行の JSON が本文に載っただけのメールが通知されてしまいます。
{
"version":"0",
"id":"CWE-event-id",
"detail-type":"Security Hub Findings - Imported",
"source":"aws.securityhub",
"account":"111122223333",
"time":"2019-04-11T21:52:17Z",
"region":"us-west-2",
"resources":[
"arn:aws:securityhub:us-west-2::product/aws/macie/arn:aws:macie:us-west-2:111122223333:integtest/trigger/6294d71b927c41cbab915159a8f326a3/alert/f2893b211841"
],
"detail":{
"findings": [{
<finding content>
}]
}
}
今回はこの Security Hub の検出結果を EventBridge ルールの入力トランスフォーマーで変換して SNS トピックでメール通知する構成を AWS CDK で実装してみました。
やってみた
システム構成
CDK コード
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as events from 'aws-cdk-lib/aws-events';
export class MainStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: cdk.StackProps) {
super(scope, id, props);
const SUBSCRIPTION_EMAIL_ADDRESS =
process.env.SUBSCRIPTION_EMAIL_ADDRESS || '';
// SNS トピックを作成
const topic = new sns.Topic(this, 'Topic');
// SNS トピックにサブスクリプションを追加
topic.addSubscription(
new subscriptions.EmailSubscription(SUBSCRIPTION_EMAIL_ADDRESS)
);
/**
* SNS トピックポリシーを設定
* MEMO: SNS トピックと EventBridge ルールの連携に L1 コンストラクトを利用しており暗黙的にポリシーが設定されないため、明示的に指定が必要
*/
const snsPolicyStatement = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['sns:Publish'],
resources: [topic.topicArn],
principals: [new iam.ServicePrincipal('events.amazonaws.com')],
});
topic.addToResourcePolicy(snsPolicyStatement);
// EventBridge ルールを作成
const eventRule = new events.Rule(this, 'EventRule', {
eventPattern: {
source: ['aws.securityhub'],
/**
* 全ての Security Hub Findings を対象にする場合は以下のように設定
* @see https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cwe-integration-types.html#securityhub-cwe-integration-types-all-findings
*/
detailType: ['Security Hub Findings - Imported'],
detail: {
findings: {
Severity: {
Label: ['MEDIUM', 'CRITICAL', 'HIGH'],
},
},
},
},
});
// 入力テンプレートを定義
const inputTemplate =
'"AWS Security Hub Fingings Notification"\n' +
'\n' +
'"Account ID:"' +
'\n' +
'"<accountId>"\n' +
'\n' +
'"Finding Title:"\n' +
'"<title>"\n' +
'\n' +
'"Severity:"\n' +
'"<severity>"\n' +
'\n' +
'"Description:"\n' +
'"<description>"\n' +
'\n' +
'"Affected Resource:"\n' +
'"<resourceId>"\n' +
'\n' +
'"Security Control ID:"\n' +
'"<securityControlId>"\n';
/**
* SNS トピックを EventBridge ターゲットに設定
* MEMO: 入力トランスフォーマーは L2 コンストラクトでサポートされていないため、エスケープハッチを使用
*/
const cfnRule = eventRule.node.defaultChild as events.CfnRule;
cfnRule.addPropertyOverride('Targets', [
{
Arn: topic.topicArn,
Id: 'SecurityHubNotificationTarget',
InputTransformer: {
InputTemplate: inputTemplate,
InputPathsMap: {
accountId: '$.detail.findings[0].AwsAccountId',
title: '$.detail.findings[0].Title',
severity: '$.detail.findings[0].Severity.Label',
description: '$.detail.findings[0].Description',
resourceId: '$.detail.findings[0].Resources[0].Id',
securityControlId:
'$.detail.findings[0].Compliance.SecurityControlId',
},
},
},
]);
}
}
上記をデプロイすると SNS トピックのサブスクライブ通知が来るので Confirm します。
またデプロイで作成された EventBridge ルールの入力トランスフォーマーを確認すると、入力パスと入力テンプレートはそれぞれ次のようになっています。
{
"accountId": "$.detail.findings[0].AwsAccountId",
"description": "$.detail.findings[0].Description",
"resourceId": "$.detail.findings[0].Resources[0].Id",
"securityControlId": "$.detail.findings[0].Compliance.SecurityControlId",
"severity": "$.detail.findings[0].Severity.Label",
"title": "$.detail.findings[0].Title"
}
"AWS Security Hub Fingings Notification"
"Account ID:"
"<accountId>"
"Finding Title:"
"<title>"
"Severity:"
"<severity>"
"Description:"
"<description>"
"Affected Resource:"
"<resourceId>"
"Security Control ID:"
"<securityControlId>"
動作確認
Security Hub で指定の重要度の検出が行われると、AWS Notification Message
というサブジェクトで SNS トピックから次のようなメールが届きます。
"AWS Security Hub Fingings Notification"
"Account ID:"
"XXXXXXXXXXXX"
"Finding Title:"
"S3.1 S3 general purpose buckets should have block public access settings enabled"
"Severity:"
"MEDIUM"
"Description:"
"This control checks whether the preceding Amazon S3 block public access settings are configured at the account level for an S3 general purpose bucket. The control fails if one or more of the block public access settings are set to false."
"Affected Resource:"
"AWS::::Account:XXXXXXXXXXXX"
"Security Control ID:"
"S3.1"
入力テンプレートにダブルクォーテーションが必要?
当初、入力テンプレートを次のように文字列をダブルクォーテーションで囲まない記載としようとしました。
// 入力テンプレートを定義
const inputTemplate =
'AWS Security Hub Fingings Notification\n' +
'\n' +
'Account ID:' +
'\n' +
'<accountId>\n' +
'\n' +
'Finding Title:\n' +
'<title>\n' +
'\n' +
'Severity:\n' +
'<severity>\n' +
'\n' +
'Description:\n' +
'<description>\n' +
'\n' +
'Affected Resource:\n' +
'<resourceId>\n' +
'\n' +
'Security Control ID:\n' +
'<securityControlId>\n';
しかし上記でデプロイすると次のように Invalid InputTemplate for target SecurityHubNotificationTarget
というエラーが発生してしまいました。
npm run deploy
> cdk_sample_app@0.1.0 deploy
> cdk deploy --require-approval never --method=direct
✨ Synthesis time: 3.99s
Main: start: Building 24a13b4bb1205fd13b43d7a95abe591d373808284a3fdd3285f6d2ca6f383620:current_account-current_region
Main: success: Built 24a13b4bb1205fd13b43d7a95abe591d373808284a3fdd3285f6d2ca6f383620:current_account-current_region
Main: start: Publishing 24a13b4bb1205fd13b43d7a95abe591d373808284a3fdd3285f6d2ca6f383620:current_account-current_region
Main: success: Published 24a13b4bb1205fd13b43d7a95abe591d373808284a3fdd3285f6d2ca6f383620:current_account-current_region
Main: deploying... [1/1]
Main: updating stack...
Main | 0 | 10:21:23 PM | UPDATE_IN_PROGRESS | AWS::CloudFormation::Stack | Main User Initiated
Main | 0 | 10:21:27 PM | UPDATE_IN_PROGRESS | AWS::Events::Rule | EventRule (EventRule5A491D2C)
Main | 0 | 10:21:28 PM | UPDATE_FAILED | AWS::Events::Rule | EventRule (EventRule5A491D2C) Resource handler returned message: "Invalid InputTemplate for target SecurityHubNotificationTarget : [Source: (String)"AWS Security Hub Fingings Notification
Account ID:
null
Finding Title:
null
Severity:
null
Description:
null
Affected Resource:
null
Security Control ID:
null
"; line: 1, column: 4]. (Service: EventBridge, Status Code: 400, Request ID: ef5d7d3c-9781-474c-91c6-e889f20f6a91)" (RequestToken: 69524520-ff0a-3dc7-510c-ef7597f43290, HandlerErrorCode: GeneralServiceException)
入力トランスフォーマーの仕様でダブルクォーテーションで囲む必要があるようです。もしダブルクォーテーションで囲みたくない場合は Lambda 関数を挟むなどの対応が必要です。
おわりに
Security Hub の検出結果を EventBridge ルールの入力トランスフォーマーで変換して SNS トピックでメール通知する構成を AWS CDK で実装してみました。
Lambda 関数などを使わずとも通知内容の整形やカスタマイズをある程度行えるのは便利ですね。
参考
以上