ポリシーリスクを可視化。自然な言葉でポリシーに関する疑問を解決します。 デモを申し込む →
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')
}