Serverless Frameworkを利用してAmazon GuardDutyの検出結果をSlackに通知する仕組みを自動構築する

re:invent 2017にてAWSアカウントの脅威検出を行うAmazon GuardDutyが発表されました。CloudTrailやVPCのログなどから異常な行動を機械学習によって検出するサービスとのことです。
しかし、GuardDutyを利用したAWSアカウントの脅威検出結果はAWSのマネジメントコンソール画面を見にいかなければならず、リアルタイムの脅威検出が行えません。
そのため、Lambdaなどを利用し、ユーザの利用しているチャットなどの媒体などを通じて通知する仕組みを作らなければならないのですが、GuardDutyはRegionalサービスのため、Multi-RegionにてAWSのサービスを利用している場合は全リージョンで有効にしなければならなず、いちいち作るのはかなりめんどくさい状況です。

本記事では、この問題を解決するためにServerless Frameworkを利用してAmazon GuardDutyの検出結果をSlackに通知する仕組みを自動構築する方法をご紹介します。


# 構成

![from-guardduty-to-slack.png](https://qiita-image-store.s3.amazonaws.com/0/137264/d3b0d7c6-5719-ae0a-5daf-a998680c1ec9.png)
https://blog.manabusakai.com/2017/12/from-guardduty-to-slack/
こちらの記事を参考に組ませていただきました。詳細については記事をご覧ください。
利用するAWSのサービスは以下の３つです。
- [Amazon GuardDuty](https://aws.amazon.com/guardduty/)
- [Amazon Cloudwatch](https://aws.amazon.com/cloudwatch/)
- [AWS Lambda](https://aws.amazon.com/lambda/)

通知は以下のフローで行われます。
1. GuardDutyにて脅威が検出される
2. GuardDutyが脅威の検出結果をCloudWatchに通知
3. CloudWatch Eventを発火条件として、Lambdaが起動
4. LambdaからSlackのIncoming Webhookを叩き、ユーザに通知

Slack通知の例
![GuardDuty_Notification.png](https://qiita-image-store.s3.amazonaws.com/0/137264/07fca14e-d36d-256f-6976-b1c8779ae53d.png)

この通知を行う仕組みをServerless Frameworkを利用して自動構築します。

# Serverless Frameworkを利用した自動構築
## Serverless Frameworkとは
![download.png](https://qiita-image-store.s3.amazonaws.com/0/137264/1910434f-67d8-2f1e-2a54-01969bae7e8f.png)
Serverless Frameworkはymlファイルを記述するだけでLambdaを含むServerless Architectureを自動構築可能なFrameworkです。AWSではCloudFormationで構築可能なものも同一のymlファイルで構築可能になっています。
本記事では、インストールの方法や利用方法は記述しません。
## 開発したもの
https://github.com/yfujit/guardduty-finder
こちらのリポジトリに今回開発したものは置いてあります。
主に開発したものは、serverless.ymlのみです。
slack_notificater.pyについては、[こちらの記事](https://blog.manabusakai.com/2017/12/from-guardduty-to-slack/)をご参照ください。

```
.
├── README.md
├── config
│   ├── webhookurl.yml
│   └── webhookurl.yml.sample
├── package-lock.json
├── serverless.yml
└── slack_notificater.py

```
#### serverless.yml
複数のRegionに同じものを自動で構築したいというモチベーションで自動構築を行うので、DeployするRegionは引数として与えられるようにしなければいけません。
そのため、provider.stage、provider.regionにてopt:region,opt:stageを設定し、コマンドラインで引数として渡せるようにしました。
今回はDefaultのRegionとして、新しいサービスが出て試すことの多いus-east-1、Stageをdevとして設定していますので、何も設定しない場合はこれらのパラメータが渡されます。（custom.defaultStage, custom.defaultRegionを参照）

slack_notificater.pyでは環境変数のWEBHOOK_URLの値を利用して通知先を決定するので、外部ファイルとしてconfig/webhookurl.ymlから環境変数を設定するようにしています。

Lambdaの発火条件ですが、cloudwatchEventのGuardDuty Findingにて設定してあります。GuardDutyにてなにか通知がきた場合に全て発火するような条件となっています。

GuardDutyの有効化は、resources配下にて行われます。ここは[CloudFormationのGuardDuty Detector](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-guardduty-detector.html)を参考にすれば実装が可能です。


```serverless.yml
service: guardduty-finder

provider:
  name: aws
  runtime: python3.6
  stage: ${opt:stage, self:custom.defaultStage}
  region: ${opt:region, self:custom.defaultRegion}

custom:
  defaultStage: dev
  defaultRegion: us-east-1
  otherfile:
    environment: ${file(./config/webhookurl.yml)}

functions:
  slack_notificater:
    handler: slack_notificater.lambda_handler
    events:
      - cloudwatchEvent:
          event:
            source:
              - "aws.guardduty"
            detail-type:
              - "GuardDuty Finding"
    environment:
      WEBHOOK_URL: ${self:custom.otherfile.environment.WEBHOOK_URL}

resources:
  Resources:
    GuardDutyDetector:
      Type: "AWS::GuardDuty::Detector"
      Properties:
        Enable: true
```

```config/webhookurl.yml
WEBHOOK_URL: SET_YOUR_SLACK_INCOMING_WEBHOOK_URL
```

### デプロイする方法
Serverless Framework側で引数として渡せるようにしてあるので、以下のようなコマンドでデプロイが可能です。こちらの例では、東日本(ap-northeast-1)に対してqa環境としてデプロイするコマンドです

```bash
$ serverless deploy --stage qa --region ap-northeast-1
```

# まとめ
本記事ではServerless Frameworkを利用してAmazon GuardDutyの検出結果をSlackに通知する仕組みを自動構築する方法をお伝えしました。TerraformやCloudFormationを利用する方法もありますが、Lambdaがからむデプロイを行う際にはServerless Frameworkを利用する方がロジックも同時にデプロイできるので楽ですね。
管理するアカウントが多くなればなるほど管理を自動化する必要がでてくると思うので、是非利用してみてください。