この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
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 []: sample@example.co.jp
おわりに
Security Hub イベントを見やすくしてメール通知してみました。 ただ、 SNS + Chatbotの構成がお手軽なので、Slack(もしくは Chime) を利用できる場合は そちら検討したほうが良さそうです。
少しでもどなたかのお役に立てば幸いです。
参考
▼ CloudFormation, AWS SAM 周り