IAMユーザーのAWSコンソール・CLIでの操作をSlackに通知する

  • 4
    いいね
  • 0
    コメント

概要

AWSコンソール、AWS CLIでの作業はオペミスがつきものです。
IAMで実行権限を制限する、手作業を減らすなどの対策も大切ですが、不正な変更をいち早く検知する仕組みがあるとより安心です。
今回は、次のような仕組みを実現してみたいと思います。

IAMユーザーのAWSコンソール・CLIでの操作をSlackに通知する

次の記事を参考にさせてもらいました。
セキュリティーグループが変更されたらSlackに通知する -ハンズラボエンジニアブログ

CloudTrailの設定

有効化

まずは、CloudTrailを有効化します。
AWSコンソールにログインし、CloudTrailのメニューを開きます。
まだCloudTrailの設定を行っていな環境では、以下のような画面が表示されるので、「今すぐ始める」を選択します。

start_imidiatory.png

CloudTailの有効化では、任意の「追跡名」を設定します。
※今回はすべてのリージョンを対象に適用します

enable_cloudtail.png

保存先の設定

CloudTrailで取得した追跡情報はログファイルとしてS3に保存されます。次のように保存先バケットを設定を行います。
ストレージの場所で、新しいS3バケットを作成しますかを「はい」にし「S3バケット」の名称を設定します。
※今回はプレフィックスの設定は省略し、その他の項目はデフォルトのまま進めます
スクリーンショット 2017-06-10 16.17.05.png

作成

最後に「作成」を選択してCloudTrailの作成は完了です。

CloudWatch Logsの設定

前記までで作成した追跡情報をCloudWatch Logsへ配信するように設定します。

設定

CloudTrailのメニューを開き、先ほど作成した追跡情報を選択します。
スクリーンショット_2017-06-10_16_19_26.png

CloudWatch Logsの項目にある「設定」を選択します。

スクリーンショット 2017-06-10 16.36.54.png

ロググループ名を入力し「次へ」を選択します。
スクリーンショット 2017-06-10 16.36.54.png

IAMロールの設定を求められるので、適切なロール選択するか新規で作成します。
スクリーンショット 2017-06-10 16.37.27.png

「許可」を選択してCloudWatch Logsの設定は完了です。

Lambdaの設定

CloudWatchのログストリームを受けて、Slackに内容を通知するLambda functionを用意します。
今回は、Serverless Frameworkを使ってLambda functionのデプロイを行いました。
開発環境構築は→ serverlessでLambdaのローカル開発環境を整える

プロジェクトの作成

次のコマンドでプロジェクトを初期化します。

$ sls create --template aws-nodejs --path CloudTrail-Slack

必要パッケージもインストールしておきましょう。

$ npm install async
$ npm install request

functionの作成

プロジェクト作成の手順で、プロジェクトフォルダに作成されたhandler.jsを編集します。

handler.js
'use strict';
const zlib = require('zlib');
const async = require('async');
const request = require('request');

// set slack setting
const slack_endpoint = 'https://slack.com/api/chat.postMessage';
const slack_token = process.env.SLACK_TOKEN;
const slack_channel = process.env.SLACK_CHANNEL;
const slack_user = process.env.SLACK_USERNAME;

module.exports.notify = (event, context, callback) => {
    const payload = new Buffer(event.awslogs.data, 'base64');
    zlib.gunzip(payload, (err, res) => {
        if (err) {
            return callback(err);
        }
        const parsed = JSON.parse(res.toString('utf8'));
        console.log('Decoded payload:', JSON.stringify(parsed));

        async.map(parsed.logEvents, (data, callback) => {
          notifySlack(data, callback);
        }, (err, results) =>{
          if (!err) {
            console.log(results);
            callback(null, 'success');
          }
        });
    });
}

let notifySlack = (data, callback) => {
  let log = JSON.parse(data.message);

  let attachment = {
    "fallback": log.eventName + " by " + log.userIdentity.userName,
    "color": "#36a64f",
    "author_name": log.userIdentity.userName,
    "title": log.eventName,
    "fields": [
      {
        "title": "awsRegion",
        "value": log.awsRegion,
        "short": false
      },
      {
        "title": "eventSource",
        "value": log.eventSource,
        "short": false
      },
      {
        "title": "eventId",
        "value": log.eventID,
        "short": false
      }
    ],
    "thumb_url": "https://d0.awsstatic.com/security-center/KMS_Benefit_100x100_Compliance.png",
    "ts": new Date(log.eventTime).getTime() / 1000
  }

  let payload = {
    form: {
      token: slack_token,
      channel: slack_channel,
      username: slack_user,
      attachments: JSON.stringify([attachment])
    }
  }
  // post slack message
  request.post(slack_endpoint, payload, (error, response, body) => {
        if (error) {
          console.log(error)
        } else {
          callback(null, 'slack api call success');
        }
      }
  )
}

serverlessの設定

プロジェクト作成の手順で、プロジェクトフォルダに作成されたserverless.yamlを編集します。
Slackの投稿先に関する設定は環境変数から取得するようになっています。(AWSのコンソール上から設定変更可能)
また、必要に応じてリージョンの設定なども行います。

serverless.yml
service: cloudtail-lambda # NOTE: update this with your service name

provider:
  name: aws
  runtime: nodejs6.10

functions:
  notify:
    handler: handler.notify

#    Define function environment variables here
    environment:
      SLACK_TOKEN: <すらっくのAPIとーくん>
      SLACK_CHANNEL: cloudtrail-test
      SLACK_USERNAME: CloudTrail

デプロイ

次のコマンドでデプロイします。

$ sls deploy

AWS コンソール上からもデプロイしたLambda functionが確認できました。
スクリーンショット_2017-06-10_19_02_35.png

Slackの設定

API Tokenの取得

以下の記事を参考にさせてもらいました。
Slack APIのTokenの取得・場所

チャンネルの作成

「serverlessの設定」でSLACK_CHANNELに設定したチャンネルを事前に作成しておきます。
スクリーンショット 2017-06-10 17.24.39.png

SNSとの連携

ストリームの開始

AWSコンソールから、CloudWatchのメニューを開きます。
「ログ」のメニューを開き、ロググループ一覧から今回作成したロググループを選択し、「アクション」から「Lambdaサービスへのストリーミングの開始」を選択します。
スクリーンショット_2017-06-10_17_27_52.png

ストリーム先の選択

今回作成したLambda functionを選択した後、「次へ」を選択します。
スクリーンショット_2017-06-10_18_31_49.png

フィルタ設定

ログの形式には「AWS CloudTrail」を選択した後、サブスクリプションフィルターのパターンを設定します。
スクリーンショット_2017-06-10_18_00_32.png

{ ($.userIdentity.type = "IAMUser") && (($.eventSource = "ec2.amazonaws.com") || ($.eventSource = "s3.amazonaws.com") || ($.eventSource = "iam.amazonaws.com") || ($.eventSource = "rds.amazonaws.com") || ($.eventSource = "signin.amazonaws.com"))}

$.userIdentity.type = "IAMUser"では、通知するユーザーの範囲をIAMユーザーに絞っています。これにより、Rootユーザー、IAMロールからのAPIコールは対象外となります。

$.eventSource = "<サービス名>.amazonaws.com"では、通知するサービスの範囲を絞っています。上記フィルタ設定では

  • EC2
  • S3
  • IAM
  • RDS
  • ログイン関連

のみに絞っています。

確認

IAMユーザーでAWSコンソールにログイン。S3バケットの作成を行いました。
Slackには次のような通知が確認できました。
スクリーンショット 2017-06-10 18.59.21.png

以上です。
簡単に実現できました:baby_chick: