AWS マネジメントコンソールへのログイン通知(2022年版)


My Redmine

岩石です。
先日AWS認定ソリューションアーキテクト-アソシエイト試験を受け無事更新できました。
3年前に合格した時は、更新までに同資格のプロフェッショナル合格とGoogle Cloudの認定の取得という気持ちで満ち溢れていましたが、日々過ごしているうちに...
50歳より60歳の方が近くなり年々学ぶということが難しくなってきてますが、次回更新の3年後悠々とするためにも、また他の資格に向かっていくためにもスケジュールを組んで取り組んでみようと考えています(Redmineで管理する?)。

マネジメントコンソールへのログインイベントの通知

(お断り: 本文中においてAWS様のサービス名称の「Amazon 〜」の Amazon について、表示上の煩雑さを省く意味で省力させていただいてる箇所があります。)

AWSのマネジメントコンソールへログインがあった場合、メールなどを使用して認知したいことって個人でも組織でもありますよね。特定のアカウントに誰がログインしたとか、誰かが不正にアクセスしようとしたとか把握することは重要なことです。どうやってその設定を行うかネットで検索すると CloudTrial・EventBridge・Amazon SNS を連携して行う方法がよく紹介されています。

昨年まではそれで簡単に実現できましたが、2021年11月にCloudTrailの仕様が変更となり、ログインのイベントが従来us-east-1リージョンで行われ記録されていたものが、不特定のリージョンでログイン処理が行われ、処理が実施されたリージョンにて証跡が記録されるようになりました。

参考:
CloudTrail のコンセプト - AWS CloudTrail

EventBridgeはAWS内で起きたイベントをトリガーに処理をさせることができるサービスです。イベントのソースを指定する必要がなく、またサーバレスに使用できるため導入が簡単なのが特徴です。

EventBridgeでのマネジメントコンソールログインのイベント検出は数行の記載だけで行えるため、導入しやすかったのですが、リージョン単位での検出しか対応していません。利用者がログインリージョンを指定することで運用的には検出も可能になるのですが、悪意を持った誰かがログインしようとしたイベントなどはどこのリージョンに記録されるか知り得ることはできないため、この方法ではAWSのすべてのリージョンにEventBridgeのルールを記載する必要があります(実際にはEventBridgeに対応したSNSトピック・サブスクリプションも全リージョンに登録する必要あり)。

現実的にはそのような対応は難しいためリージョンを跨いで検出をするように作成する必要があります。

代替案として

  1. CloudTrailの証跡をCloudWatch Logsに出力 し、
  2. 同サービスの サブスクリプションフィルターでConsole Loginを検出 して、
  3. SNSを呼び出すLambdaを起動 する

という方法を設定します。
(自分で考えたのではなくてAWSサポートのご支援をいただきました)

CloudTrailの設定

マルチリージョンの証跡の設定

CloudTrailで証跡を作成するのは以前と同じですが、 マルチリージョンの証跡 を作成する必要があります。新規に証跡を作成する場合はチェックを入れるだけですが、既に作成済みの証跡ではマルチリージョン対応されてない可能性があります。

マネジメントコンソールではマルチリージョン対応への変更ができないのでAWS CLIを使用して次のコマンドで変更します。もしコマンド実行のためにわざわざIAMユーザを発行するのであれば、Cloud Shellを使用した方がお手軽でかつ余分なユーザを作らないでよいのでおすすめです。

aws cloudtrail update-trail --name [証跡名] --is-multi-region-trail

参考:
CloudTrail ログ ファイルを複数のリージョンから受け取る

CloudWatch Logsへログを出力

ログを保管するためにS3に証跡が保管されてますが、ここから検出するにはAmazon Atenaなどを使って探し出すことになります。
これはこれで良い方法だと思いますが、それほど複雑な検索では無いことや、その後の通知への連携を考えCloudWatch Logsへ出力し監視をすることにします。
マネジメントコンソールから登録すると、ロググループやそこへ出力するロールのポリシーなど自動的に作成してくれるので簡単です。

参考:
CloudWatch Logs へのイベントの送信

SNSの登録

今回は通知にAmazon SNSを使用してメールを送信します。この内容については従来の方法と全く違いはありません。

登録後送られてきた認証メールに応答することでサブスクリプションが登録されます。

登録するメールアドレスがメーリングリストなど共有のものである場合、誰かがunscribeのリンクを使用して解除してしまう可能性があります。次のURLで紹介されているような対応でそういったトラブルを防ぐことができるようです。
Amazon SNS のサブスクリプション解除リンクを無効化もしくは削除する方法を教えてください | DevelopersIO

SNSを呼び出すLambda

SNSを呼び出すLambda関数を作成します。このLamnda関数はCloudWatch Logsから呼び出されるため、同じリージョンに配置する必要があります。また実行関数には当該のSNSトピックのpublish許可を含むポリシーを付与してください。

下記はトピックを絞らない場合のサンプルポリシーです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sns:publish"
            ],
            "Resource": "*"
        }
    ]
}

Lambdaへ送られてくるログイベントはJSON形式をgzipで圧縮しそれをBase64でエンコードしたものです。JSON形式のまま通知を送信しても良いですが、項目が多いのとJSONの見にくさという課題があります。どうせLambdaを使用しますので、必要項目を整形して通知したいと思います。Base64デコード・gzip解凍したらあとはJSONから必要な項目を抜き出すだけです。

Rubyでのサンプルコードを載せておきますが、プログラミングスキルが著しく低いので😅処理の意味だけ汲み取ってください🙇‍♂️

require 'json'
require 'aws-sdk'
require 'zlib'
require 'base64'

def lambda_handler(event:, context:)
    aws_account_name = "[**AWS Account**]" # AWSアカウントの名称・通称
    aws_sns_region = "[**SNS Region**]" # 通知に使用するAmazon SNSが登録されてるリージョン
    aws_sns_topic_arn = "[**SNS arn**]" # 通知に使用するAmazon SNSのトピックARN
    gzip = Zlib::Inflate.new(Zlib::MAX_WBITS + 32) 
    event_gzip = Base64.decode64(event['awslogs']['data'].to_s)
    event_json = JSON.parse(gzip.inflate(event_gzip))
    event_json['logEvents'].each {|log|
      event_message_json = JSON.parse(log['message'])
      if event_message_json['eventName'] == "ConsoleLogin" || event_message_json['eventName'] == "SwitchRole"
        if event_message_json['userIdentity']['type'] == "IAMUser" || event_message_json['userIdentity']['type'] == "Root"
          message = <<EOS
#{event_message_json['eventName']} #{event_message_json['responseElements']['ConsoleLogin']} on AWS #{aws_account_name}
Event Time: #{event_message_json['eventTime']}
Region: #{event_message_json['awsRegion']}
Access From: #{event_message_json['sourceIPAddress']}
Account Type: #{event_message_json['userIdentity']['type']}
Account Name: #{event_message_json['userIdentity']['userName']}
Use MFA: #{event_message_json['additionalEventData']['MFAUsed']}

EOS
        elsif event_message_json['userIdentity']['type'] == "AssumedRole"
          message = <<EOS
#{event_message_json['eventName']} on AWS #{aws_account_name}
Event Time: #{event_message_json['eventTime']}
Region: #{event_message_json['awsRegion']}
Access From: #{event_message_json['sourceIPAddress']}
Account Type: #{event_message_json['userIdentity']['type']}
Account Name: #{event_message_json['additionalEventData']['SwitchFrom']} #{event_message_json['additionalEventData']['SwitchTo']}

EOS
        else
          message = event_message_json.to_s
        end
        sns = Aws::SNS::Resource.new(region: aws_sns_region)
        topic = sns.topic(aws_sns_topic_arn)
        topic.publish({
          subject: "Login to Management Console on AWS #{aws_account_name}",
          message: message
        })
        { statusCode: 200, body: JSON.generate(message) }
      end
    }
end

CloudWatch Logs

最後にCloudWatch Logの設定です。CloudTrailで設定したCloudWatch LogsのLog Groupを開きます。

サブスクリプションフィルターを表示し、 作成 から Lambdaサブスクリプションフィルターの作成 を選択し下記の内容で登録します。

ストリーミングを開始 を押下で登録完了し監視が始まります。

参考:
サブスクリプションを使用したログデータのリアルタイム処理

あとは別のブラウザかプライベートウィンドウでAWSへのログインや認証の失敗などを起こしてみてください。

通知内容

仕組みの都合上、従来の方法より通知は少し遅れて行われます。

上記のコードでこんな感じのメールが来るようになります。

ConsoleLogin Success on AWS test-farend
Event Time: 2022-06-16T01:23:45Z
Region: us-west-2
Access From: x.x.x.x
Account Type: IAMUser
Account Name: iwaishi
Use MFA: Yes

またSwitch Roleでのログインでは

SwitchRole on AWS test-farend
Event Time: 2022-06-16T01:23:45Z
Region: us-east-1
Access From: x.x.x.x
Account Type: AssumedRole
Account Name: arn:aws:iam::XXXXXXXXXXXX:user/hogehoge

といったメールが通知されます。

なお、このコードではAppStream2.0へSAML認証でログインした場合など、通常のAWSマネジメントコンソールへのログイン以外の AwsConsoleSignIn イベントの通知にきちんと対応してません。

ログイベントのフィルターを使ってどのようなイベントが記録されているかなど確認し、一度JSONのすべてを取得してから必要な項目を選んで整形などチューニングしていくのが良いかと思われます。

お知らせ

最後までご覧いただきありがとうございました。

弊社ではAWSを活用したソリューションの企画・設計・構築・運用や、Ruby on Rails・Javascriptフレームワークなどを使用したアプリケーション開発を行うスタッフを募集しております。
ご興味をお持ちの方は弊社Webサポート窓口へのご連絡か、弊社社員と繋がりをお持ちの方は社員を通じてご連絡ください。

お問い合わせ

My Redmine

こちらの記事もオススメです!
Redmineに関するオンラインセミナーを始めて1年経ったふりかえり
オンラインセミナーを初めて開催してから1年経ちました。開催にあたり準備したことや良かったことをふりかえります。
中国の小中高生向けオンライン教育プラットフォーム「国家中小学智慧教育平台」がすごい
「国家中小学智慧教育平台」は教科書のPDFが無料でダウンロードでき、また多くの科目の講義動画を見ることができる。
ファーエンドテクノロジーに入社しました
2022年4月に入社しました。開発やマーケティングの支援を行なっています。
チケットの作成時間を短縮!RedmineのCSVインポート機能はやっぱり便利
RedmineのCSVインポートの機能でチケットの作成時間を短縮。タスク漏れ防止にもなりました。
ウェブ・セキュリティ基礎試験(徳丸基礎試験)に合格しました
ウェブ・セキュリティ基礎試験(徳丸基礎試験)に向けてやった勉強内容を紹介します。
ファーエンドテクノロジーからのお知らせ(2024/04/24更新)
入門Redmine 第6版 出版記念企画セミナー「Redmineのアクセス制御」【2024/5/30開催】
入門Redmine 第6版(2024年3月23日発売)の書籍から「Redmineのアクセス制御」について解説します。
My Redmine 初回ご契約で「入門Redmine 第6版」プレゼントのお知らせ
Redmineのクラウドサービス「My Redmine」を初めてご契約いただいたお客様にRedmine解説書「入門Redmine 第6版」を進呈いたします。
2024年度ブランドパートナーに島根県在住のモデル ユイさんを継続起用
ユイさん(モデルスタジオミューズ所属)をファーエンドテクノロジーの2024年度ブランドパートナーとして継続して起用します。
My Redmine スタンダードプランおよびAdminサポートデスクプランの料金改定のお知らせ【2024年4月ご利用分より】
2024年4月ご利用分より、My Redmine スタンダードプラン(民間企業・個人向け及び官公庁向け)とAdminサポートデスクプランの料金を改定いたします。
Redmineの最新情報をメールでお知らせする「Redmine News」配信中
新バージョンやセキュリティ修正のリリース情報、そのほか最新情報を迅速にお届け