ちょっと話題の記事

AWSが提供するアカウント・アクティビティのリアルタイム分析ソリューションを試してみた

2018.02.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS上のサービス運用では、セキュリティ・監査・コスト効率化などの観点から、どのようにリソースが操作され、どのようなリソースが利用されているのか、といった情報が求められます。

このようなシステムは、自社で頑張って独自に作り込りこむこともあれば、対応が先送りになっていることもあるかと思います。

難しそうと敬遠されがちなリアルタイム分析を身近にすべく?、アカウント・アクティビティをリアルタイムに保存・分析・可視化する一気通貫なリファレンス実装をAWS が公開しました。

Introducing the Real-Time Insights on AWS Account Activity

わずか5分で構築が完了し、分析や可視化のカスタマイズも可能です。

特に、以下に該当する場合は有用と思われます。

  • AWSのアカウント・アクティビティ分析をはじめたいAWSユーザー
  • AWS上でリアルタイム分析を開発したいエンジニア
  • AWS上でソリューション開発したいエンジニア

以下では、このリファレンス実装を実際に動かしてみます。

アーキテクチャー

このソリューションの肝となる

  • アカウントアクティビティの記録(AWS CloudTrail)
  • アクティビティのリアルタイム分析(Amazon Kinesis Data Analytics)
  • 可視化ダッシュボード(Amazon Cognitoで認証・認可)

は以下の構成で実現されています。

ソリューションをデプロイしてみる

何はともあれ、AWS アカウントにデプロイしてみましょう。

概要

  • デプロイ作業は CloudFormation のボタンポチポチのみ
  • ソリューションはUS East/N. Virginia/US-East-1)リージョンにデプロイ
  • 全リージョンのアクティビティを分析
  • ランニングコストは約 $100/月
  • ダッシュボードユーザーの初期パスワードはEメールで送信

AMC にログイン

デプロイしたい AWS アカウントでログインします。

Select Template

次の URL からソリューション構築用の AWS CloudFormation テンプレートを呼び出し、スタックを作成します。

Real-Time Insights on AWS Account Activity 用 CloudFormation テンプレートローンチリンク

このテンプレートの実体は US East の S3 バケットの次のパスにあります。

s3://real-time-insights-account-activity/latest/real-time-insights-account-activity.template

デフォルト入力のまま「Next」ボタンをクリックします。

Specify Details

ソリューションに必要なパラメーターを埋めます

"User Name" はダッシュボードのユーザー名に利用します。

"User Email Address" で登録したメールアドレスに対して、ユーザーの初期パスワードが送信されます。

"Dashboard Bucket Name" はソリューションで利用する S3 バケットに利用します。大文字英字などは S3 バケットの命名規則として利用できないため、お気をつけ下さい。

入力後に「Next」ボタンをクリックします。

operations

デフォルト入力のまま「Next」ボタンをクリックします。

Reviews

入力内容を確認します。

問題がなければ、"I acknowledge that ..." のチェックをして「Create」ボタンをクリックします。

CloudFormation のスタックが作成されます。

ダッシュボードユーザー向けパスワード付きメールを受け取る

スタック作成開始後、しばらくすると「Your CloudTrail Insight Dashboard Login」と言う件名で、スタック作成時に指定したメールアドレス向けに、ダッシュボードユーザーの初期パスワードを含めたメールが送信されます。

次のURLにアクセスし、初期パスワードでログインします。

https://s3.us-east-1.amazonaws.com/{スタック作成時のS3バケット名}/dash.html

ダッシュボードを閲覧

初回ログイン後は、ウィザードに従い、パスワードを変更します。

ログイン直後は、CloudFormation のスタック作成に関連するリソース操作をもとに表示されているはずです。

以上で、ソリューションのデプロイは完了です。

ソリューションの後片付け

ソリューションが不要になった時の削除方法を紹介します。

リソース全般を CloudFormation で作成しているため、スタックを削除すれば、リソースも削除されます。

ただし、CloudFormation のリソース作成時に S3 バケットだけは DeletionPolicy: Retain で作成しているため、削除されません。

スタック削除後に手動で削除して下さい。

コンポーネントを確認してみる

次に

  • アカウントアクティビティの記録
  • リアルタイム分析
  • ダッシュボード

の主要コンポーネントを確認します。

アカウントアクティビティの記録

re:Invent 資料から

全リージョンのアカウントアクティビティを AWS CloudTrail で取得します。 リージョン毎にアクティビティログの保存を分けることも可能ですが、1バケットに集約します。

Trails に「Apply trail to all regions:Yes」となっている設定が追加されています。

Amazon CloudWatch Events の以下のような Rule により、アクティビティログが書き込まれると、Amazon Kinesis Data Firehose(RealTimeInsightsAccountActivityCloudTrailInput) に連携されます。

リアルタイム分析

リアルタイム分析には Amazon Kinesis Data Analytics を利用します。 データソースとなる Amazon Kinesis Data Firehose のストリームデータ(RealTimeInsightsAccountActivityCloudTrailInput)に対して、SQL の Window 関数を使って分析し、SQL 結果を Amazon Kinesis Data Streams( RealTimeInsightsAccountActivityDataSteam ) に渡します。

Amazon Kinesis Data Streams(RealTimeInsightsAccountActivityCloudTrailInput)に対して は

  • DynamoDB にデータを保存する Lambda 関数(real-time-insights-account-activity-update-ddb)
  • S3 にデータを保存する Amazon Kinesis Data Firehose( RealTimeInsightsAccountActivityAnalyticsOutput )

がぶら下がっています。

real-time-insights-account-activity-helper という名前の Lambda 関数も作成されますが、こちらはソリューションデプロイ時にのみ利用されるため、デプロイ後は不要です。

Kinesis Data Analytics の SQL を確認

CREATE STREAM で出力先となる In-application ストリームを定義し、そのストリームに書き込む PUMP を定義しています。

実際のSQLを見てみます

CREATE STREAM "DESTINATION_SQL_STREAM"
    ( eventTimeStamp TIMESTAMP, computationType VARCHAR(256), category VARCHAR(1024), subCategory VARCHAR(1024),
        unit VARCHAR(256), unitValue BIGINT);
CREATE STREAM "DESTINATION_ANOMALY_STREAM"
    ( eventTimeStamp TIMESTAMP, computationType VARCHAR(256), category VARCHAR(1024), subCategory VARCHAR(1024),
        unit VARCHAR(256), anomalyScore DOUBLE);
CREATE STREAM "TOTAL_CALL_COUNT_STREAM"
 (eventTimeStamp TIMESTAMP, callCount BIGINT);

CREATE OR REPLACE PUMP "PUMP_FOR_TOTAL_CALL_COUNT" AS
  INSERT INTO "TOTAL_CALL_COUNT_STREAM"
  SELECT STREAM STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND) eventTimeStamp, COUNT(*) totalCalls
       FROM "SOURCE_SQL_STREAM_001" cloudtraillogs
       GROUP BY STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND);
CREATE OR REPLACE PUMP "PUMP_FOR_ANOMALY_CALL_COUNT" AS
 INSERT INTO "DESTINATION_ANOMALY_STREAM"
 SELECT eventTimeStamp, 'AnomalyScore', CAST(callCount as VARCHAR(10)), 'None', 'Sum', ANOMALY_SCORE FROM
 TABLE(RANDOM_CUT_FOREST(
              CURSOR(SELECT STREAM * FROM "TOTAL_CALL_COUNT_STREAM"), 100, 256, 100000, 20));

CREATE OR REPLACE PUMP "PUMP_FOR_CALLS_PER_IP" AS
  INSERT INTO "DESTINATION_SQL_STREAM"
    SELECT eventTimeStamp, 'CallsPerUniqueIp', sip, 'None', 'Sum', callsPerIp FROM (
       SELECT STREAM STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '1' MINUTE) eventTimeStamp, COUNT(*) callsPerIp, "sourceIPAddress" sip
       FROM "SOURCE_SQL_STREAM_001" cloudtraillogs
       GROUP BY "sourceIPAddress", STEP(cloudtraillogs.ROWTIME BY INTERVAL '1' MINUTE), STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '1' MINUTE));


CREATE OR REPLACE PUMP "PUMP_FOR_CALLS_PER_API" AS
  INSERT INTO "DESTINATION_SQL_STREAM"
    SELECT eventTimeStamp, 'CallsPerAPI', "eventSource", "eventName" , 'Sum', callsPerAPI FROM (
       SELECT STREAM STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND) as eventTimeStamp, COUNT(*) callsPerAPI, "eventSource", "eventName"
       FROM "SOURCE_SQL_STREAM_001" cloudtraillogs
       GROUP BY "eventSource", "eventName", STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND));

CREATE OR REPLACE PUMP "PUMP_FOR_CALLS_PER_SOURCE" AS
  INSERT INTO "DESTINATION_SQL_STREAM"
    SELECT eventTimeStamp, 'CallsPerServiceType', "eventSource", 'None', 'Sum', callsPerSource FROM (
       SELECT STREAM STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND) eventTimeStamp, COUNT(*) callsPerSource, "eventSource"
       FROM "SOURCE_SQL_STREAM_001" cloudtraillogs
       GROUP BY "eventSource", STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND)
       HAVING "eventSource" <> 'ec2.amazonaws.com');

CREATE OR REPLACE PUMP "PUMP_FOR_EC2_CALLS" AS
  INSERT INTO "DESTINATION_SQL_STREAM"
    SELECT eventTimeStamp, 'EC2Calls', "eventSource", "errorCode", 'Sum', callsPerSource FROM (
       SELECT STREAM STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND) eventTimeStamp, COUNT(*) callsPerSource, "errorCode", "eventSource"
       FROM "SOURCE_SQL_STREAM_001" cloudtraillogs
       GROUP BY "eventSource", "errorCode", STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND)
       HAVING "eventSource" = 'ec2.amazonaws.com');

CREATE OR REPLACE PUMP "PUMP_FOR_CALLS_PER_USER" AS
  INSERT INTO "DESTINATION_SQL_STREAM"
    SELECT eventTimeStamp, 'CallsPerUser', "userName" , 'None', 'Sum', callsPerUser FROM (
       SELECT STREAM STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND) as eventTimeStamp, COUNT(*) callsPerUser, "userName"
       FROM "SOURCE_SQL_STREAM_001" cloudtraillogs
       GROUP BY "userName", STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND));

CREATE OR REPLACE PUMP  "PUMP_FOR_SUCCESSFUL_CALLS"  AS
 INSERT INTO "DESTINATION_SQL_STREAM"
   SELECT eventTimeStamp, 'NumberOfSuccessfulCalls', 'All' , 'None', 'Sum', numberOfSuccessfulCalls FROM (
       SELECT STREAM STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND) AS eventTimeStamp, COUNT(*) numberOfSuccessfulCalls
       FROM "SOURCE_SQL_STREAM_001" cloudtraillogs
       GROUP BY STEP(cloudtraillogs.ROWTIME BY INTERVAL '10' SECOND), STEP(cloudtraillogs."eventTimestamp" BY INTERVAL '10' SECOND));

DynamoDB を確認

  • cloudtrail-log-analytics-metrics : 時系列のメトリクス
  • cloudtrail-log-ip-metrics : IP ごとのリクエスト数

という2つのテーブルが作成されます。

ダッシュボード

S3 の Static website hosting 機能を HTML/JavaScript によりダッシュボードを提供しています。 Cognito Identity Pool ユーザーで認証することで、AuthenticatedUserRole IAM Role により DynamoDB へのアクセスが認可され、ダッシュボードを表示します。

UnauthenticatedUserRole IAM Role も作成されます。 このソリューションの利用状況のトラッキングのために、"mobileanalytics:PutEvents" が Allow されています。

AWS Documentation » AWS Answers » Real-Time Insights on AWS Account Activity » Appendix C: Collection of Anonymous Data

トラッキングを無効化したい場合、CloudFormation テンプレートの AnonymousUsage を No にします。

Mappings:
    SourceCode:
        General:
            S3Bucket: "%%BUCKET_NAME%%"
            KeyPrefix: "real-time-insights-account-activity/latest"
    Send:
        AnonymousUsage:
            Data: "No"

ソリューションをハックする

このソリューションは GitHub 上で公開されており、自由に改善・フォークできます。

https://github.com/awslabs/real-time-insights-account-activity

また、ダッシュボードをカスタマイズする次のドキュメントが公開されています。

AWS Documentation » AWS Answers » Real-Time Insights on AWS Account Activity » Appendix B: Customizing the Dashboard

re:Invent 2017 でのセッション詳細

このアーキテクチャーの雛形は re:Invent 2017 でもセッション発表がありました。

ABD301 - Analyzing Streaming Data in Real Time with Amazon Kinesis

スライド14ページ目 以降が該当します。

まとめ

今回はAWSが提供するアカウント・アクティビティのリアルタイム分析ソリューションを紹介しました。

AWS が提供する様々なサービスを、少しばかりのコーディング(LambdaとSQL)でつなげることで、低コスト($100/月)にリアルタイム分析を簡単にはじめられます。 リアルタイム分析は高価で、技術的に難しく、手を出しにくいと思われていた方には朗報です。

リファレンス実装らしく、AWS のお作法に則った模範的なアーキテクチャーのため、AWS でサービス開発するエンジニアにとっても、このソリューションは参考になるのではないでしょうか。

破壊力があるのに、今ひとつ広まっていない Amazon Kinesis Data Analytics をうまく活用している点も、Kinesis 好きとしては見逃せません(笑)。

参照