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


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

次の記事を参考にさせてもらいました。
[セキュリティーグループが変更されたらSlackに通知する -ハンズラボエンジニアブログ](https://www.hands-lab.com/tech/entry/2102.html)

## CloudTrailの設定

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

![start_imidiatory.png](https://qiita-image-store.s3.amazonaws.com/0/79414/eb0515da-5890-db7b-a7d9-2b5aaa26e0e5.png "start_imidiatory.png")


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

![enable_cloudtail.png](https://qiita-image-store.s3.amazonaws.com/0/79414/97b8037e-cdeb-d5f5-e133-f09d8c3baf24.png "enable_cloudtail.png")


### 保存先の設定

CloudTrailで取得した追跡情報はログファイルとしてS3に保存されます。次のように保存先バケットを設定を行います。
ストレージの場所で、新しいS3バケットを作成しますかを「はい」にし「S3バケット」の名称を設定します。
※今回はプレフィックスの設定は省略し、その他の項目はデフォルトのまま進めます
![スクリーンショット 2017-06-10 16.17.05.png](https://qiita-image-store.s3.amazonaws.com/0/79414/2b1ae50b-4f11-8334-8f3b-d9a0d95cee0f.png "スクリーンショット 2017-06-10 16.17.05.png")

### 作成

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

## CloudWatch Logsの設定

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

### 設定
CloudTrailのメニューを開き、先ほど作成した追跡情報を選択します。
![スクリーンショット_2017-06-10_16_19_26.png](https://qiita-image-store.s3.amazonaws.com/0/79414/697dcde8-0f3d-8a58-4b60-321ef51e02dc.png "スクリーンショット_2017-06-10_16_19_26.png")

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

![スクリーンショット 2017-06-10 16.36.54.png](https://qiita-image-store.s3.amazonaws.com/0/79414/4813be00-037e-79bd-2b48-319c14373f5d.png "スクリーンショット 2017-06-10 16.36.54.png")

ロググループ名を入力し「次へ」を選択します。
![スクリーンショット 2017-06-10 16.36.54.png](https://qiita-image-store.s3.amazonaws.com/0/79414/0b72c10c-b74a-db56-5273-a6c60f96178f.png "スクリーンショット 2017-06-10 16.36.54.png")

IAMロールの設定を求められるので、適切なロール選択するか新規で作成します。
![スクリーンショット 2017-06-10 16.37.27.png](https://qiita-image-store.s3.amazonaws.com/0/79414/7e1e002f-5333-5fd4-aa4c-ea6b567bcce8.png "スクリーンショット 2017-06-10 16.37.27.png")

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

## Lambdaの設定

CloudWatchのログストリームを受けて、Slackに内容を通知するLambda functionを用意します。
今回は、Serverless Frameworkを使ってLambda functionのデプロイを行いました。
開発環境構築は→ [serverlessでLambdaのローカル開発環境を整える](http://qiita.com/daikiojm/items/891bac3a61bdea0a4340)

### プロジェクトの作成

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

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

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

```
$ npm install async
$ npm install request
```

### functionの作成

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

```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のコンソール上から設定変更可能)
また、必要に応じてリージョンの設定なども行います。

```yml: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](https://qiita-image-store.s3.amazonaws.com/0/79414/c3ee273b-0a56-95ab-f7fe-0caddc2438e0.png "スクリーンショット_2017-06-10_19_02_35.png")



## Slackの設定

### API Tokenの取得

以下の記事を参考にさせてもらいました。
[Slack APIのTokenの取得・場所](http://qiita.com/ykhirao/items/0d6b9f4a0cc626884dbb)

### チャンネルの作成

「serverlessの設定」で`SLACK_CHANNEL`に設定したチャンネルを事前に作成しておきます。
![スクリーンショット 2017-06-10 17.24.39.png](https://qiita-image-store.s3.amazonaws.com/0/79414/46c1bb0d-09eb-8acd-e5b2-85077f978729.png "スクリーンショット 2017-06-10 17.24.39.png")

## SNSとの連携

### ストリームの開始
AWSコンソールから、CloudWatchのメニューを開きます。
「ログ」のメニューを開き、ロググループ一覧から今回作成したロググループを選択し、「アクション」から「Lambdaサービスへのストリーミングの開始」を選択します。
![スクリーンショット_2017-06-10_17_27_52.png](https://qiita-image-store.s3.amazonaws.com/0/79414/899713f0-7daa-eb36-e8ce-f95340ebf762.png "スクリーンショット_2017-06-10_17_27_52.png")

### ストリーム先の選択

今回作成したLambda functionを選択した後、「次へ」を選択します。
![スクリーンショット_2017-06-10_18_31_49.png](https://qiita-image-store.s3.amazonaws.com/0/79414/61a461b3-d178-e167-02fa-9c11b420876e.png "スクリーンショット_2017-06-10_18_31_49.png")


### フィルタ設定

ログの形式には「AWS CloudTrail」を選択した後、サブスクリプションフィルターのパターンを設定します。
![スクリーンショット_2017-06-10_18_00_32.png](https://qiita-image-store.s3.amazonaws.com/0/79414/12a7e897-3682-94ba-993c-786ff19cbcf5.png "スクリーンショット_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](https://qiita-image-store.s3.amazonaws.com/0/79414/85beb8a3-216c-2024-987d-bf01665a7b16.png "スクリーンショット 2017-06-10 18.59.21.png")



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