S3のアクセスログをサーバレスに処理してAthenaで解析してみた

はじめに

AWSチームのすずきです。

APIの署名バージョン情報がログ項目として追加されたS3のサーバアクセスログ、CloudTrailより廉価な利用が可能ですが、 出力されるログファイル数の多さ、正規表現によるログのパースの困難さなどにより、Athenaなどを利用した解析が難しい課題がありました。

S3のサーバアクセスログで署名バージョンの確認ができるようになりました

今回この解決のため、ログファイルのパースと集約をLambdaとFirehoseを利用して実施、Athenaによる解析処理が行いやすいログ形式への変換を実現しました。

また、後処理のLambdaを用いて署名バージョン2(SigV2)のアクセスログのみを抽出。移行対応が必要となるログが存在した場合、 CloudWatch Logs で簡単に確認可能とする仕組みも用意しました。

これらの仕組みと、展開するテンプレートを紹介させて頂きます。

構成図

設置方法

CloudFormation展開

  • AWSコンソールにログイン済みのブラウザから、展開リンクをクリックします。
  • 展開用リンク
  • IAMリソースの作成を承認をチェックし「作成」します。

  • 5分程度でリソース設置が完了します。

リソース説明

S3(オリジナルアクセスログ)

s3://<スタック名>-accesslog-<リージョン>-<アカウントID>

  • 調査対象とするS3アクセスログの出力先(ターゲットバケット)として利用します。
  • 非圧縮、テキスト形式のS3アクセスログの一時的な置き場として利用します。

Lambda (前処理)

<スタック名>-LambdaFunction-xxxxxxx

  • アクセスログを複数の正規表現を用いてパース後、JSON形式のログデータをFirehoseに投入します。

Firehose

  • JSON形式のログデータを一定時間(600秒)バッファし、圧縮(GZ形式)して出力します。

S3(JSON変換済)

s3://<スタック名>-firehose-output-<リージョン>-<アカウントID>

  • Athenaで解析可能なJSONファイルの保存場所となります。
  • 非圧縮で100MBのアクセスログ、15MB程度に圧縮された状態で保存されます。
  • 今回のテンプレートでは、以下のS3にGZ圧縮されたログが保存されます。
s3://<スタック名>-firehose-output-<リージョン>-<アカウントID>/firehose/s3_logs/<年>/<月>/<日>/<時間>/
  • FirehoseでS3プリフィックス設定を追加する事で、年月日をAthenaのパーティションとして認識するApache Hiveフォーマットで保存する事も可能です。

Firehose でS3プリフィックスのカスタマイズが可能になりました

  • 2019年3月現在、CloudFormation未対応でした。

Lambda(後処理)

<スタック名>-LambdaFunction2-xxxxxxx

  • S3のイベントトリガーで起動し、Firehoseより出力されたログファイルから、V2署名(SigV2)のログを抽出します。
  • 集計、確認に必要な項目を、標準出力を利用してCloudWatchLogsに出力します。
  • V2署名(SigV2)のアクセスログが少ない場合、簡易なログ確認手段となります。
  • V2署名のログが多く、CloudWatch Logsの費用が問題となる場合や、CloudWatch Logs Insightsを利用した可視化の必要性がない場合には、後処理Lambdaのトリガーを無効にしてください。

CloudWatch Logs (Insights)

ロググループ 「/aws/lambda/<スタック名>-LambdaFunction2-xxxxxxx」

  • V2署名のS3アクセスログが蓄積されます。

署名バージョン2のS3 APIログをCloudWatch Logs Insights で集計してみた

CloudWatch Logs Insights クエリ例

ロググループ 「/aws/lambda/<スタック名>-LambdaFunction2-xxxxxxx」 と、任意の期間を指定してクエリを実行します。

  • ログ20件の確認
fields @message 
| filter SignatureVersion = 'SigV2'
| limit 20

  • IPアドレス、S3バケット、操作内容、ユーザエージェント別件数集計
stats count(*) by RemoteIP, Bucket, Operation, UserAgent
| filter SignatureVersion = 'SigV2'

Athena

JSON形式で集約したログ、Athenaを利用して解析する方法を紹介します。

テーブル作成

  • Firehoseの出力S3より、解析対象となる年月日のパスを確認します。
  • Athenaのテーブル作成時、確認したS3パスをロケーションとして指定してします。
  • DB領域は「default」を利用しました。
CREATE EXTERNAL TABLE IF NOT EXISTS default.s3accesslog (
  `Bucket` string,
  `Timestamp` timestamp,
  `RemoteIP` string,
  `Requester` string,
  `RequestID` string,
  `Operation` string,
  `Key` string,
  `RequestURI` string,
  `HTTPstatus` string,
  `ErrorCode` string,
  `BytesSent` string,
  `ObjectSize` string,
  `TotalTime` string,
  `TurnAroundTime` string,
  `Referrer` string,
  `UserAgent` string,
  `VersionId` string,
  `HostId` string,
  `SignatureVersion` string,
  `CipherSuite` string,
  `AuthenticationType` string,
  `HostHeader` string 
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
  'serialization.format' = '1'
) LOCATION 's3://<スタック名>-firehose-output-<リージョン>-<アカウントID>/firehose/s3_logs/<年>/<月>/<日>/'
TBLPROPERTIES ('has_encrypted_data'='false');

クエリ例

  • 10件表示
SELECT * FROM "default"."s3accesslog" limit 10;

  • 過去24時間に発生した、V2署名アクセス、接続元IP、IAM、バケット、ユーザエージェント別集計
SELECT count(1) AS cnt,
         RemoteIP,
         Requester,
         Bucket ,
         Operation,
         UserAgent ,
         SignatureVersion
FROM "default"."s3accesslog"
WHERE Timestamp > now() - interval '24' hour
AND SignatureVersion = 'SigV2'
GROUP BY  RemoteIP, Requester, Bucket ,Operation, UserAgent , SignatureVersion
ORDER BY  cnt DESC 

件数が多い場合、RemoteIPなどを除外する事で、利用頻度の高いシステムを絞り込みやすくなります。

  • 過去24時間に発生した、V2署名アクセス、バケット、ユーザエージェント別集計
SELECT count(1) AS cnt,
         Bucket ,
         UserAgent ,
         SignatureVersion
FROM "default"."s3accesslog"
WHERE Timestamp > now() - interval '24' hour
AND SignatureVersion = 'SigV2'
GROUP BY  Bucket ,Operation, UserAgent , SignatureVersion
ORDER BY  cnt DESC 

まとめ

LambdaとFirehoseにより、S3のアクセスログの解析が簡単に実施出来るようになりました。

CloudTrailのオブジェクトログと比較して、S3のアクセスログは廉価に利用する事が可能です。 大量のS3API利用があるシステムや、継続的なV2署名ログの確認が必要な場合、 今回紹介させて頂いた仕組みなどを参考に頂ければと思います。

今回、S3のアクセスログのパースに利用した正規表現は、先のブログ記事に頂いたコメントとリンク先の情報を踏襲させていただきました。 ume 様、ありがとうございました。

bungoume/parser.py