CloudTrailとEventBridgeルールを使って特定のIAMたちを管理したい
こんにちは、せーのです。
今日は特定のIAMユーザーの作成や削除、また特定のアカウントからスイッチロールでやってくる特定IAMの管理をCloudTrailとEventBridgeを使って実装してみたいと思います。
想定していること
- 外部のベンダーさんにシステム構築を発注している
- プロジェクトの終了などのタイミングでIAMの整理が行われる
- 後日監査の意味で入退場を把握したい
概要
IAMに関する操作は全てAPI操作なので、APIを蓄積してあるCloudTrailを使うのが一番です。
CloudTrailイベントはEventbridgeで引っ掛けることができますので、イベント内容を指定して、対象ユーザーを絞った上でルールとして設定すれば、リアルタイムにて検知することができます。
検知したイベントはSNSと連携してメールやSlackに飛ばしたり、Lambdaなどに飛ばして加工したり自由に処理することができます。
やるべきアーキテクチャはシンプルなのですが、検知すべき内容を調べるのが手間なんですよね。
手順
EventBridgeルールの作成
検知はEventbridgeの「ルール」を作成することで実現できます。
まずマネージメントコンソールよりEventbridgeのページに飛んで、左ペインの「ルール」をクリック、中央の「ルールを作成」ボタンから新規ルール作成画面に飛びます。
名前をつけます。ルールタイプは「イベントパターンを持つルール」です。
次にイベントパターンを作ります。
イベントソースは「AWSイベントまたはEventbridgeパートナーイベント」を選択します。
イベントパターンはカスタムにし、JSONにて直接検知したいパターンを書き込みます。
このJSONは色々なパターンが書けるので、後述します。
JSONが書き終わったらターゲットを決めます。
今回は通知される内容を把握するため、SNSでメールに飛ばしてみようと思います。
SNSのトピックは事前に作っておき、ここではトピック名を選択する形になります。
※参考
これでEventbridgeルールの完成です。ルールは後からでも修正できますので、JSONを色々書き換えてみましょう。
JSONルールを色々ためしてみる
IAMの作成、削除
まず最初にIAMの作成、削除を検知します。これはCloudTrailの「Createuser」「Deleteuser」を検知すればOKです。
JSONとしてはこうなります。
{
"source": ["aws.iam"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["iam.amazonaws.com"],
"eventName": ["CreateUser", "DeleteUser"]
}
}
ただ、これだと全てのIAMユーザーのCreate, Deleteを検知してしまうので、特定ユーザーに絞ってみます。
{
"source": ["aws.iam"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["iam.amazonaws.com"],
"eventName": ["CreateUser", "DeleteUser"],
"requestParameters": {
"userName": ["cm-seinoIAM-test"]
}
}
}
こうするとcm-seinoIAM-test
というIAMユーザーがCreate、Deleteされたりした時だけ、検知されます。
複数ユーザーの場合
一人だけ検知しても、なかなかシステム的な運用は厳しいです。JSONのうちIAMユーザー名を指定しているuserName
は配列形式になっているので、複数のユーザーを列記することができます。
{
"source": ["aws.iam"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["iam.amazonaws.com"],
"eventName": ["CreateUser", "DeleteUser"],
"requestParameters": {
"userName": ["cm-seinoIAM-test", "cm-jinnoIAM-test"]
}
}
}
これでcm-seinoIAM-test
とcm-jinnoIAM-test
という2つのIAMユーザーのCreate/Deleteを検知できるようになりました。
prefixとsuffix
対象の人が増えるたびにこのルールを書き換えていくのはなんだか面倒です。こんなときにはprefix(先頭の文字が一致)やsuffix(最後の文字が一致)を使ってみましょう。
{
"source": ["aws.iam"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["iam.amazonaws.com"],
"eventName": ["CreateUser", "DeleteUser"],
"requestParameters": {
"userName": [{ "prefix": { "equals-ignore-case": "cm-" }}]
}
}
}
こう書くことで、cm-
から始まるIAMユーザー全てを検知してくれます。
上の例ではequals-ignore-case
をつけることで大文字・小文字関係なく反応するようにしています。サービスです。
さて、先頭や最後の文字の一致で検出するにはもう一つ、wildcard
という方法もあります。
{
"source": ["aws.iam"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["iam.amazonaws.com"],
"eventName": ["CreateUser", "DeleteUser"],
"requestParameters": {
"userName": [{ "wildcard": "cm-*" }]
}
}
}
このように書くことでprefixと同じ効果がでます。この書き方はこの後書くスイッチロールで役に立ちます。
入力トランスフォーマー
こうして検知したイベントをそのままSNSからメールに投げると、このようなメッセージとなります。
{
"version": "0",
"id": "********-****-****-****-************",
"detail-type": "AWS API Call via CloudTrail",
"source": "aws.iam",
"account": "************",
"time": "****-**-**T08:43:29Z",
"region": "us-east-1",
"resources": [],
"detail": {
"eventVersion": "1.10",
"userIdentity": {
"type": "AssumedRole",
"principalId": "********************:cm-seino.tsuyoshi",
"arn": "arn:aws:sts::************:assumed-role/cm-seino.tsuyoshi/cm-seino.tsuyoshi",
"accountId": "************",
"accessKeyId": "********************",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "********************",
"arn": "arn:aws:iam::************:role/cm-seino.tsuyoshi",
"accountId": "************",
"userName": "cm-seino.tsuyoshi"
},
"attributes": {
"creationDate": "****-**-**T08:07:35Z",
"mfaAuthenticated": "true"
}
}
},
"eventTime": "****-**-**T08:43:29Z",
"eventSource": "iam.amazonaws.com",
"eventName": "CreateUser",
"awsRegion": "us-east-1",
"sourceIPAddress": "***.***.***.**",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36",
"requestParameters": {
"userName": "cm-seinoIAM-test"
},
"responseElements": {
"user": {
"path": "/",
"userName": "cm-seinoIAM-test",
"userId": "********************",
"arn": "arn:aws:iam::************:user/cm-seinoIAM-test",
"createDate": "****-**-** 8:43:29 AM"
}
},
"requestID": "********-****-****-****-************",
"eventID": "********-****-****-****-************",
"readOnly": false,
"eventType": "AwsApiCall",
"managementEvent": true,
"recipientAccountId": "************",
"eventCategory": "Management",
"tlsDetails": {
"tlsVersion": "TLSv1.3",
"cipherSuite": "TLS_AES_128_GCM_SHA256",
"clientProvidedHostHeader": "iam.amazonaws.com"
},
"sessionCredentialFromConsole": "true"
}
}
さすがCloudTrail、証跡ということでとても細かいのですが、細かすぎてよくわかりません。
今回欲しいのはCreate/Deleteの日時の対象のIAMユーザー名だけなので、その情報にフィルタリングしてみたいと思います。
ここでは「入力トランスフォーマー」という機能を使ってみましょう。
作成したEventbridgeルールを編集し、ターゲットの項目まで進めると、ターゲットの下に「追加設定」というトグルがありますので開きます。
ターゲット入力を「入力トランスフォーマー」とし、「入力トランスフォーマーを設定」ボタンをクリックします。
入力トランスフォーマーは「入力パス」と「テンプレート」、「出力」の欄に分かれています。
ターゲットへの入力としてきたJSON、今回で言えばCloudTrailから発出されたJSONのうち必要な項目を変数: JSONの値
という形で「入力パス」にまとめます。今回必要なのは「いつ」「誰が」「何をした」なので、それぞれeventTime, username, eventNameの3つを同じ名前の変数としてまとめます。
次に「テンプレート」です。テンプレートは実際にターゲットに投げる際にどのような形にするか、を設定します。
例えばメールの文章のような形にしてもよいですし、後で処理をしやすい形を考えます。今回はCSVの形にしてみました。
最後に「出力」ですが、これは設定した入力パス、テンプレートがあっているかどうかをテストする項目です。
上の「サンプルイベント」という項目に実際に送られてくる元のJSON(今回でいえばCloudTrailからのJSON)をセットし、「出力を生成」ボタンを押すと、結果が「出力」に表示されます。
これで入力トランスフォーマーの設定は完了です。
この状態でcm-で始まるIAMユーザーが作成、削除されれば、CSVの形でメールが飛んでいく事になります。
ターゲットをLambdaにして、受け取ったCSVをファイルに書き込んでS3などに保存すればcm-のユーザー専用の入退場管理ができます。
スイッチロール
もう一つ、別アカウントにいるIAMユーザが管理しているアカウントにスイッチロールしてきた場合を検知してみましょう。JSONはこのようになります。
{
"source": ["aws.signin"],
"detail-type": ["AWS Console Sign In via CloudTrail"],
"detail": {
"eventSource": ["signin.amazonaws.com"],
"eventName": ["SwitchRole"],
"additionalEventData": {
"SwitchFrom": ["arn:aws:iam::123456789012:user/cm-seino.tsuyoshi"]
}
}
}
これで特定のアカウント123456789012
からスイッチロールしてきた特定のユーザーcm-seino.tsuyoshi
を検知できます。
SwitchFromとは「どこからスイッチロールしてきたのか」という情報で、arnの形で入ってきます。同じように「どこへ行くのか」というSwitchToという項目もありますが、今回必要なのは「どこからきたのか」なのでSwitchFromを検知対象にしています。
このSwitchFromの部分を先程ご紹介した「wildcard」を使って汎用化させてみましょう。
{
"source": ["aws.signin"],
"detail-type": ["AWS Console Sign In via CloudTrail"],
"detail": {
"eventSource": ["signin.amazonaws.com"],
"eventName": ["SwitchRole"],
"additionalEventData": {
"SwitchFrom": [{ "wildcard": "*/cm-*" }]
}
}
}
このようにするとcm-
ではじまるユーザーがスイッチロールしたものが引っかかります。応用で特定のアカウントからスイッチロールしてきたユーザーを全て検知するなら、cm-
の部分をアカウントIDに変えればOKです。
まとめ
CloudTrailにて特定IAMユーザーの作成、削除、特定アカウントからのスイッチロールなどをEventBridgeにて検知する方法をお知らせしました。
他にもCloudTrail Lakeを作成したり、S3バケットに保管されているCloudTrailデータに対してAthenaでクエリ検索する方法もあると思いますが、CloudTrail Lakeは保管されている期間に注意を払う必要がありますし、S3バケットの中にあるCloudTrailデータは大量なので、きちんとパーティショニングしないとコストが掛かる可能性があります。
今回ご紹介したこの方法ですと入退場とスイッチロールした時のみイベントが走りますので、必要最低限のコストで回せるかと思います。どうぞ参考にしてみてください。
参考リンク
https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-create-pattern-operators.html
https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/eb-create-rule.html