【AWS】AWS Security Hubフィルタリング結果を管理者に届けるCloudFormationテンプレート
はじめに
ご覧いただきありがとうございます。阿河です。
AWSには便利なセキュリティサービスが用意されています。
例えばAmazon GuardDutyは、悪意のあるアクティビティや不正な行動を継続的にモニタリングする脅威検出サービスです。ただし有効化するだけで終わって、検知結果を踏まえて実際に対処や改善を行うまでは触手が伸びていないという方もいらっしゃるのではないのでしょうか。
今回は一元的なセキュリティチェックが可能なAWS Security Hubの検知結果を、重要度別にフィルタリングして定期的に管理者に届けることで、セキュリティ意識を高めて改善のステップに繋げる一助になればと思い、本記事を書かせていただきました。
対象者
- AWSを運用中
- 運用を自動化したい
概要
- 前提環境
- CloudFormation実行のための準備/実行
- 挙動結果の確認
1. 前提環境
- Security Hubを有効化済
https://docs.aws.amazon.com/securityhub/latest/userguide/fsbp-standard.html
セキュリティ基準は「AWS Foundational Security Best Practices (FSBP) standard」を有効化。こちらはドキュメントにあるように、AWSアカウントとリソースがセキュリティのベストプラクティスから逸脱した場合に検出する一連のコントロールです。
2. CloudFormation実行のための準備/実行
フィルタリング対象の選定
マネジメントコンソールの「findings」のページで、フィルタリングを試すことができます。
今回のフィルタリング対象は
- Severity Label(検出結果の重大度。CRITICAL/HIGH/MEDIUM/LOWそれぞれ確認)
- Compliance Status(チェック結果。FAILEDのみを指定)
- Record State(Findingの記録状態。ACTIVEを指定)
- Product Name(結果を生成したプロダクトの名前。今回はSecurity Hubを指定)
の4つでフィルタリングを行います。
YAMLファイルを用意
YAMLファイルの中身は以下です。
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
AdminMailAdress:
Type: String
Resources:
# Configuring SNS Topic
SNSTopic:
Type: "AWS::SNS::Topic"
Properties:
TopicName: "Topic-For-SecuriyHub-Report"
# Configuring SNS Subscription
Subscription:
Type: "AWS::SNS::Subscription"
Properties:
TopicArn: !Ref SNSTopic
Endpoint: !Ref AdminMailAdress
Protocol: "email"
# Role of Lambda Function
LambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/AmazonSNSFullAccess
- arn:aws:iam::aws:policy/AWSSecurityHubFullAccess
# Configuring Lambda Function
Function1:
Type: AWS::Lambda::Function
DependsOn:
- SNSTopic
Properties:
Environment:
Variables:
TOPIC: !Ref SNSTopic
Code:
ZipFile: |
import json
import boto3
import os
topic = os.environ['TOPIC']
client = boto3.client('securityhub', region_name="ap-northeast-1")
sns_client = boto3.client('sns')
topic_arn = topic
def calc(iterator):
sum_list = []
for page in iterator:
sum_list.append(len(page['Findings']))
sum = 0
for i in sum_list:
sum = sum + i
return sum
def lambda_handler(event, context):
paginator = client.get_paginator('get_findings')
page_iterator_critical = paginator.paginate(
Filters={
'SeverityLabel': [
{
'Value': 'CRITICAL',
'Comparison': 'EQUALS'
},
],
'ComplianceStatus': [
{
'Value': 'FAILED',
'Comparison': 'EQUALS'
},
],
'RecordState': [
{
'Value': 'ACTIVE',
'Comparison': 'EQUALS'
},
],
'ProductName': [
{
'Value': 'Security Hub',
'Comparison': 'EQUALS'
},
]
}
)
page_iterator_high = paginator.paginate(
Filters={
'SeverityLabel': [
{
'Value': 'HIGH',
'Comparison': 'EQUALS'
},
],
'ComplianceStatus': [
{
'Value': 'FAILED',
'Comparison': 'EQUALS'
},
],
'RecordState': [
{
'Value': 'ACTIVE',
'Comparison': 'EQUALS'
},
],
'ProductName': [
{
'Value': 'Security Hub',
'Comparison': 'EQUALS'
},
]
}
)
page_iterator_medium = paginator.paginate(
Filters={
'SeverityLabel': [
{
'Value': 'MEDIUM',
'Comparison': 'EQUALS'
},
],
'ComplianceStatus': [
{
'Value': 'FAILED',
'Comparison': 'EQUALS'
},
],
'RecordState': [
{
'Value': 'ACTIVE',
'Comparison': 'EQUALS'
},
],
'ProductName': [
{
'Value': 'Security Hub',
'Comparison': 'EQUALS'
},
]
}
)
page_iterator_low = paginator.paginate(
Filters={
'SeverityLabel': [
{
'Value': 'LOW',
'Comparison': 'EQUALS'
},
],
'ComplianceStatus': [
{
'Value': 'FAILED',
'Comparison': 'EQUALS'
},
],
'RecordState': [
{
'Value': 'ACTIVE',
'Comparison': 'EQUALS'
},
],
'ProductName': [
{
'Value': 'Security Hub',
'Comparison': 'EQUALS'
},
]
}
)
critical = calc(page_iterator_critical)
high = calc(page_iterator_high)
medium = calc(page_iterator_medium)
low = calc(page_iterator_low)
mail_title = "Security Report"
message = f"CRITICAL: {critical}\nHIGH: {high}\nMEDIUM: {medium}\nLOW: {low}\n\n\n\nhttps://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/fsbp-standard.html\nAWS Foundational Security Best Practices (FSBP)"
mail = sns_client.publish(
TopicArn=topic_arn,
Subject=mail_title,
Message=message
)
return 0
FunctionName: "get-security-hub-report"
Handler: "index.lambda_handler"
MemorySize: 128
Timeout: 20
PackageType: Zip
Runtime: "python3.9"
Role: !GetAtt LambdaRole.Arn
# Configuring EventBridge
CFnEventRule:
Type: AWS::Events::Rule
DependsOn:
- Function1
Properties:
ScheduleExpression: rate(1 hour)
State: ENABLED
Name: "security-hub-report"
Targets:
- Arn: !GetAtt Function1.Arn
Id: !Ref Function1
# Lambda Permission
LambdaPermission:
Type: 'AWS::Lambda::Permission'
DependsOn:
- CFnEventRule
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt Function1.Arn
Principal: 'events.amazonaws.com'
SourceArn: !GetAtt CFnEventRule.Arn
何点か補足します。
- Security Hubの検知結果のフィルタリングには、Lambdaを使用しております。
page_iterator_critical = paginator.paginate(
Filters={
'SeverityLabel': [
{
'Value': 'CRITICAL',
'Comparison': 'EQUALS'
},
],
'ComplianceStatus': [
{
'Value': 'FAILED',
'Comparison': 'EQUALS'
},
],
'RecordState': [
{
'Value': 'ACTIVE',
'Comparison': 'EQUALS'
},
],
'ProductName': [
{
'Value': 'Security Hub',
'Comparison': 'EQUALS'
},
]
}
)
フィルタリングの内容は、環境に合わせて変更してください。
- EventBridgeでは、1時間ごとにメールを送るようにセッティングしてあります。
# Configuring EventBridge
CFnEventRule:
Type: AWS::Events::Rule
DependsOn:
- Function1
Properties:
ScheduleExpression: rate(1 hour)
State: ENABLED
Name: "security-hub-report"
Targets:
- Arn: !GetAtt Function1.Arn
Id: !Ref Function1
あくまで検証のために高頻度で送るように設定しておりますが、環境に合わせて「ScheduleExpression」を調整してください。
CloudFormationの実行
マネジメントコンソールの「CloudFormation」ページから、「Create Stack」に進みます。
「Upload a template file」で、YAMLファイルをアップロードします。
「Specify stack details」のページでは、スタック名と、メールアドレスを入力します。
「Configure stack options」のページは特に設定を変更せず、先に進みます。
最後のページでは、「Capabilities」内のチェックボックスにチェックを入れてください。
以上の設定で作成を行います。
サブスクリプションの承認
指定したメールアドレス宛に、承認のためのメールが届きます。
こちらの承認を行います。
3. 挙動結果の確認
重要度別に件数をカウントした結果が、指定したメールアドレスに届きます。
おわりに
以上、「EventBridge + Lambda + SNS」を利用して、管理者に定期的にレポート結果を届けることができました。
ご活用頂けると幸いです。
御覧いただき、ありがとうございました!
Discussion