はじめに
こんにちは!
BFT名古屋支店・インフラ女子(?)のやまぐちです。
前回の記事に引き続き、ログインしたらSlackへ通知する、という仕組みの実装について説明をします。
前提としてセキュアなログイン、その後の操作をするための仕組みは前回の記事を参照ください。
なお、調べた限りAWSで実現するのに3通りのやり方がありました。
① CloudTrail→CloudWatch→SNS→Lambda→Slack(この記事)
② CloudTrail→CloudWatch Event→Lambda→Slack
③ CloudTrail→CloudWatch→ChatBot→Slack
②はEventだとサインインの仕組み上うまく取れない場合があるためやめました(検証済)。詳しくはこちらの記事に記載されています。
CloudTrailが記録するConsoleLoginイベントについて | DevelopersIO
③ も試してみたい気持ちはありましたが最終的にアラームの内容をそのまま転送するのではなく(これはわかりにくいのであまりオススメしません)必要な情報だけ整形してSlackへ通知したいためどうしてもLambdaを挟む必要があり、だったら①だよね、ということで①の方法で実現しています。
特定のIAMユーザがAWS管理コンソールへログインした際にSlackへ通知が飛ぶ仕組みの実装
全体像はこちらです。ここではそれぞれのサービスの細かい仕様は説明しませんが、必ず公式サイトで仕様と何にどのくらい費用がかかるのかを確認しながら進めてください。
手順は大きく分けて3つ。
- CloudTrailを有効にしてCloudWatchへログを転送する
- LambdaでSlackへ連携する関数を作成する
- CloudWatchでアラームを作成し、SNSを介してLamdaを実行する
前半と後半部分を作り、最後に真ん中を結んでひとつなぎにします。
1.CloudTrailを有効にしてCloudWatchへログを転送する
AWS CloudTrail(ユーザーアクティビティと API 使用状況の追跡)| AWS
CloudTrailはAWSで行われた操作やAPIの呼び出し状況をログとして出力してくれるAWSサービスです。これを有効にしておくと「いつ」「誰が」「何をしたか」ということを後から確認することができます。
CloudTrailを有効にするとCloudTrail画面で表示される様々なイベントをS3へ転送し保存することができます。一つ目の証跡に関してはこれにかかる費用は無料です(保存先のS3の費用はかかるのと、CloudTrailでも一部設定を有効にすると費用がかかるものがあります)。
また、今回はアラームを作成できるようにCloudTrailでCloudWatch Logsへの連携を行いますので、CloudWatch Logs側の費用がかかることに注意です。
料金 - Amazon CloudWatch | AWS
設定はそんなに難しくありません。
デフォルトではチェックが入っていないので、CloudWatch Logsの [ 有効 ] にチェックを入れます。ロググループやロール名も後でわかりやすいように命名します。
2.LambdaでSlackへ連携する関数を作成する
設定は後ろ(つまりSlack)から順次行います。
Slackの設定
Slackのようなチャットツールだと何かしら外部のアプリケーションと連携してメッセージを受け付けるような仕組みが用意されています。まずはSlack側でメッセージを受け付ける準備をします。
- メッセージを受け付けるチャンネルの作成
- そのチャンネルに通知するためのURL(Incoming Webhook)の作成
チャンネルの作成は説明不要だと思いますので、Incoming Webhookの作成の部分だけ少し説明です。
Slack上の [ アプリを追加する ] を押し、検索ウィンドウに [ Incoming ]
と入れると [ Incoming Webhook ] というアプリが表示されるので [ 追加 ] を押します。
するとブラウザが開くので [ Slackに追加 ] を押し、投稿先のチャンネルを選択、 [ Incoming Webhookインテグレーションの追加 ] を押します。
ここで表示される [ Webhook URL ] は前段のLambdaで設定するSlackへの通知先となるのでコピーしておきます。また、名前をカスタマイズのところも元々 [ Incoming-webhook ] となっているところを [ AWS ] に変更しています。これはSlackへ通知する時のユーザ名なのでわかりやすくしました(が、Slackへはなぜか " Incoming-webhook "という名前で送信されているので後で原因は確認しようと思います )。
SNSの設定
次にLambdaの設定時にLambdaを実行する契機(トリガー)を指定するため、先にSNS(Simple Notification Service)でトピックを作ります。
「トピックってなに?話題?」と聞きなれない言葉ですが、SNSはPub/Subメッセージングモデルでメッセージを送信する仕組みであり、メッセージを送る側(パブリッシャー)とメッセージを受け取る側(サブスクライバー)は互いを認識する必要がない(=疎結合)という特徴があります。
詳しくはこちらなどで。
今回はトピックだけが必要なので、さくっと名前だけ決めて作成です。費用も無料枠の中で賄えます。
料金 - Amazon SNS | AWS
Lambdaの設定
さて、問題はLambdaです。
Lambdaとはサーバが不要で、プログラムを書いて実行する環境だけ借りられるAWSのサービスです。一か月ごとに100万リクエスト、40万GB-秒コンピューティング時間の無料枠があります。
料金 - AWS Lambda |AWS
LambdaではSlack連携用の設計図が事前に用意されているので(便利!)これを利用していきます。cloudwatch-arlarm-to-slack-pythonという設計図を使います。
元のテンプレートをそのまま利用せずに少し変更した箇所が以下の三点です。
- KMSという暗号鍵のサービスは利用しない
- Lambda上でSlackへの情報を暗号化する必要なし
- ↑のため、Slack通知用のURLを複合化(暗号を元に戻す)操作が不要
テストデータは以下のサイトのものを参考にさせていただきました!
【無料】CloudWatchのアラームをSlackへ通知する(Python版)備忘録 - Qiita
テスト実行してみてSlack側に通知が来ることを確認ができれば完了です。
3.CloudWatchでアラームを作成し、SNSを介してLamdaを実行する
ここまででCloudTrail→CloudWatch Logsへのログ転送、SNS→Lambda→Slackへの通知を設定しました。最後にCloudWatchでアラームを作成し、作成したSNSトピックへのアクションを登録することで、最初から最後までつなげていこうと思います。
メトリクスフィルタを作成する
CloudWatch LogsでCloudTrailのログを確認できるので、ここでアラームとしたいログを探します。よくあるのは "error" や "Error" (大文字小文字は区別されます)などで問題があった場合にアラームをあげるようにする設定ですが、今回はサインインイベントです。例えばログインした場合のログ(一部マスク)がこちらです。
{ "eventVersion": "1.08", "userIdentity": { "type": "IAMUser", "principalId": "XXXXXXXX", "accountId": "XXXXXXXX", "accessKeyId": "", "userName": "XXXXXXXX" }, "eventTime": "2021-06-08T07:53:06Z", "eventSource": "signin.amazonaws.com", "eventName": "ConsoleLogin", "awsRegion": "us-east-1", "sourceIPAddress": "XX.XX.XX.XX", "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36", "requestParameters": null, "responseElements": { "ConsoleLogin": "Success" }, "additionalEventData": { "LoginTo": "https://console.aws.amazon.com/console/homeXXX", "MobileVersion": "No", "MFAUsed": "Yes" }, "eventID": "27d68ec1-1016-46b5-8fd3-31d9cdc74f95", "readOnly": false, "eventType": "AwsConsoleSignIn", "managementEvent": true, "eventCategory": "Management", "recipientAccountId": "XXXXXXXX" }
上のようなログのみを抽出するために条件を以下としました。フィルタ条件の書き方はちょっとクセがあります。かなり強めのクセでいろいろ制限があるので注意が必要です。個別にログインするユーザ名を指定すると条件が長くわかりにくくなるため、MFAを有効にしている条件を追加することで、弊社の別チームのログインと区別しています。
■ フィルタ条件
{ ($.userIdentity.type = "IAMUser") && ($.eventName = "ConsoleLogin") && ($.additionalEventData.MFAUsed = "Yes") && ($.eventType = "AwsConsoleSignIn")}
この抽出条件さえ決まればあとは勝ったも同然です。
フィルターとパターンの構文 - Amazon CloudWatch Logs
目的のログを抽出できる条件が描けたら [ Create Metrics Filter ] でメトリクスフィルタを作成します。
「メトリクス」は測定・管理している指標、と思ってください。これを設定することで、今回で言うとログインした回数をグラフで表示できるようになります。
アラームの作成
メトリクスフィルタができたらその流れでアラームを作成します。
AWSへサインインしたログを検知するとカウントアップしていく設定です。
うまくいくか確認してみた
なお、この仕組みだとログインしたことをリアルタイムまたはにアリアルでは検知できないのが難点です。
仕様上CLoudTrailでサインインイベントを確認するまでに最大15分くらいかかります。また、CloudTrail→CloudWatch Logsへも最大5分、その後のアラームの検知でも最大5分なので、最悪ログインしてから通知されるまでに25分ほどかかることもありそうです。
今回のテスト結果はこちらの通り、13分後にSlackに通知されることを確認できました。
# | 項目 | 確認場所 | 時間 | 備考 |
---|---|---|---|---|
1 | ログインの実施 | PCの時間を目視 | 13:08 | |
2 | ログへの出力-1 | CloudTrail | 13:08 | |
3 | ログへの出力-2 | CloudWatch Logs | 13:20 | 実際のログインから12分遅れ。 |
4 | アラームのアクション実行 | CloudWatch Alarm | 13:21 | |
5 | Slackでのメッセージ受信 | Slack | 13:21 |
CloudWatch Logsの時間はイベントの中身の時間ではなく、CloudWatch Logsで受信した時間です。ログの中身を見るとログインの時間は13:08になっていました。
終わりに
これでAWSのマネジメントコンソールへサインインした際にSlackへ通知する仕組みの実装について、2回に分けた説明が完了です。
最初うまくいかず何でだ~!といろいろ確認していたらどうやらAWS上で認識されるのに時間がかかっていたようで気が付いたらできているというオチ。。。まぁいろいろ調査できたのでよい時間でした。
Slackに通知が行くようにはなったのですが、まだ送られるメッセージがアラーム内容そのままのため「誰が」ログインしたのかということがわかりません。
こちらの変更はまた次の回で実施、説明させていただければと思います。
(2021/06/14追記)
アラームの内容だったのをログの内容の一部を出力するように変更しました!
bftnagoya.hateblo.jp
ここまで読んでいただきありがとうございました~^ ^