Security Hubの検知をグループ化したカスタムインサイトを作成し、インサイトの情報を取得してメールを送信するLambda関数をEventBridgeで定期実行してみた

Security Hubの検知をグループ化したカスタムインサイトを作成し、インサイトの情報を取得してメールを送信するLambda関数をEventBridgeで定期実行してみた

Clock Icon2023.04.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

たぬき( @tanuki_tzp )です。

突然ですが、Security Hub の検知をグループ化したカスタムインサイトの情報を取得して、SNS トピックに送信する Lambda 関数を作成し、EventBridge で毎日定時に送ってもらうことにしました。

前提

  • Security Hub 有効化済み
  • SNS トピック作成済み

今回説明しないこと

  • Security Hub の有効化について
  • SNS トピックの作成と認証について
  • EventBridge のスケジュール方式について

構成図

手順

下記の順で作成していきます。

Security Hub

カスタムインサイトの作成

Security Hub のメニューバーから、インサイトを選択し、「インサイトを作成する」を押してください。

今回は、重要度ラベル毎に集計した結果を見たいと思うので、デフォルトの条件に「グループ化条件:重要度ラベル」を設定しています。
検知件数が多くて二度見してしまいましたが、今回に限っては通知が確認しやすい、ばらけている数字なので許してください。 ブログを書き終えたら即対応します。

わかりやすい名前をつけて、インサイトを作成します。インサイト名は次の工程で使います。

カスタムインサイト ARN の確認

コンソール左下から CloudShell を起動し、下記の AWS CLI コマンドを打つと、カスタムインサイトの ARN が確認できます。
Lambda 関数の環境変数として設定するため、メモしておきましょう。

aws securityhub get-insights
  • 実行結果
aws securityhub get-insights
{
    "Insights": [
        {
            "InsightArn": "arn:aws:securityhub:ap-northeast-1:123456789012:insight/123456789012/custom/xxxx-xxxx-xxxx",
            "Name": "test_securityhub_custom_insight",
            "Filters": {
                "WorkflowStatus": [
                    {
                        "Value": "NEW",
                        "Comparison": "EQUALS"
                    },
                    {
                        "Value": "NOTIFIED",
                        "Comparison": "EQUALS"
                    }
                ],
                "RecordState": [
                    {
                        "Value": "ACTIVE",
                        "Comparison": "EQUALS"
                    }
                ]
            },
            "GroupByAttribute": "SeverityLabel"
        }
    ]
}

Lambda 関数

IAM ロールの作成

Lambda 関数 にアタッチする IAM ロールが必要なため、先にロールを作成しておきます。

今回は最小限の許可ポリシーにしたいため、ポリシーも作成します。

IAM に移動し、メニューバーから「ポリシー」を開き、「ポリシーを作成」を押し、下記の JSON を貼り付けてください。
[SNS トピックの ARN]は、あらかじめ作っておいた SNS トピックの ARN に置き換えてください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "securityhub:GetInsightResults"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "sns:Publish"
            ],
            "Resource": [
                "[SNS トピックの ARN]"
            ]
        }
    ]
}

確認画面で最小限のポリシーになっていることを確認したら、任意の名前を付けてポリシーの作成をしてください。

次に、サービスメニューから「ポリシー」を開き、「ロールの作成」から、信頼されたエンティティタイプを「AWS のサービス」、ユースケースを「Lambda」を選択して「次へ」を押してください。

先ほど作ったポリシーを選択し、「次へ」を押し、任意の名前を付けてロールを作成してください。

Lambda 関数の作成

Lambda のメニューバーから、「関数」を押し、「関数の作成」を押してください。

Lambda 関数の名前は適宜付けてください。
今回は Python 3.9 で動作するコードを作成しました。
実行ロールは先ほど作成した IAM ロールを指定してください。

作成された関数の、コードソースに下記を記載してください。

import boto3
import json
import os

def lambda_handler(event, context):
    # 検索条件として使用するインサイトのARNをLambda関数の環境変数から取得
    insight_arn = os.environ['INSIGHT_ARN']
    
    # SNSトピックのARNと、FromアドレスとToアドレスをLambda関数の環境変数から取得
    topic_arn = os.environ['SNS_TOPIC_ARN']
    
    # AWS リージョンを指定
    region = 'ap-northeast-1'
    
    # SecurityHub クライアントを作成
    client = boto3.client('securityhub')

    # インサイトに含まれるリソースを取得
    findings = []
    response = client.get_insight_results(InsightArn=insight_arn)
    while response.get('NextToken'):
        findings.extend(response['InsightResults']['ResultValues'])
        response = client.get_insight_results(InsightArn=insight_arn, NextToken=response['NextToken'])
    findings.extend(response['InsightResults']['ResultValues'])
    
    # 結果を出力
    body = ""
    for result in findings:
        body += result['GroupByAttributeValue'] + ":" + str(result['Count']) + "\n"

    # SNSクライアントを作成
    sns_client = boto3.client('sns', region_name=region)
    
    # メールの件名と本文を作成
    subject = 'Security Hub Insight Results '
    
    # メールを送信
    response = sns_client.publish(
        TopicArn=topic_arn,
        Message=body,
        Subject=subject,
        MessageStructure='string',
        )
    return {
        'statusCode': 200,
        'body': json.dumps(findings)
    }

入力後、「Deploy」を忘れないようにしてください。

環境変数の設定

今回は SNS トピック ARN とカスタムインサイト ARN を環境変数として設定します。
「設定」タブから環境変数を選択し、編集を押してください。

追加する環境変数は下記です。

  • SNS トピック ARN

キー: [SNS_TOPIC_ARN]
値: [SNS トピックの ARN]

  • カスタムインサイト ARN

キー: [INSIGHT_ARN]
値: [カスタムインサイト ARN]

実行テスト

コードタブに戻って、「Test」を押すと、コードを手動実行できます。

初回押下時に、テストイベントの作成が出ますが、今回は呼び出すための値が必要ないため、任意のイベント名を設定して保存してしまって大丈夫です。
手動実行し、statusCode 200 と正常にメールが送信されていることを確認してください。

ここでエラーが出たり、メールが送信されない場合は SNS トピックの設定を含む、すべてのサービスを確認してください。
個人的にはまったポイントは、環境変数の ARN に"(ダブルクォーテーション)が入っていた、でした。

EventBridge

EventBridge の設定

Lambda 関数の概要から、トリガーとなる EventBridge を設定します。
「トリガーを追加」を押してください。

ソースで「EventBridge」を検索し、選択します。

今回は、新規ルールで、 cron 方式で設定します。
設定時間は UTC であることに注意してください。

トリガーを追加すると、設定に追加されます。

動作確認

設定した時刻に Lambda 関数が実行され、メールが送信されているか確認します。
本ブログでは設定を 0:00 UTC (9:00 JST) にしたので、設定時である昨日より Critical が増えているのも確認し、泣きました。

以上が、Security Hub カスタムインサイトの結果を取得して SNS 送信する Lambda 関数を EventBridge で定期実行する手順になります。

まとめ

今回は、特に Lambda 関数に log の設定をしませんでしたが、異常があったときの原因究明方法として設定しておいたほうがいいかもしれません。
また、body 部分を Security Hub カスタムインサイトの値のみにしてしまったため、メールを受け取ったときにちょっと寂しい気持ちになりました。
body 部も適宜優しい言葉を追加していただければと思います。

本ブログが誰かのお役に立てれば幸いです。

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.