Amazon Web Services ブログ

Amazon Elasticsearch Service を使用して GuardDuty でセキュリティをリアルタイムで監視する

Amazon GuardDuty を使用して AWS アカウントとワークロードを保護すると、大量のデータをすばやく検索して可視化できるようになります。企業によっては、何千ものアカウントからアクティビティを分析しているところもあるでしょう。分析後、是正措置がとれるようにセキュリティチームに警告する必要があります。重要なのは、タイミングです。

GuardDuty に、Amazon Elasticsearch Service とデータの可視化を組み合わせることで、データを実用的な洞察に変換することができます。この記事では、Amazon ES を使用して GuardDuty の調査結果を分析する方法を示します。また、Amazon ES のオープンソースのデータ可視化プラグインである Kibana を使用して、データを検索、探索、可視化する方法を紹介します。

このようなリソースの作成を開始するための概要と手順は、次のとおりです。

ソリューションの概要と前提条件

設定方法の高水準フローは、以下に示すように次のステップから構成されています。

  1. Amazon Cloudwatch Events と AWS Lambda を使用して GuardDuty の調査結果を収集します。それを Amazon S3 に送信します。
  2. S3 バケットに配信されたログを Amazon ES に送信します。
  3. Amazon ES で調査結果を分析、検索、集約します。
  4. Kibana ダッシュボードを使用して結果を可視化します。

ここで手順を開始する前に、このAWS ブログ記事で概説しているように、マスターアカウントで GuardDuty を有効にします。マスターアカウントには、GuardDuty のすべての調査結果が集計されます。マスターアカウントは通常、セキュリティチームによって管理され、すべてのメンバーアカウントの結果が表示されます。

ステップ 1: AWS Lambda を使用して GuardDuty ログを収集し、Amazon S3 に送信する

GuardDuty ログを集約するには、GuardDuty の結果を集計する AWS Lambda 関数を設定して、Amazon S3 に格納します。下記の Lambda 関数では、Amazon Simple Notification Service (Amazon SNS) を使用してセキュリティチームにアラートや通知を送信することもできます。

Amazon S3 ユーザーガイド で概説されているように、GuardDuty ログを格納するために、以下に示す S3 バケットを作成します。これを行う際、バケットの Amazon Resource Name (ARN) に注意してください。ARN は後で使用します。ARN は次の形式になります。

arn:aws:s3:::guarddutylogs

Lambda 関数を追加する

  1. AWS マネジメントコンソールにサインインします。
  2. 計算セクションで、[Lambda] を選択します。
  3. [関数の作成] を選択します。
  4. [Author from Scratch] を選択します。
  5. 以下に示す [Author from Scratch] セクションで、以下を追加します。
    • 名前。関数の名前を入力します。
    • ランタイム環境として Node.js 6.10 を選択します
    • ロール。次のいずれかのオプションを選択します。
      • 既存のロールを選択する。適切なロールがある場合は、それを選択します。
      • テンプレートから新しいロールを作成する。このオプションを選択すると、ポリシーテンプレートを選択せず​​に続行できます。デフォルトでは基本的な Lambda 実行権限を持つロールが作成されます。
    • ロール名。ロールの名前を入力します。
    • ポリシーテンプレート。新しいロールを作成した場合は、これを空白のままにしておくことができます。
  6. 関数の作成を選択します。
  7. 関数コードについては、以下のコードを使用してください。
    'use strict'
    
    const aws = require('aws-sdk');
    const s3 = new aws.S3();
    
    // Environment variables for S3 Bucket
    var BucketNAME = process.env.GUARDDUTY_LOGS_BUCKET;
    
    function postToS3(message, context) {
    
        console.log("Entered postToS3");
        var key = message.id+".json";
        console.log("Save the object in S3 bucket with key: " + key)
    
        var params = {
            Body: JSON.stringify(message),
            Bucket: BucketNAME,
            Key: key
        };
        s3.putObject(params, function(err, data) {
            if (err) console.log(err, err.stack); // an error occurred
            else console.log(data); // successful response
            /*
            data = {
             ETag: "\"6805f2cfc46c0f04559748bb039d69ae\"",
             VersionId: "tpf3zF08nBplQK1XLOefGskR7mGDwcDk"
            }
            */
        });
    }
    
    exports.handler = function(event, context) {
    
        console.log('Received event: ', JSON.stringify(event, null, 2));
        
        var final_event;
        console.log(event + " : " + context.awsRequestId);
        if (event.source === "aws.guardduty") {
            final_event = event.detail;
        } else {
            final_event = event;
            return;
        }
    	
    	// Send the GuardDuty findings as a json object to S3
        postToS3(final_event, context);
    };
    
  8. 下の画像に示すように、次の環境変数を渡します。

GUARDDUTY_LOGS_BUCKET: ログが配信される S3 バケットの名前。前に作成したものと同じです。

Lambda 関数のロールを作成する

Lambda 関数には、GuardDuty ログを S3 バケットに送信するための適切な権限が必要です。また、SNS トピックに通知を送信し、ログを CloudWatch Logs グループに投稿する権限も必要です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:eu-west-1:xxxxxxxxxxxx:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:eu-west-1: xxxxxxxxxxxx:log-group:/aws/lambda/guardduty_to_s3:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::guarddutylogs/*"
        }
    ]
}

GuardDuty の CloudWatch イベントルールとターゲットを作成する

  1. CloudWatch により GuardDuty が生成したすべての結果のイベントを送信できるようにするルールを作成します。次の CloudWatch CLI コマンドを実行します。
    aws events put-rule --name Test --event-pattern "{\"source\":[\"aws.guardduty\"]}"
  2. 先に作成したルールのターゲットとして Lambda 関数をアタッチします。次の CloudWatch CLI コマンドを実行します。
    aws events put-targets --rule Test --targets Id=1,Arn=arn:aws:lambda:us-east-1:111122223333:function:<your_function>
  3. ターゲットを呼び出すために必要な権限を追加します。次の Lambda CLI コマンドを実行します。
    aws lambda add-permission --function-name <your_function> --statement-id 1 --action 'lambda:InvokeFunction' --principal events.amazonaws.com

Amazon ES ドメインを作成する

Amazon Elasticsearch Service (Amazon ES) は、AWS クラウドでドメインの作成、Elasticsearch クラスタのデプロイ、操作、スケールを簡単に行うことができるマネージド型サービスです。Amazon ES は、ログ解析、リアルタイムアプリケーションモニタリング、クリックストリーム解析などのユースケースでよく使用されるオープンソースの検索および解析エンジンです。Amazon ES を使用して、GuardDuty のログを分析し、特定の種類の調査結果をフィルタリングできます。また、異なるアカウント間で発生したアクティビティの情報をすばやく把握するためのダッシュボードを構築することもできます。

Amazon ES ドメインを作成するには、Amazon ES 開発者ガイドの「Amazon ESドメインを作成する」の手順に従ってください。Amazon ES クラスタが起動して実行されたら、以下に示すようにエンドポイントをメモします。

ステップ 2: S3 から Amazon ES にストリーミングデータを送信する

データが S3 バケットに入った後は、現在と過去のデータがすべて格納されたリポジトリとして機能します。Amazon Machine Learning を使用してモデルを構築したり、S3 バケットをデータレイクとして使用したりすることができます。

S3 バケットからストリーミングデータを Amazon ES ドメインにロードします。ストリーミングデータは、解析とダッシュボード用の新鮮なデータを提供します。ストリーミングデータをロードするには、AWS クラウドのイベントハンドラーとして Lambda 関数を使用します。GuardDuty の調査結果が S3 バケットに置かれるたびに、カスタム Java/Node.js/Python/C# コードを実行する Lambda 関数を呼び出すイベントをトリガーできます。

この設定では、先に作成した Amazon S3 バケットを使用して、すべての GuardDuty の調査結果がイベントソースとして配信されます。AWS ブログポストに記載されている手順に従って設定し、Node.js 6.10 環境で以下のコードを使用してください。S3 バケットのファイルの JSON ファイルサフィックスが Lambda 関数をトリガーするようにしてください。

Elasticsearch、http-aws-es、およびフラットライブラリは、AWS ではなくサードパーティ製です。AWS は外部コンテンツの機能または適合性について責任を負いません。

関数コードについては、以下のコードを使用してください。

/* Imports */
var AWS = require('aws-sdk');
var connectionClass = require('http-aws-es');
var elasticsearch = require('elasticsearch');
var flatten = require('flat');

/* Globals */
var esDomain = {
    endpoint: 'xxxxxxx-guarddutylogsdemo-xxxxxxxxxxxxxxxxxxxxxxx.eu-west-1.es.amazonaws.com',
    region: 'eu-west-1',
    index: 'guarddutylogs',
    doctype: 'logs'
};
var s3 = new AWS.S3();
var elasticClient = new elasticsearch.Client({  
    host: esDomain.endpoint,
    log: 'error',
    connectionClass: connectionClass,
    amazonES: {
      credentials: new AWS.EnvironmentCredentials('AWS')
    }
});
/*
 * Add the given document to the ES domain.
 * If all records are successfully added, indicate success to lambda
 * (using the "context" parameter).
 */
function postDocumentToES(bucket, key, context) {
    
    console.log('Bucket : ' + bucket + '  Key: ' + key);
    //var req = new AWS.HttpRequest(endpoint);
	var logdata = "";
	var numDocsAdded = 0;   // Number of log lines added to ES so far
	
	var params = {
  		Bucket: bucket,
  		Key: key
	};	
	
	var getObjectPromise = s3.getObject(params).promise();
	getObjectPromise.then(function(data) {
  		logdata = JSON.parse(data.Body);
		console.log("logdata: " + JSON.stringify(logdata) + " id: " + logdata.id);
		var flattened_data = JSON.stringify(flatten(logdata, { maxDepth: 10 }));
		console.log("flattened data: " + flattened_data);
	
		elasticClient.create({
			index: esDomain.index,
			type: esDomain.doctype,
			id: logdata.id,
			body: flattened_data
		}, function (error, response) {		
			if(error)
				console.log("error : " + error); // Publish the error response
			else
				console.log("response: " + response);
		});
	}).catch(function(err) {
  		console.log(err);
	});	

}

/* Lambda "main": Execution starts here */
exports.handler = function(event, context) {
	
	console.log('Received event: ', JSON.stringify(event, null, 2));

    event.Records.forEach(function(record) {
        var bucket = record.s3.bucket.name;
        var objKey = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
        postDocumentToES(bucket, objKey, context);
    });
}

ステップ 3: 分析と検索に Amazon ES を使用する

Amazon ES にデータをストリーミングしたら、分析と、アカウントの予期しない活動の検索に用いることができます。Amazon ES ドメインのドキュメントを検索するには、Amazon ES 検索 API を使用するか、Kibana を使用してドキュメントを検索できます。Kibana は、Amazon ES で動作するように設計された、可視化のためによく用いられるオープンソースツールです。Amazon ES では、Amazon ES ドメインに Kibana がデフォルでトインストールされています。次の作業を行います。

  1. GuardDuty の調査結果を表示するため、Amazon ES サービスが提供するバージョンを選択できます。ただし、セキュリティのためには余分な作業が必要になります。私のノートパソコンで Kibana を実行し、AWS SigV4 署名プロキシ (この github で入手できます) を使用します。この署名プロキシは、AWS ではなくサードパーティによって開発されました。AWS は外部コンテンツの機能または適合性について責任を負いません。 このプロキシは開発とテストには適していますが、実稼働ワークロードには適していません。詳細については、AWS ブログ記事をご覧ください。
  2. Kibana を使用するには、少なくとも 1 つのインデックスパターンを設定します。これらのインデックスパターンは、分析に必要なインデックスを識別するために Kibana が使用します。ブラウザで Kibana を開いたら、[始めてみよう] ページが表示されます。データを可視化するためにインデックスパターンを作成します。[管理] タブに移動し、[インデックスパターン] を選択します。インデックスパターンを作成します。この場合、guardutylogs のワイルドカードとして [*] を選択し、[次のステップ] を選択します。これは下の画像に示されています。
  3. Kibana プロセスのステップ 2 では、時間フィルタを設定し、時間範囲でデータを絞り込むことができるフィールドを選択することもできます。Time Filter フィールドとして [eventLastSeen] を選択し、下に示すように [Create Index Pattern] を選択します。
  4. [索引パターン] 画面には、ドキュメントフィールドが表示されます。たとえば、調査結果の種類、攻撃の発生元の都市名や国名、その他のフィールドなどがあります。この場合、上記の Lambda 関数は簡単な検索と解析のためにデータを平坦化しています。[Discover] タブを選択してデータを検索します。
  5. これまでに収集したデータの中で最も有用な情報の 1 つは 「種類」です。調査結果の種類の目的は、潜在的なセキュリティ問題の簡潔で読みやすい説明を行うことです。表内のドキュメント項目の左側にある [展開]を選択してドキュメント表のドキュメントを展開すると、そのドキュメントのフィールドとデータが表示されます。ドキュメント表のフィールド列の表示または非表示を切り替えるには、[表の列をトグルする] を選択します。[種類] のフィールドを選択すると、以下に示すように種類の列がある表にデータをロードします。

ステップ 4: Kibana ダッシュボードを使用してデータを可視化する

Kibana では、Amazon ES インデックスにあるデータを可視化することもできます。その後、関連する可視化を表示するダッシュボードを構築できます。Kibana の可視化は、上記と同様に Amazon ES 上で実行されるクエリに基づいています。Amazon ES の集計を使用してデータを抽出して処理することで、興味のあるスパイク、ディップ、トレンドを示す表やグラフを作成できます。

Kibana でダッシュボードの設定を開始するには、次の手順を実行します。

Kibana の左側のナビゲーションパネルで [Visualize] を選択します。[Create a Visualization +] を選択して新しい可視化を作成し、[Maps] の下にある [Region Map] を選択します。この場合、どの国が攻撃の発信元であるかを確認します。[*] インデックスパターンを選択します。

[Options] パネルの [Layer Settings] で、[Join] フィールドで [Country Name] が選択されていることを確認します。

[Data] パネルで、次の手順を実行します。

  1. [Metrics] で、集計の種類として [Count] を選択します。特定の国に由来する攻撃ベクトルの総数が表示されます。
  2. バケットの下:
    1. 形状フィールドの場合は、[Aggregation] の下の [Terms] を選択します。
    2. フィールドの場合、以下に示すように action.portProbeAction.portProbeDetails.0.remoteIpDetails.country.countryName.keyword をフィールドとして選択します。
    3. Order By 選択メトリック: Count。
    4. Order の場合は、[Descending] を選択し、サイズは 5 とします。
    5. 以下に示すように、[Apply Changes] を選択します。これにより、地図上にその値がプロットされます。

国の値をプロットする際にログを考慮する時間枠を選択します。これは、以下に示すように、攻撃がどのように進化しているかについての洞察を得るのに役立ちます。

変更が適用されると、マップがプロットされます。[Save] を選択し、可視化を [Attack By Country] として保存します。

同様の方法で、攻撃の種類に応じて別の可視化を作成することができます。例を以下に示します。

GuardDuty 調査結果のダッシュボードを作成する

Kibana でダッシュボードを作成するには、次の手順を実行します。

  1. 左側のナビゲーションパネルで [Dashboard] を選択します。新しいダッシュボードを作成するには、[+] 記号を選択します。上部のナビゲーションバーで [Add] を選択し、作成した 2 つの可視化を選択します。可視化がダッシュボードに追加されます。
  2. 上部のナビゲーションバーで [Save] を選択し、名前を付けます。再度 [Save] を選択して保存します。

このダッシュボードでは、以下に示すように複数のマップ、円グラフ、縦棒グラフを追加して、アカウント上で起こっている脅威ベクトルをすばやく把握できるようにしました。

結論

このブログ記事では、Amazon ES を使用して GuardDuty 調査結果の集約、検索、分析を行う方法を紹介しました。このソリューションは、AWS 環境内の不正なアクティビティや悪意のあるアクティビティを把握するのに役立ちます。この情報があれば、どのアカウントでも発生するイベントに対していつアクションを行うかを決めることができます。

このブログ記事に関するコメントは、以下の「コメント」セクションからお送りください。


著者について

Rohan Raizada は、アマゾン ウェブ サービスのソリューションアーキテクトです。彼はあらゆる規模の企業がクラウドを採用するのに協力し、AWS を使用してスケーラブルでセキュアなソリューションを構築するのをサポートしています。自由な時間は家族と時間を過ごし、野外でサイクリングをするのが趣味です。