Security Hub の通知を EventBridge ルールの入力トランスフォーマーで変換して SNS トピックでメール通知する構成を AWS CDK で実装してみた

Security Hub の通知を EventBridge ルールの入力トランスフォーマーで変換して SNS トピックでメール通知する構成を AWS CDK で実装してみた

Clock Icon2025.01.22

こんにちは、製造ビジネステクノロジー部の若槻です。

Amazon EventBridge ルールでは、入力トランスフォーマーを利用することにより、ターゲットにイベントを送信する前にイベントの内容を変換することができます。

https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-transform-target-input.html

入力トランスフォーマーの用途としては AWS Security Hub の検出結果の通知の整形があります。検出結果のイベントは次のような Json 形式で送信されるため通知で受ける際には情報が過多となっており、また SNS トピック経由でメール送信する場合は未整形の1行の JSON が本文に載っただけのメールが通知されてしまいます。

https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cwe-event-formats.html

{
   "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 コード

lib/main-stack.ts
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 関数などを使わずとも通知内容の整形やカスタマイズをある程度行えるのは便利ですね。

参考

https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-transform-target-input.html
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_events.CfnRule.InputTransformerProperty.html
https://dev.classmethod.jp/articles/cdk-python-eventbridge-transformer/
https://dev.classmethod.jp/articles/event-notification-from-securityhub-easier-to-see/
https://blog.mmmcorp.co.jp/2024/08/14/security_hub_notifications_without_lambda/

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.