S3のログにCloudWatchLogsのAlarmを設定する（コンソール編）

# はじめに
VPCの不正トラフィックにアラームを設定する方法を、[コンソール](https://qiita.com/a_b_/items/54252d2ba7944ecefcad)と[CloudFormation](https://qiita.com/a_b_/items/e7d0d60d356355dcf5b5)の2手法で記事にしましたが、今回はS3のログに同様のことを行う方法を記事にしました。

# 目的
S3に置いた機密情報が、想定外の取得をされていないか、がわかる環境の構築を目的としました。
悪意あるサービス等からの取得があったら、すぐにメール通知が飛ぶ、という環境がゴールのイメージです。

# S3を監視するサービス
調べたところ、以下がありました。

- S3アクセスログ
    - [AWS CloudTrail を使用した Amazon S3 API コールのログ記録](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/cloudtrail-logging.html)
    - [サーバーアクセスログを使用したリクエストのログ記録](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/ServerLogs.html)
- [CloudWatchメトリクス](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/cloudwatch-monitoring.html)

## 2つある、S3アクセスログ
[参考にしたページ](https://dev.classmethod.jp/articles/cloudtrail-s3-logs-vs-s3-server-access-logs/)などからの情報によると、以下のような違いがありそうです。

| | CloudTrail使用 | サーバアクセスログ |
| - | - | - |
| 料金 | 少し掛かる | 安価 |
| 改ざん防止 | 可能 | 不可 |
| 出力先 | CloudWatch <br>S3 | S3 |
| 監視対象バケット | 全て <br>任意（複数） | 任意（１つ） |
| アクセス元がわかるか | sourceIPAddress | Remote IP <br> （[プロキシやFW経由だと実際のアドレスが不明確になる](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/LogFormat.html)）とあり |

アラームを送信することまでを目的としているため、CloudWatchに出力できる方が良いです。
S3だと、Athenaを作って、Lambda等で周期的に不正トラフィックの有無を取得するSelect文を流して、SNSで通知、という作りこみが必要になりそうです。
また改ざん防止やアクセス元の特定の用途から、CloudTrailの方法を用いました。

## 標準のメトリクスではダメ？
標準機能として、[CloudWatchメトリクス](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/cloudwatch-monitoring.html)がありますが、使ってみたところ出来合いのメトリクスであり、”特定のアクセス元（以外）からのアクセス数”のような細かい内容での検出は出来なそうでした。

## これだけでは不十分
ここで行うことは”検出”だけで、不正アクセスを未然に防ぐことはできません。
IAMポリシーやバケットポリシーを適切に設定する必要があります。

# やったこと
コンソールから、以下のことを行いました。

1. 監視するバケットの作成
1. CloudTrailで、証跡の作成
    1. 保存先S3バケットは、操作の中で作成できます。
    1. CloudWathLogsのロググループも、操作の中で作成できます。
    1. IAMロールも、操作の中で作成できます。
1. CloudWatchLogsから、メトリクスの作成
1. アラームの作成
    1. 作成時に、SNSトピックも作成できます。


# 構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/20af0ff5-55d9-db95-6d69-5bb2c0f69aaa.png)

# 実際の手順

## 監視するバケットの作成

普通にバケットを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/906a0519-8564-1dca-299e-77581e0bc22a.png)


## CloudTrailで、証跡の作成
[CloudTrail]-[証跡]から、[証跡の作成]をクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/8660141d-5992-ce37-9dc9-5ae4a6f1cd4d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/be7ca7f1-fa80-5eb9-a02d-5df88d4de981.png)

- 証跡名
    - 任意の名前で。
- ストレージの場所
    - 今回は新しく作ります
- 証跡ログバケット及びフォルダ
    - 自動でバケット名が入ります
    - フォルダまで指定できますが、**末尾のスラッシュ(/)はつけないでください。**
        - 作成されるバケットポリシーで、リソースに指定する出力先バケットのパスのスラッシュが重複してしまいます。
        - 下部の「ログはxxxxに保存されます」を見て問題ないか確認ください。
- ログファイルの SSE-KMS 暗号化
    - 今回は無効で。
- ログファイルの検証
    - 有効にしました。
- SNS 通知の配信
    - 無効にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/d8b9fe7e-64b8-f9a8-282e-5741eb0e7133.png)

- CloudWatch Logs
    - 有効
- ロググループ
    - 今回は新規としました。
- ロググループ名
    - 任意で。
- IAMロール
    - 新規に作ります。
- ロール名
    - 任意で。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/33eb783b-7518-bc18-0a75-f2fb4f72e9d3.png)

- イベントタイプ
    - データイベントのみ選択。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/7f245955-3e05-7de3-5d66-4ef220bd90e5.png)

- [基本的なイベントセレクターに切り替えます]をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/34a1b7ff-2860-9032-9b1b-cc7c5b672219.png)

- データイベントソース
    - S3を選択。
- S3バケット
    - 現在および将来のすべての S3 バケットの”読み取り”と”書き込み”のチェックを外す。
        - 今回は、作成したバケットのみを監視します。
        - こちらを使うと、「他の証跡の出力先バケットのログ」や「自身の出力先バケットのログ」も対象になるようです。
    - 個々のバケットの選択
        - 監視するバケットを指定して、”読み取り”と”書き込み”のチェックを入れる。

[次へ]-[証跡の作成]で完了です。

## CloudWatchLogsから、メトリクスの作成
[VPCフローログの際](https://qiita.com/a_b_/items/54252d2ba7944ecefcad#cloudwatchlogs%E3%81%8B%E3%82%89%E3%83%A1%E3%83%88%E3%83%AA%E3%82%AF%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90)の作業と同じですので省略します。
フィルターパターンは前回と異なりJSON形式のログなので、[公式](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/FilterAndPatternSyntax.html)を参考に作成します。


```
# GetObject、かつ送信データが1KBを超えるアクセス
{ ($.eventName = "GetObject") && ($.additionalEventData.bytesTransferredOut > 1000 )}

# VPCエンドポイントを経由していないアクセス
{ ($.vpcEndpointId NOT EXISTS) }
# { ($.vpcEndpointId = "" ) } や { ($.vpcEndpointId IS NULL ) }はダメでした。

# 特定のIP以外からのアクセス
{ ($.sourceIPAddress!="123.123.123.123") }

# GetObject、かつ送信データが1KBを超え、特定のIP以外またはVPCエンドポイント以外のアクセス
{ ($.eventName = "GetObject") && ($.additionalEventData.bytesTransferredOut > 1000 ) && ( ($.sourceIPAddress!="123.123.123.123") && ( ($.vpcEndpointId != "vpce-xxxxxxxx" ) || ($.vpcEndpointId NOT EXISTS) ) ) }
# 多分あっている、はず
```


## アラームの作成
こちらも[VPCフローログの際](https://qiita.com/a_b_/items/54252d2ba7944ecefcad#%E3%82%A2%E3%83%A9%E3%83%BC%E3%83%A0%E3%81%AE%E4%BD%9C%E6%88%90)と同じになりますので省略します。

# 作成したアラームが動くかチェック
作った構成が機能するか確かめます。

- 1KBのファイルをアップし、CloudShell（決められたIP以外）から、GetObjectしました。
    - 結構待ちます。
        - [公式](https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/how-cloudtrail-works.html)によると、15分以内といっています。

> CloudTrail は、通常、API コールから平均 15 分以内にログを配信します。この時間は保証されません。

アラームが飛ぶと、以下のようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/00f0ffa3-d384-4905-19ab-b7985c29c920.png)




# 片づけ
- アラームの削除
    - [CloudWatch]-[すべてのアラーム]から、作成したアラームをチェックし、[アクション]-[削除]。
- SNSの削除
    - [Amazon SNS]-[トピック]-[（作成したトピック）]をクリックして、"サブスクリプション"から対象のアドレスを選択し、削除。
    - その後、対象のトピックを削除。
- メトリクスフィルターの削除
    - 対象のメトリクスフィルターの右上にチェックをして、削除。
- 証跡の削除
    - [CloudTrail]-[証跡]から、作成した証跡をチェックし、削除。
- S3の削除
    - 監視対象バケット、および保存先バケットを両方とも空にして削除します。
- IAMロールの削除
    - 作成されたロールを選択し、削除。
- ロググループの削除
    - [CloudWatch]-[ロググループ]から、作成したロググループを選択し、[アクション]-[ロググループの削除]。
- メトリクスの削除は・・・
    - [手動では削除できず、](https://dev.classmethod.jp/articles/tsnote-support-cloudwatch-delete-metrics-launch-001/)新しくデータが発行されなければ自動で削除されるようです。

# 課題

- VPCエンドポイント経由以外のアクセスをフィルタから除外する方法
    - パブリック経由のアクセス
    - パブリックサービスからのアクセス
        - Athena、Lambda、Glue等
    - コンソールからのアクセス

見たところ、以下のようなことが確認できました。
見落とし、見間違いあると思われますので、ご自身で確認ください。

| サービス | vpcEndpointId | sourceIPAddress | その他 |
| - | - | - | - |
| パブリック経由のEC2 | なし | パブリックIP |  |
| コンソールからのアクセス | ない？ | 接続元のIP |  |
| CloudShellからのアクセス | なし | [AWSのIP範囲](https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-ip-ranges.html#aws-ip-download)のうち、`region=="ap-northeast-1" service=="AMAZON"`のIPか？ |  |
| Athenaからのアクセス | なし | "athena.amazonaws.com"` がセット |  テーブル名は見当たらない（見落としたかも？）|
| Lambdaからのアクセス | なし | [AWSのIP範囲](https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-ip-ranges.html#aws-ip-download)のうち、`region=="ap-northeast-1" service=="EC2"`のIPか？ | ・userAgentに`AWS_Lambda_python3.9 `の文字列あり <br>・$.userIdentity.sessionContext.sessionIssuer.userNameに、関数名あり |

これらを除外するフィルターを作るのは難しそうです。VPC内部からのアクセスに出来るのであれば、そちらが楽かもしれません。

# まとめ
「S3ログから不正アクセスを検出」というテーマで環境を構築する方法をまとめました。
~~次はCloudFormationを用いて作る方法を記事にしたいと思います。~~
[CloudFormationを用いた記事はこちらになります。](https://qiita.com/a_b_/items/2d2c6de3c4c7353c4dca)