こんにちは! ROBOT PAYMENT サブスクペイのシステム基盤チームの 高尾 です。
システム基盤チームではアプリケーションやシステムインフラの基礎部分の構築や管理、メンテナンス、性能改善などを行っています。 今回はAWSコンソールへのログイン検知機構について、rootログインやSwitchRoleの検知・通知機能を追加したお話をしようと思います。
はじめに
今回の変更の背景を説明します。
弊社ではセキュリティ上の観点から、AWSアカウントにコンソールログインしたユーザを検知して技術チーム宛てにログイン通知が送信されるようにしています。
通知の仕組みとしては、下記のAWSドキュメントにも記載のあるコンソールログインが記録されるリージョンにEventBridgeを配置し、コンソールログインイベントを検知したらSlack通知用のLambdaを発火させるという流れを採用していました。
参考:コンソールログインに関するAWSドキュメントの「注記」 https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/cloudtrail-event-reference-aws-console-sign-in-events.html
そんな中、普段利用するユーザの権限にはない特定の操作をするため、専用ロールへのSwitchRoleを取り入れる動きがありました。
これにより今回のログイン検知機構の改修に至ったというわけです。
ちなみにrootログインについては従来の仕組みでも検知はできるようになっていましたが、この際通知時のメッセージ内容をrootログイン用にするようにしました。
改修内容
ログイン検知から通知までのフローですが、下記のように変更をしました。
(改修前)
AWSログイン → EventBridgeでCloudTrailの”ConsoleLogin”イベントを検知 → LambdaでSlack通知
※CloudTrail、EventBridge、Lambdaともリージョン①に配置
(改修後)
AWSログイン → CloudTrail用のS3バケットでログファイル配置時にイベント通知 → SNS → SQS → LambdaでSlack通知 → DynamoDBにイベントIDを格納
※CloudTrail、SQS、LambdaおよびDynamoDBはリージョン①、S3バケットとSNSはリージョン②に配置
改修後のフローが回りくどくなっている感じがありますが、これはSwitchRoleアクセスしたときのリージョンがリージョン①以外である場合に、既設のEventBridgeではアクセス検知ができないためです。
もしもリージョン①以外でもEventBridgeでSwitchRoleを検知するとしたら、各リージョンに既設と同じ設定のEventBridgeルールを作成する必要があり、手段としては非現実的と判断しました。
そこで、CloudTrailをマルチリージョン化し、CloudTrail用S3バケットのイベント通知でSlack通知用Lambdaを呼び出すことにしました。
しかし元々CloudTrail用のS3バケットはリージョン②にあり、S3バケットのイベント通知では他リージョンのLambdaを直接呼び出せないため、SNSとSQSを挟むことでリージョン跨ぎを実現したのです。
実装で苦労した点
AWSリソース構成については、SNSとSQSを追加しそれぞれのポリシーにアクセス元を設定するだけで苦労は無かったのですが、Lambdaコードの書き換えには苦戦しました。
Lambdaコード改修事項①
(ユーザログイン、rootアクセスおよびSwitchRoleアクセスの切り分けおよび抽出情報の整理)
以前だとEventbridgeからコンソールログインの都度、ログインイベントがLambdaに送られてきました。
参考:
https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/cloudtrail-event-reference-aws-console-sign-in-events.html#cloudtrail-event-reference-aws-console-sign-in-events-iam-user
そのためLambdaコードではイベントログのイベント発生時間やユーザ名、ログインの成否を抽出し、あとはSlack通知処理部分のメッセージの型にそれらの情報を渡すことで完結していました。
しかし改修後はrootやSwitchRoleアクセスのイベント情報も抽出対象となります。
これらのイベントはデータ構造は基本的に同じなのですが、所々項目の値や項目名そのものが異なる部分があったため、Lambdaコードでは抽出項目の組み合わせによりどのアクセスなのかを切り分けるようにしました。
アクセス先特定のための抽出事項:
| 抽出項目 | SSOユーザのイベントレコード | rootのイベントレコード | SwitchRoleのイベントレコード | 備考 |
|---|---|---|---|---|
| アクセスユーザのタイプ |
"userIdentity": { "type": "AssumedRole", ... } |
"userIdentity": { "type": "Root", ... } |
"userIdentity": { "type": "AssumedRole", ... } |
"userIdentity" の "type" が "Root" なら root アクセスと判断 |
| イベント名 | "eventName": "ConsoleLogin", | "eventName": "ConsoleLogin", | "eventName": "SwitchRole", | "eventName" が "SwitchRole" なら SwitchRole アクセスと判断 |
上記の表より、アクセス判断は下記のようになります。
・rootz
「アクセスユーザのタイプ」が “Root” である場合はrootアクセス
・SwitchRole
「イベント名」が “SwitchRole” である場合はSwitchRoleアクセス
・ユーザ
「アクセスユーザのタイプ」が “AssumedRole” かつ「イベント名」が “ConsoleLogin” である場合はユーザアクセス
また、rootアクセスはアクセス元IPを、SwitchRoleアクセスはスイッチ元情報を抽出するようにしました。
アクセス元特定のための抽出事項:
| 抽出項目 | root のイベントレコード |
SwitchRole のイベントレコード |
備考 |
|---|---|---|---|
| アクセス元IP | "sourceIPAddress": "IPアドレス", | - | アクセスを試みている端末のグローバルIPアドレス |
| スイッチ元 | - |
"additionalEventData": { "SwitchFrom": "ユーザARN", .... }, |
SwitchRole元のユーザARN |
ログインの成否についてもイベントレコードがSwitchRoleのみ異なります。
ログイン成否の抽出事項:
| 抽出項目 | SSOユーザ のイベントレコード |
root のイベントレコード |
SwitchRole のイベントレコード |
備考 |
|---|---|---|---|---|
| ログイン成否 |
"responseElements": { "ConsoleLogin": "[Success, Fail]" }, |
"responseElements": { "ConsoleLogin": "[Success, Fail]" }, |
"responseElements": { "SwitchRole": "[Success, Fail]" }, |
SwitchRole の時は項目名が "SwitchRole" |
結果として、Slack通知処理に渡す情報は下記のようになりました。
- イベント発生時間
- ユーザ名(SwitchRoleの場合はスイッチ先ロール名)
- ログインの成否
- アクセス元IP(rootアクセス時のみ取得)
- スイッチ元(SwitchRole時のみ取得)
Lambdaコード改修事項②
(SQSから渡される情報のうち以前に受け取った内容の処理)
改修事項①でそれぞれのアクセス通知自体はできるようになったのですが、今度は別の問題が発生しました。
いくつかの一度通知した内容が再度通知されるようになったのです。
どうもCloudTrailのログ配信タイミングに起因しているようです。
(参考)https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/how-cloudtrail-works.html#how-cloudtrail-works-trails
「CloudTrail は、通常、API コールから平均 5 分以内にログを配信します。この時間は保証されません。」
そこで、Slack通知処理の際にログインイベントのIDを参照するようにし、過去に処理したIDかを確認する仕組みも追加しました。
処理の流れは以下のとおりです。
①Lambda処理開始
②イベントレコードからイベントIDを抽出
③抽出したイベントIDが既にDynamoDBにあるか確認
あれば次のイベントIDを抽出(②に戻る)
無ければ通知に必要な情報を抽出
④通知処理
これにより、同じログインのイベント通知が再送されることは回避されるようになりました。
以上でLambdaの改修は完了となります。
まとめ
いかがでしたでしょうか。
今回はAWSコンソールログインに加え、rootやSwitchRoleアクセスの検知にも対応したリソース構成と通知処理の概要をご紹介いたしました。
当初はSwitchRoleのイベントログもリージョン①に出力されるものと思い込んでいたため、テスト実装して検知できないこともあると分かったときにはかなり焦りましたが、冒頭で示したフローによりリージョン間のリソース呼び出しが可能という知見が得られたので、今後似たような状況に直面した際に生かせそうです。
この記事がこれからログイン検知機構の新規構築、または改修しようとしている方のご参考になれば幸いです。
最後までお読みいただきありがとうございました。
We are hiring!!
ROBOT PAYMENTでは一緒に働く仲間を募集しています!!!