🚀

Amazon CloudWatch メトリクスフィルターを設定してみた

2023/12/27に公開

はじめに

こんにちは!みゃっちーです。🦔

AWS SCS試験ただ勉強してももったいないから面白そうな構成作ってみよう第2弾です。
今回はCloudWatch Logsに取得したログのメトリクスフィルターを使った抽出です!

想定している全体像

想定している全体像は下記の通りです。

AWS CloudTrailでユーザのAPI履歴をS3、CloudWatchLogsに送信し、特定のアラートでSNSでメールを送信する。

前回の記事で赤色で囲ったAWS CloudTrailの証跡をCloudWatch logsに取得する構築を行いました。
https://zenn.dev/devcamp/articles/5fdc895633aaed

今回は青枠で囲った部分のCloudWatch メトリクスフィルターの設定を行い、EC2作成イベントの抽出を行います。

そもそもCloudWatch メトリクスフィルターとは?

公式の説明は下記の通りです。

1 つまたは複数のメトリクスフィルターを作成することで、CloudWatch Logs で受信するログデータを検索およびフィルタリングできます。・・・ログデータを数値の CloudWatch メトリクスに変換し、グラフを作成したり、アラームを設定したりできます。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/MonitoringLogData.html

このようにメトリクスフィルターを使うことで特定のログの抽出、表示、アラート設定を行うことができます。

やってみた

1.CloudWatch ロググループの確認

前回作成したAWS CloudTrail 証跡のロググループが作成されていることを確認します。

2.メトリクスフィルターの作成

対象のロググループを選択すると、メトリクスフィルターの作成という欄があるので作成ボタンを押します。

3.フィルタパターンの定義

必要なログを取り出すフィルターの設定を行います。

対応しているログの種類には大きく分けて3種類存在し、それぞれに設定方法が用意されています。

  • json形式
  • 非構造化ログ形式
  • スペース区切り形式

json形式

json形式サンプルログ
{"Records": [{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "EXAMPLE6E4XEGITWATV6R",
        "arn": "arn:aws:iam::123456789012:user/Mateo",
        "accountId": "123456789012",
        "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
        "userName": "Mateo",
        "sessionContext": {
            "sessionIssuer": {},
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2023-07-19T21:11:57Z",
                "mfaAuthenticated": "false"
            }
        }
    },
    "eventTime": "2023-07-19T21:17:28Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "StartInstances",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "192.0.2.0",
    "userAgent": "aws-cli/2.13.5 Python/3.11.4 Linux/4.14.255-314-253.539.amzn2.x86_64 exec-env/CloudShell exe/x86_64.amzn.2 prompt/off command/ec2.start-instances",
    "requestParameters": {
        "instancesSet": {
            "items": [
                {
                    "instanceId": "i-EXAMPLE56126103cb"
                },
                {
                    "instanceId": "i-EXAMPLEaff4840c22"
                }
            ]
        }
    },
    "responseElements": {
        "requestId": "e4336db0-149f-4a6b-844d-EXAMPLEb9d16",
        "instancesSet": {
            "items": [
                {
                    "instanceId": "i-EXAMPLEaff4840c22",
                    "currentState": {
                        "code": 0,
                        "name": "pending"
                    },
                    "previousState": {
                        "code": 80,
                        "name": "stopped"
                    }
                },
                {
                    "instanceId": "i-EXAMPLE56126103cb",
                    "currentState": {
                        "code": 0,
                        "name": "pending"
                    },
                    "previousState": {
                        "code": 80,
                        "name": "stopped"
                    }
                }
            ]
        }
    },
    "requestID": "e4336db0-149f-4a6b-844d-EXAMPLEb9d16",
    "eventID": "e755e09c-42f9-4c5c-9064-EXAMPLE228c7",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "123456789012",
    "eventCategory": "Management",
     "tlsDetails": {
        "tlsVersion": "TLSv1.2",
        "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "clientProvidedHostHeader": "ec2.us-east-1.amazonaws.com"
    },
    "sessionCredentialFromConsole": "true"
}]}

json形式フィルターの例

# 特定のパラメータの値を検索したいとき
{ $.eventName = "StartInstances" }
# 正規表現を使用するとき
{ $.eventName = %^Start% }
#特定のパラメータの数値に条件を付けたい場合
{ $.errorCode = 400} 
#「=」以外でも可
{ $.errorCode > 400} 
#特定のIPアドレス以外を指定する場合
{ $.sourceIPAddress != 123.123.* }

非構造化ログ形式

非構造化ログ形式サンプル

[83078518-fcc1-4d30-9573-8b9737671438] INFO : Created table gluetest in database mygluedatabase
[83078518-fcc1-4d30-9573-8b9737671438] BENCHMARK : Finished writing to Catalog
[83078518-fcc1-4d30-9573-8b9737671438] BENCHMARK : Crawler has finished running and is in state READY

非構造化ログ形式記述の例

#1つの単語を抽出する場合
INFO
#2つの単語を抽出する場合
INFO Created
#どちらかに当てはまる単語、文字列を抽出する場合
?INFO ?"Created table gluetest in database mygluedatabase"
#特定の単語が含まれないものを抽出するとき
INFO -Created

スペース区切り形式

スペース区切り形式サンプル

127.0.0.1 Prod frank [10/Oct/2000:13:25:15 -0700] "GET /index.html HTTP/1.0" 404 1534                     

スペース区切り形式記述の例

#すべての値を指定する場合
[ip=%127\.0\.0\.[1-9]%, user, username, timestamp, request =*.html*, status_code = 4*, bytes]
#一部省略して検索する場合
[..., request =*.html*, status_code = 4*, bytes]
#一部複数の条件を追加する場合
[ip, user, username, timestamp, request =*.html*, status_code = 404 || status_code = 410, bytes]            

今回のCloudTrailの証跡ログはjson形式なので下記の構文で「RunInsutans」のイベント名を指定することにしました。

{ $.eventName = "RunInstances" }

4.フィルタパターンのテスト

指定したフィルタパターンできちんとフィルターができるのかを試します。
テストではロググループに現時点で保存されているログと、任意で自分で作成したログを選択することができます。

試しにEC2の作成イベントが入っているログを選択しパターンをテストボタンを押すとこのように作成イベントのみを抽出することができました。

5.メトリクスの割り当て

各設定項目はこのようにしました。それぞれの設定項目の詳細はこの後のメトリクスの表示と照らし合わせてみるとわかりやすいかと思います。

  • フィルター名: RunInstance
  • メトリクス名前空間: 新規作成 EC2_Surveillance
  • メトリクス名: RunInstance
  • メトリクス値: 1
  • デフォルト値: 0

ちなみに「0に設定するとき」と「設定なし」の違いですがわかりやすい例だと、以下の図のようにメトリクス値の平均を算出する際に割る母数が変わります。

6.メトリクスフィルターの設定完了!

このように作成されたメトリクスフィルターが表示されます。

作成したメトリクスを表示する

1.すべてのメトリクスを選択

2.作成したメトリクス名前空間を選択

3.作成したメトリクス名を選択

このように表示することができました!

実際にEC2を起動して観測してみる

一つの場合

実際に起動してみるときちんとメトリクス上に検知されていることがわかります!

複数の場合

メトリクスは5分ごとに集計するので5分以内に複数(2)インスタンスを立ち上げるときちんと表示されます!

さいごに

ここまで読んでいただきありがとうございました!

一番初めのCloudTrailの大量のログから特定のログをきちんとフィルターすることができていて、わかりやすく使いやすくなったと思います!

次回はAWS SNSトピックとサブスクリプションを設定してメールを送れるようにしたいと思います。

Discussion