Security Hub イベントを見やすく加工して Eメール通知してみる
はじめに
Security Hubで出てきた検出結果イベントを SNS, CloudWatch Events ルール (EventBridge) を介してメールや Slackチャンネルに投稿ができます。
Security Hub イベント → SNS → Eメール
Security Hub イベント → SNS → Chatbot
のような構成です。 Chatbot の場合は重要度に応じて色分け、いい感じに表示してくれます。
Eメールは Security Hub イベントの JSONがそのまま本文に入ります。Chatbotの通知に比べると見づらいですね。
本ブログでは Eメール通知内容を見やすく してみます。
Security Hub イベント → Lambda → SNS → Eメール
といった構成を作ってみます。
Lambdaで Security Hub イベントを加工、メッセージを SNS Publish します。
2020/08/20 追記
Lambda を挟まなくても EventBridge の ターゲット入力の変換 を利用することで、SNSに渡す文章をカスタマイズできます。
計算処理などを行いたい場合は、本ブログのように Lambdaを介する構成で良いと思います。
構成内容
作成したAWSリソースを説明していきます。 (※ Security Hub を有効化していることは前提です)
SNS
適当な名前( securityhub-finding-topic
など ) のSNSトピックを作成します。
送信先のEメールアドレスを指定したサブスクリプションを作成します。
Lambda(IAMロール)
Lambdaのサービスロールを作成して、 以下のようなインラインポリシーを付けます。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "sns:Publish" ], "Resource": "*", "Effect": "Allow" } ] }
Lambdaの基本的な実行ポリシー ( AWSLambdaBasicExecutionRole
)に
sns:Publish
許可を追加したポリシーになります。
Lambda(Function)
Python3.7 で Lambda Functionを作成します。 コードは以下です。
import json import os import boto3 SNS_TOPIC_ARN = os.environ['SNS_TOPIC_ARN'] client = boto3.client("sns") def lambda_handler(event, context): subject = "Security Hub Findings Notification" message = parse_event(event) client.publish( TopicArn=SNS_TOPIC_ARN, Subject=subject, Message=message ) def parse_event(event): texts = [] # Top texts.append("# Security Hub Findings") texts += [ "- Region :: {}".format(event['region']), "- Account :: {}".format(event['account']), "- Time :: {}".format(event['time']) ] # Findings for index, fd in enumerate(event['detail']['findings']): texts.append("## Finding #{}".format(index + 1)) texts += [ "- Title :: {}".format(fd['Title']), "- Severity :: {}".format(fd['Severity']['Label']), "- Compliance :: {}".format(fd['Compliance']['Status']), "- RecordState :: {}".format(fd['RecordState']), "- AwsAccountId :: {}".format(fd['AwsAccountId']), ] # Resources texts.append("- Resources ::") for rs in fd['Resources']: texts.append(" - Id :: {}".format(rs['Id'])) # Remediation texts.append("### Remediation Recommendation") texts += [ fd['Remediation']['Recommendation']['Text'], fd['Remediation']['Recommendation']['Url'] ] # Raw Content texts.append("# Raw Content") texts.append("{}".format(event)) return "\n".join(texts)
環境変数に SNS_TOPIC_ARN
を作成、さきほど作成したSNSトピックのARNを指定します。
EventBridge(ルール)
EventBridgeに新規ルールを作成します。
パターンは イベントパターン
、以下のようなカスタムパターンを今回は使用します。
{ "source": [ "aws.securityhub" ], "detail-type": [ "Security Hub Findings - Imported" ], "detail": { "findings": { "Compliance": { "Status": [ { "anything-but": "PASSED" } ] }, "RecordState": [ "ACTIVE" ], "Resources": { "Type": [ { "anything-but": "AwsAccount" } ] } } } }
このパターンは「(AwsAccount リソースタイプの結果は除外して) セキュリティチェックに合格しなかった結果を取得する」 ものです。
※Security Hub 検出結果のフィルタリング例は下記にまとめているので参照ください。
ターゲットにさきほど作成した Lambda関数を指定します。
確認
構築してしばらくすると以下のようなメールが届きました。
- # Security Hub Findings
- Region … イベントのリージョン
- Account … イベントのAWSアカウント
- Time … イベントの時刻
- ## Finding #X … 取得した各検出結果
- Title … セキュリティチェック項目のタイトル
- Severity … 重要度
- Compliance … チェック結果
- RecordState … チェック結果のステータス
- AwsAccountId … チェック対象の AWSアカウントID
- Resources … チェック対象の各リソースID
- Remediation Recommendation … 修復アクションの内容、参考リンク
- # Raw Content … イベントのJSONそのままの内容
JSONそのままの内容よりかは、見やすくなったと思います。
(参考) SAMで構築してみた
上記リソースは AWS サーバーレスアプリケーションモデル (AWS SAM)を使って構築してました。 SAMのプロジェクト構成内容を記します。
プロジェクト
sam init
を使って新規プロジェクトを作成します。
sam init --runtime python3.7 --name securityhub-sns-notification
template.yaml
template.yaml
は以下です。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: securityhub-sns-notification Parameters: EmailAddress: Type: String Resources: # SNSトピック、サブスクリプション SnsTopic: Type: AWS::SNS::Topic Properties: TopicName: securityhub-finding-topic SnsSubscription: Type: AWS::SNS::Subscription Properties: Protocol: email Endpoint: !Ref EmailAddress TopicArn: !Ref SnsTopic # # IAMロール IamRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: "sts:AssumeRole" Policies: - PolicyName: "LambdaPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" - "sns:Publish" Resource: "*" # Lambda Function Function: Type: AWS::Serverless::Function Properties: CodeUri: src/ Handler: app.lambda_handler Runtime: python3.7 Environment: Variables: SNS_TOPIC_ARN: !Ref SnsTopic Role: !GetAtt IamRole.Arn Events: EventBridgeRule: Type: CloudWatchEvent Properties: Pattern: source: - "aws.securityhub" detail-type: - "Security Hub Findings - Imported" detail: findings: Compliance: Status: - anything-but: "PASSED" RecordState: - "ACTIVE" Resources: Type: - anything-but: "AwsAccount"
src/app.py
(Lambda関数)
前述のLambda関数のコードを src/app.py
に格納します。
ビルド、デプロイ
sam build sam deploy --guided
デプロイの設定は下記のとおりです。パラメータに指定した EmailAddress
に送信先メールアドレス
を記入します。
> sam deploy --guided Configuring SAM deploy ====================== Setting default arguments for 'sam deploy' ========================================= Stack Name [securityhub-sns-notification]: securityhub-sns-notification AWS Region [ap-northeast-1]: ap-northeast-1 Parameter EmailAddress []: [email protected]
おわりに
Security Hub イベントを見やすくしてメール通知してみました。 ただ、 SNS + Chatbotの構成がお手軽なので、Slack(もしくは Chime) を利用できる場合は そちら検討したほうが良さそうです。
少しでもどなたかのお役に立てば幸いです。
参考
▼ CloudFormation, AWS SAM 周り