AWS CDKでSecurity Hubのイベント駆動型アーキテクチャを構築してみた
こんにちは!製造ビジネステクノロジー部の小林です。
前回の記事ではAWS CDKを利用して、Security Hubを構築しました。
今回はその続編として、Security Hubで検出された脆弱性を重要度レベルに応じて自動的にメール通知する仕組みを作っていきます。EventBridge、Lambda、SNSを組み合わせたサーバーレスアーキテクチャで、セキュリティ監視を効率化する方法をご紹介します!
やりたいこと図
全体のワークフロー
- Security Hubで検出結果が生成される
- EventBridgeルールで、重要度が CRITICAL、HIGH、MEDIUM の検出結果をキャプチャ
- ルールに一致すると、Lambda関数が自動的に呼び出される
- Lambda関数は検出結果の詳細を処理し、SNSトピックにメッセージを発行
- SNSトピックは、登録されたメールアドレスに通知を送信
このアーキテクチャにより、重要なセキュリティ問題が検出された際に、自動的に担当者にメール通知が送られるため、迅速な対応が可能となります。
プロジェクト構造
プロジェクトの構造は以下のようになります。
.
├── bin
│ └── cdk-security-hub.ts # エントリーポイント
├── lambda
│ └── index.ts # Lambdaソース
├── lib
│ ├── cdk-security-hub.ts # Security Hub設定
│ └── security-action.ts # EventBridge,SNS,Lambda関数定義
...
やってみる
AWS CDKを使って通知システムのインフラストラクチャを定義します。このコードでは、Security Hubからの検出結果を処理するためのEventBridgeルール、Lambda関数、SNSトピックを作成します。
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import * as path from 'path';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class SecurityAction extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// SNSトピック
const topic = new sns.Topic(this, 'SecurityTopic');
// メール購読
topic.addSubscription( // トピックにメール購読を追加
new subscriptions.EmailSubscription('<メールアドレスを指定>') // 指定したメールアドレスに通知を送信
);
// Lambda関数
const handler = new NodejsFunction(this, 'SecurityHandler', {
entry: path.join(__dirname, '../lambda/index.ts'),
handler: 'handler',
runtime: lambda.Runtime.NODEJS_20_X,
bundling: {
forceDockerBundling: false,
externalModules: [
'aws-sdk',
],
},
environment: {
TOPIC_ARN: topic.topicArn, // Lambda関数内でSNSトピックのARNを環境変数として使用できるようにする
},
});
// Lambda関数にSNSトピックへのメッセージ発行権限を付与
topic.grantPublish(handler);
// EventBridgeルール
const rule = new events.Rule(this, 'SecurityRule', {
eventPattern: { // どのイベントをキャプチャするかを定義
source: ['aws.securityhub'], // Security Hub からのイベントをフィルタリング
detailType: ['Security Hub Findings - Imported'], // インポートされた検出結果のみを対象
detail: {
findings: {
Severity: {
Label: ['CRITICAL', 'HIGH', 'MEDIUM'], // 重要度が CRITICAL、HIGH、MEDIUM の検出結果のみをフィルタリング
}
}
}
}
});
// ルールのターゲットとしてLambda関数を追加
rule.addTarget(new targets.LambdaFunction(handler));
}
}
イベントを処理するLambda関数を実装します。この関数は、検出された脆弱性の情報を受け取り、SNSを通じてメール通知を送信します。
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
const snsClient = new SNSClient({});
export const handler = async (event: any): Promise<void> => {
const findings = event.detail.findings;
if (!findings || findings.length === 0) return;
const topicArn = process.env.TOPIC_ARN;
for (const finding of findings) {
await snsClient.send(new PublishCommand({
TopicArn: topicArn, // このARNで指定されたSNSトピックに通知が送信されます
Subject: `Security Alert:開発環境 ${finding.Title.substring(0, 99)}`, // SNSのSubjectには最大100文字の制限があるため、substring(0, 99)で99文字に制限している
Message: `Severity: ${finding.Severity.Label}\nResource: ${finding.Resources[0]?.Id || 'Unknown'}` // SNSメッセージの本文を設定。検出結果の重要度(CRITICAL、HIGH、MEDIUM)を表示
}));
}
};
デプロイ
以下のコマンドでデプロイします。このコマンドは、先ほど定義したSecurityActionスタックをAWS CloudFormationを通じてプロビジョニングします。
# 特定のスタックをデプロイする場合
cdk deploy SecurityAction
# 複数のスタックがある場合、すべてをデプロイする場合は
cdk deploy --all
デプロイ後にメールを確認する
CDKのデプロイが完了したら、SNSトピックのサブスクリプション確認メールが登録したメールアドレス宛に届きます。これはAWSのセキュリティ対策で、意図しないメールアドレスへの通知を防ぐためのものです。
- 受信ボックスを確認し、「AWS Notification - Subscription Confirmation」というメールを探します
- メール内の「Confirm subscription」リンクをクリックして承認します
SNSコンソールで確認
サブスクリプションが正常に確認されたかどうかをAWSコンソールで確認できます。
- AWSマネジメントコンソールにログイン
- SNSサービスに移動
- 左側のナビゲーションから「Subscriptions」(サブスクリプション)を選択
- 登録したメールアドレスのステータスが「Confirmed」(確認済み)になっていることを確認
実際にアラートを発報させてメールを受信してみる
システムが正しく動作するかテストするために、Security Hubで手動でアラートを発報させてみましょう!
AWS Security Hubコンソールにアクセスし、左側のナビゲーションから「Findings」(検出結果)を選択。
任意の検出結果を選択(チェックボックスをオン)し、画面上部の「Actions」(アクション)ボタンをクリック。
右画面の「ワークフローのステータス」のプルダウンで「通知済み」を選択。
これにより、EventBridgeルールの条件に一致するイベントが生成され、Lambda関数が起動し、SNS通知が送信されます。
受信ボックスを確認
数分以内に、設定したメールアドレスに「Security Alert:開発環境」で始まるメールが届きます。このメールには、検出結果の重要度とリソースIDが含まれています。
届きました!中身も確認してみます。
ちょっと内容を見てみました。
件名
Security Alert:開発環境 VPC flow logging should be enabled in all VPCs
これはVPC Flow Logsが有効になっていないという警告です
重要度
Severity: MEDIUM
中程度の重要度のセキュリティ問題です。
影響を受けるリソース
Resource: arn:aws:ec2:ap-northeast-1:×××××××××××:vpc/vpc-0f49868e25f93db82
東京リージョン(ap-northeast-1)のAWSアカウント内のVPC(vpc-0f49868e25f93db82)が対象です
問題の内容
VPC Flow Loggingが有効化されていないことを示しています。VPC Flow Logsはネットワークトラフィックを記録するための機能で、セキュリティ監査やトラブルシューティングに重要です。
メールの下部
購読解除のリンクとAWSサポートへの問い合わせ方法が記載されています。
トラブルシューティング
もし、通知が届かない場合は、以下を確認してみてください。
- SNSサブスクリプションが確認済みになっているか
- Lambda関数のログ(CloudWatch Logs)でエラーが発生していないか
- EventBridgeルールが正しく設定されているか(CloudWatch Logsのメトリクスで確認可能)
- Security Hubが有効で、検出結果が生成されているか
おわりに
今回は、AWS CDKを使ってSecurity Hubの検出結果を自動通知するシステムを構築してみました。EventBridge、Lambda、SNSを組み合わせることで、セキュリティアラートを即座にメールで受け取れるようになり、インシデント対応の迅速化が図れます。
セキュリティ対策は「やりっぱなし」ではなく、検知から対応までの一連の流れを自動化することが重要です。
今回のような仕組みを土台に、さらにSlack通知や自動修復の仕組みなども追加していきたいと思います!
余談ですが、筆者はイベント駆動型のアーキテクチャは、ピラゴラスイッチみたいで好きです。
それではまた次回の記事でお会いしましょう!