ポリシーリスクを可視化。自然な言葉でポリシーに関する疑問を解決します。 デモを申し込む →

Published:

CloudTrail と CloudWatch Events が連携する過度に複雑な仕組み

by FireMon

私のクラウドの取り組みの中で最も悩まされた問題の一つが、CloudTrail と CloudWatch Events がどのように連携するのかを理解することでした。なぜか、その接続が実際にどう機能するのか、とりわけマルチリージョンや AWS Organization の証跡(トレイル)という概念とどう関わるのかを理解するまでに、何年も(そして多くの検証)を要しました。そして一通り理解した後は、誰もがすでに知っていることだと思い込んでいましたが、最近の会話から、この混乱がかなり一般的であることが明らかになりました。そこで、可能な限り分かりやすく整理してみます。

まず、解決しようとしている課題は次のとおりです。CloudTrail のイベントに基づいて CloudWatch ルールを構築し、それを使って通知を送信するか Lambda 関数をトリガーする

それだけです。新しいセキュリティグループのルールを開放する API 呼び出し(お尋ねであれば、AuthorizeSecurityGroupIngress です)といった単純なものについて通知を送りたいのです。

これを機能させるには、以下が必要です。

  • API 呼び出しが行われるリージョンで CloudTrail が有効になっていること。
  • CloudTrail が CloudWatch へストリーミングしていること。
  • その特定の API 呼び出し、またはすべての CloudTrail API 呼び出しを検知する CloudWatch ルールがAPI 呼び出しのリージョンに存在すること。

ここからが混乱のもとです。

  • マルチリージョンの CloudTrail または Organization の証跡を作成すると、AWS は裏側で実際にはすべてのリージョン(Organization の証跡の場合はすべてのアカウント)に証跡を設定しています。これらはすべて別個の証跡ですが、それぞれが結果を共有の S3 バケットに送信するよう構成されており、各証跡はそのホームアカウントおよびリージョンでしか管理できません。
  • しかし、API 呼び出しの CloudWatch イベントはその API 呼び出しのリージョンにしか作成されません
  • したがって、マルチリージョンの証跡を作成するとデータはすべて一元的に収集されますが、イベントはローカルにしか現れません。ホーム証跡のリージョンにある CloudWatch ルールは、その(ホーム)リージョンで行われた API 呼び出しに対してのみトリガーされます。つまり、セキュリティグループ変更のアラームを構築しても、CloudTrail が有効になっているにもかかわらず、ホームリージョンでしか機能せず、他のリージョンでは機能しません。
  • CloudWatch のロググループ/ログストリームはプライマリリージョンに表示され、他のリージョンには表示されませんが、各イベントはそのイベントをトリガーしたリージョンで作成されます。
  • API 呼び出しのすべてのイベントを収集したい場合は、ドキュメント化されていないイベント定義(下に貼り付けてあります)を使用する必要があります。
  • Amazon のドキュメントを読んでも、こうした点は明確に説明されていません。少なくとも私が見つけられた範囲では書かれていません。実際、サポートへの問い合わせ中にこれを突き止めたことがあり、私のイベントが流れ始めるなか、AWS の担当者は「そういう仕組みではないと思うのですが」とつぶやき続けていました。彼にとっては厳しい夜だったでしょう。

これは私にとって、なぜか非常に直感に反するものでした。証跡を一元化すれば、API 呼び出しイベントを起点とする CloudWatch ルールも一元化できると思い込んでいたのです。残念ながらそれは完全に誤りで、証跡を一元化しても、対象としたいリージョンすべてでルールを作成する必要があります。複数アカウントからイベントを収集するために Event Bus を使う場合でも、やはりイベントを Bus に送るために、すべてのアカウントのすべてのリージョンで CloudWatch ルールを作成する必要があり、さらに目的の通知やアクションをトリガーするために、Event Bus のすべてのリージョンでルールを構築する必要があります。

CloudWatch ルールがサポートするニアリアルタイムのアラート機能や自動修復/アクションを利用したい場合、私が推奨するアプローチは次のとおりです。

  • マルチリージョンの証跡を有効にします。これは一度だけ行えばよく、Organization の証跡で十分です。
  • これにより、必要なリージョナル証跡がすべて作成されます。一つの中央集約型の証跡のように見えますが、実際にはデータを中央の受信先に送る複数のリージョナル証跡の集まりです。
  • 選択肢 1: ニアリアルタイムのアラートが必要なリージョンごとに CloudWatch ルールを作成します。ここでは CloudFormation や Terraform が役立ちます。
  • 選択肢 2: すべてのイベントを一元化します。各リージョン内で、すべての CloudTrail イベントを Lambda 関数または SNS トピックへ送るルールを作成し、そこから目的の宛先へ転送します。私たち自身もこの手法を使っており、カスタム API エンドポイント経由で送信していますが、Kinesis やほぼ任意の宛先へストリーミングすることもできます。

取り組みを始めるにあたって、二つのコードサンプルを紹介します。

CloudTrail からすべてのイベントを収集するための、CloudWatch ルール用の隠れたフィルターパターン:

{

"detail-type": [

"AWS API Call via CloudTrail"

] }

次に、イベントを転送する Lambda コードのサンプルです。この例では Kinesis への転送です。

import json

import boto3

def lambda_handler(event, context):

kinesis = boto3.client('kinesis', region_name='us-west-2')

data = json.dumps(event)

print(data)

response = kinesis.put_record(

StreamName='cloudsec_prod_stream_alert_kinesis',

Data=data,

PartitionKey='test-client-id'

)

print(response)

return {

'statusCode': 200,

'body': json.dumps('Record added')

}

CloudTrail と CloudWatch の連携の仕組み | FireMon