目次
BeeXの榊原です。本記事はBeeX Advent Calendar 2024、14日目の記事です。今回はCloudWatch Logs Insights(以下、インサイト)に関して深堀していきます。ログ分析の際、ロググループのログストリームを1つ1つ確認するのは非常に手間です。今回ご紹介するインサイトを用いればより短時間、かつ結果を整然とした状態で確認できます。ぜひとも本記事を読んでお試しいただければと思います。
前提
今回説明に用いるログはCloudWatchLogsのロググループに出力されている、AWS Network Firewall(以下NFW)のアラートログとしています。今回は触れませんが、VPCフローログやCloudTrail等、CloudWatchロググループに出力されるログであればなんでも利用可能です。NFWのルールですが、送信先が「*.google.com」かつ、使用プロトコルがHTTPS通信のもの以外ブロックするよう設定されてます。このルールに沿わない通信は全てアラートログとして出力されます。アラートログは例えば以下のように出力されます。こちらを基に後続の説明とコマンドをご覧ください。
{
"firewall_name": "20241205NFW",
"availability_zone": "ap-northeast-1a",
"event_timestamp": "1733485480",
"event": {
"app_proto": "tls",
"src_ip": "192.168.0.139",
"src_port": 57132,
"event_type": "alert",
"alert": {
"severity": 3,
"signature_id": 4,
"rev": 0,
"signature": "aws:alert_established action",
"action": "blocked",
"category": ""
},
"flow_id": 84131002713947,
"dest_ip": "20.70.246.20",
"proto": "TCP",
"verdict": {
"action": "drop"
},
"tls": {
"sni": "microsoft.com",
"version": "UNDETERMINED"
},
"dest_port": 443,
"pkt_src": "geneve encapsulation",
"timestamp": "2024-12-06T11:44:40.413968+0000",
"direction": "to_server"
}
}
クエリの入力手順
CloudWatchのコンソール画面のサイドバーから「ログのインサイト」を入力(➀)し、ログを流す時間範囲(現時刻から1時間前まで等)とロググループを選択し(➁)、クエリを入力後、「クエリを実行」を選択します。(➂)
クエリをながすロググループのデータ量に対して課金されるので、➁は慎重に選びましょう。
クエリ構文
項目は各ドキュメントのURLに紐づけています。より深く知りたい方はご覧ください。
fields
クエリ結果で表示したいフィールドを選択します。このコマンドを中心にログ結果を出力していきます。なお、ログ以外に自動で生成されるフィールドもあります。下記公式ドキュメントをご参照ください。
CloudWatch Logs Insights は次の 5 つのシステムフィールドを自動的に生成します。
@message未解析の生のログ イベントが含まれます。これはInputLogeventmessageのフィールド と同等です。
@timestampログ イベントのフィールドにイベント タイムスタンプが含まれます。これはInputLogeventのフィールド timestampと同等です。
@ingestionTime CloudWatch Logs がログイベントを受信した時刻が含まれます。
@logStream ログイベントが追加されたログ ストリームの名前が含まれます。ログ ストリームは、ログを生成したのと同じプロセスを通じてログをグループ化します。
@logは、 形式のログ グループ識別子です 。複数のログ グループをクエリする場合、これは特定のイベントがどのロググループに属しているかを識別するのに役立ちます。
実際にクエリを入力してみましょう。結果を表示させたいフィールドをそのまま記載します。
fields @timestamp, event.tls.sni, event.verdict.action, event.src_ip, event.dest_ip, event.dest_port
すると以下のように出力されます。ただフィールドの内容をそのまま出力するだけなので、後続のfilterやsort、limitやdedupと組み合わせて使いましょう。
| @timestamp | event.tls.sni | event.verdict.action | event.src_ip | event.dest_ip | event.dest_port |
| --- | --- | --- | --- | --- | --- |
| 2024-12-06 11:46:05.000 | | drop | 192.168.0.139 | 124.83.184.124 | 80 |
| 2024-12-06 11:46:01.000 | microsoft.com | drop | 192.168.0.142 | 20.231.239.246 | 443 |
| 2024-12-06 11:45:58.000 | | drop | 192.168.0.139 | 124.83.184.124 | 80 |
| 2024-12-06 11:45:57.000 | yahoo.co.jp | drop | 192.168.0.142 | 183.79.219.252 | 443 |
| 2024-12-06 11:45:55.000 | microsoft.com | drop | 192.168.0.142 | 20.231.239.246 | 443 |
※クエリ結果のエクスポートはCSVやJSON形式での出力が可能です。今回はブログ用にMarkdown形式で出力しています。
filter
and とor を使って複数条件を組み合わせたり、正規表現を用いて必要なログを絞って取得するコマンドです。たとえば下記コマンドは「送信元のIPが192.168.0.139かつ通信に対するアクションがドロップのもの」で絞っています
fields @timestamp, event.tls.sni, event.verdict.action, event.src_ip, event.dest_ip, event.dest_port
| filter event.src_ip = "192.168.0.139" and event.verdict.action = "drop"
すると結果は以下のように出力されます。microsoftへのHTTPS通信がドロップされています。
| @timestamp | event.tls.sni | event.verdict.action | event.src_ip | event.dest_ip | event.dest_port |
| --- | --- | --- | --- | --- | --- |
| 2024-12-06 11:44:58.000 | microsoft.com | drop | 192.168.0.139 | 20.70.246.20 | 443 |
| 2024-12-06 11:44:54.000 | microsoft.com | drop | 192.168.0.139 | 20.70.246.20 | 443 |
| 2024-12-06 11:44:53.000 | microsoft.com | drop | 192.168.0.139 | 20.70.246.20 | 443 |
上で紹介した他にも部分文字列一致や除外、大文字小文字を区別しないパターンもあります。ご自身の用途に合わせて色々組み合わせて試してください。
display
最終的に結果を表示させたいフィールドを記載します。クエリの最後に書いて使いましょう。公式ドキュメントのコマンド例もdisplayは最後に記載されています。最初にドキュメントを見たときにfieldsと何が違うか不明でしたが、以下のような挙動をとります。
・displayが無い場合はfieldsの内容がクエリ結果に表示される。
・fieldsのあとにdisplayを記載しないとdisplayがうまく機能しない。
filterの時のコマンドにdisplayをつけて出力してみましょう。
fields @timestamp, event.tls.sni, event.verdict.action, event.src_ip, event.dest_ip, event.dest_port
| filter event.src_ip = "192.168.0.139" and event.verdict.action = "drop"
| display @timestamp, event.tls.sni, event.verdict.action
すると確かに今回はfieldsで指定したフィールドではなく、displayで指定したフィールドのみが出力されました。
| @timestamp | event.tls.sni | event.verdict.action |
| --- | --- | --- |
| 2024-12-06 11:44:58.000 | microsoft.com | drop |
| 2024-12-06 11:44:54.000 | microsoft.com | drop |
| 2024-12-06 11:44:53.000 | microsoft.com | drop |
pattern
ログデータの中で共通パターンを見つけ出してグループ化してくれる機能です。低頻度アクセスログクラスのロググループには非対応です。こちらは実際の出力結果を見る方がイメージがつきやすいので早速見てみましょう。下記コマンドを入力します。
fields @timestamp
| pattern @message
すると以下のように出力されます。今回は@messageの出力パターンと比率を出力しています。ログの傾向を把握したいとき効果を発揮します。
diff
指定した期間に検出されたログと、それよりも前に取得されたログイベントを比較する機能です。patternと併用しないと使えません。もしクエリ文にpatternがなければ「Compile Error - Diff compilation Failed : Diff operation has to be used after Pattern」というエラーを吐きます。また、比較対象のログが無い場合も「Compile Error - Diff compilation Failed : Query's baseline end time is either before the log groups creation time or exceeds the log groups log retention settings」というエラーを吐いてしまいます。比較できる期間は前日の同じ時刻、前週の同じ時刻、前月の同じ時刻の3パターンです。低頻度アクセスログクラスのロググループには非対応です。
今回も実際の出力結果をみて確認してみましょう。下記コマンドを入力します。
fields @timestamp
| pattern @message
| diff previousDay
結果は以下のように出力されます。今回は@messageの出力パターンを1日前のものと比較しています。現在のログパターンと過去のログパターンの比較結果が表示されます。
parse
こちらもfilterと同じくフィールドからデータを抽出する機能です。globモードと正規表現の両方が使えます。ただ、今回のように比較的単調なログを分析するケースであれば、わざわざ利用する必要はありません。上で紹介したfieldsやfilterを用いて抽出する方法がよいでしょう。必要なケースが発生したらまた検証して別途記事化します。
sort
指定したフィールドで昇順、降順でログを並べ替えるコマンドです。limitと組み合わせれば上位の何件か、下位の何件かまで絞って結果を表示できます。並べ替える際のロジックは以下の通りです。
数値以外のすべての値は、すべての数値より前に来ます。数値は数のみを含む値であり、数と他の文字の組み合わせは含まれません。
数以外の値の場合、アルゴリズムは、連続する数と連続するアルファベット文字を別々のチャンクにグループ化して比較します。数以外の部分は Unicode 値の順に並び、数の部分は最初に長さ順に並んでから、数値の順に並びます
今回は通信がドロップされている通信先ドメイン名を上り順で出力してみます。
fields event.src_ip, event.verdict.action
| filter event.verdict.action = "drop"
| sort event.tls.sni desc
| display @timestamp, event.tls.sni, event.verdict.action
結果は以下のように出力されます。実際にはドメイン毎に同じ内容がいくつも続いていますので、重複部分は省略します。重複部分を省きたい場合は後ほど紹介するdedupを使います。
| 2024-12-06 15:07:45.000 | aws.amazon.com | drop |
| 2024-12-06 15:07:45.000 | aws.amazon.com | drop |
<省略>
| 2024-12-06 15:06:33.000 | microsoft.com | drop |
| 2024-12-06 15:06:34.000 | microsoft.com | drop |
<省略>
| 2024-12-06 15:07:13.000 | www.beex-inc.com | drop |
| 2024-12-06 15:07:13.000 | www.beex-inc.com | drop |
<省略>
| 2024-12-06 15:06:44.000 | yahoo.co.jp | drop |
| 2024-12-06 15:06:44.000 | yahoo.co.jp | drop |
stats
ログデータをグラフで視覚化する機能で、棒グラフ、折れ線グラフ、積み上げ面グラフが対応しています。インサイトの機能のみでグラフ化できることに驚きました。下記コマンドは各tls.sni(宛先ドメイン)のdropアクションの発生件数をカウントし、集計結果を宛先ドメインごとに分類するコマンドです。
fields event.tls.sni as sni, event.verdict.action as action
| filter action = "drop"
| stats count(*) as drop_count by sni
図示すると以下のように集計が可能です。
簡単な図表で十分な場合、利用を考えると良いでしょう。
limit
検索結果のクエリ結果はlimitの記載がない場合は最大で10000返しますが、limitをつかえば、検索結果をlimitの後ろの数に絞ることができます。下記のように記載すればクエリ結果は10個のみ返ってきます。
fields event.verdict.action
| filter event.verdict.action = "drop" and ispresent(event.tls.sni)
| limit 10
| display @timestamp, event.tls.sni, event.verdict.action
dedup
重複排除(deduplication)から来ています。名前の通り指定したフィールドの値に対して重複した結果を削除します。1つ注意すべきなのが、dedupコマンドの後のクエリで使用できるクエリコマンドはlimit だけです。displayもNGです。
先ほどsortの説明で下記コマンドを入力しましたが、結果に重複部分がありました。
fields event.src_ip, event.verdict.action
| filter event.verdict.action = "drop" and ispresent(event.tls.sni)
| sort event.tls.sni asc
| display @timestamp, event.tls.sni, event.verdict.action
重複部分を消したい場合、このコマンドの末尾にdedupを加えます。
fields event.src_ip, event.verdict.action
| filter event.verdict.action = "drop" and ispresent(event.tls.sni)
| sort event.tls.sni asc
| display @timestamp, event.tls.sni, event.verdict.action
| dedup event.tls.sni
すると結果は以下のように出力されます。非常にきれいになりました。
| @timestamp | event.tls.sni | event.verdict.action |
| --- | --- | --- |
| 2024-12-06 14:43:01.000 | amazonlinux-2-repos-ap-northeast-1.s3.dualstack.ap-northeast-1.amazonaws.com | drop |
| 2024-12-06 15:08:17.000 | aws.amazon.com | drop |
| 2024-12-06 15:06:51.000 | microsoft.com | drop |
| 2024-12-06 15:08:09.000 | www.beex-inc.com | drop |
| 2024-12-06 15:13:38.000 | yahoo.co.jp | drop |
その他
ここまで紹介したもの以外にもログを整形する方法は様々ありますが、本記事では省略します。気になる方は下記リンクからご覧ください。
・ブール、比較、数値、日時、その他の関数
・特殊文字を含むフィールド
・クエリでのエイリアスとコメントの使用
最後に
今回はNFWのアラートログを例に、インサイトに投げるクエリ文の内容にフォーカスしました。ドキュメントを読むだけではなかなか利用イメージがわきづらいものの、実際に作成したログに対して一つ一つ検証をすることで非常に理解が深まりました。これでログストリームを1つ1つ手作業で確認する生活とはおさらばできそうです。ここまでお読みいただきありがとうございました。
- カテゴリー
- タグ