Terraformを使ってAmazon GuardDutyを全リージョンで有効化する

こんにちは。
STORES.jp のSREチーム所属の清水です。この記事は [STORES.jp Advent Calendar](https://adventar.org/calendars/4549) 2019の7日目の記事です。Amazon GuardDutyをTerraformで扱う方法を紹介します。

## GuardDutyとは
STORES.jp では、[GuardDuty](https://aws.amazon.com/jp/guardduty/)というAWSサービスを利用しています。GuardDutyは、悪意ある操作や不正な動作を継続的にモニタリングする脅威検出サービスです。様々な不審なアクティビティを検知して通知をしてくれるので運用に役立っており、全てのAWSリージョンでGuardDutyを有効にしています。

GuardDutyは、[AWS公式](https://docs.aws.amazon.com/ja_jp/guardduty/latest/ug/guardduty_settingup.html)でも全てのリージョンでの有効化が強く推奨されているサービスです。
> GuardDuty は、サポートされているすべての AWS リージョンで有効にすることが強く推奨されています。

ハッカーは侵入にした際になるべく気づかれないように、アカウント所有者が普段は使用していないリージョンでマイニングなど不正な活動をします。そのため、全リージョンでGuardDutyを有効化することが重要です。

CloudFormationで全リージョンのGuardDutyを有効化する方法は様々なサイトで紹介されているのですが、Terraformで有効化している例はあまり見かけません。

今回は、Terraformを使って全リージョンでGuardDutyを有効化する方法を紹介します。

## Terraformでの設定方法

### ディレクトリ構成
なるべく簡潔に記述するために、stateファイルの保存場所やTerraformのバージョン指定などは省略しています。

```
├── main.tf
├── modules
│   └── aws
│       └── guardduty
│           └── main.tf
└── providers.tf
```

### ファイルの説明

 **main.tf** 

- `main.tf`では、各リージョンごとのGuardDutyの設定をmoduleで定義しています。それぞれのmoduleで異なるproviderを指定して、tokyo, seoul, mumbaiリージョンにGuardDutyを設定しています。

```
module "tokyo" {
  source = "./modules/aws/guardduty"
  providers = {
    aws = aws.Tokyo
  }
}

module "seoul" {
  source = "./modules/aws/guardduty"
  providers = {
    aws = aws.Seoul
  }
}

module "mumbai" {
  source = "./modules/aws/guardduty"
  providers = {
    aws = aws.Mumbai
  }
}
```

 **providers.tf**

- `providers.tf`では、どのパブリッククラウドの、どのAPIとやりとりをするのか記述しています。provider毎にaliasを設定することで、module内のproviders設定で、`aws = aws.Tokyo`と指定すれば、AWSのTokyoリージョンのAPIとやりとりをするようになります。

```
provider "aws" {
  region = "ap-northeast-1"
}

provider "aws" {
  region = "ap-northeast-1"
  alias  = "Tokyo"
}

provider "aws" {
  region = "ap-northeast-2"
  alias  = "Seoul"
}

provider "aws" {
  region = "ap-south-1"
  alias  = "Mumbai"
}
```

**modules/aws/guardduty/main.tf**

- `modules/aws/guardduty/main.tf`には、各リージョンに共通するGuardDutyのリソース設定を記述します。この例では、GuardDutyを有効化して、15分間隔で結果を通知する設定をしています。

```
resource "aws_guardduty_detector" "guardduty" {
  enable                       = true
  finding_publishing_frequency = "FIFTEEN_MINUTES"
}
```

***

上記、3個のファイルをディレクトリ構成通りに配置して、ルートディレクトリで`terraform apply`などを実行すれば、各リージョンにGuardDutyがまとめて設定されます。実際の運用では、自分のAWSアカウントで有効にしているリージョンの分だけ`main.tf`, `providers.tf`にリージョン設定を追加することになります。terraformの基本的な概念・サブコマンドなどの使い方は他ドキュメントを参考にしてください。

## 通知を受け取る
ここからは、Terraformとは直接関係ありませんが、GuardDutyの検出した内容をどうやって通知として受け取っているのか、通知側のシステム構成を説明します。STORES.jpのSREチームでは、AWSの複数リソースとAWS Lambdaなどを組み合わせた処理システムを構築する際に、[Serverless Framework](https://serverless.com/)をよく利用しています。今回は詳しく解説しませんが、Serverless Frameworkはサーバーレスアプリケーションのためのフレームワークです。

まず、CloudWatch EventsでGuardDutyの結果を[モニタリング](https://docs.aws.amazon.com/ja_jp/guardduty/latest/ug/guardduty_findings_cloudwatch.html)します。

CloudWatch Eventsは、GuardDutyの結果の変更を検知すると、そのイベントをLambda関数に渡します。Lambda関数ではイベント情報を閲覧のためのデータ構造に加工して、SlackにIncoming Webhookで通知する処理をしています。

以上の処理の流れを図にまとめると、以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546691/826cbf97-ffb7-74a0-bdb7-1429b92fcfa2.png)




Slackでは以下のような形式の通知が届いています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546691/9b41b22f-5412-c235-5d1c-7ef47ca1ec34.png)


## おわりに
STORES.jpのSREチームでは、Infrastructure as Codeだけではなく、監視・セキュリティ・パフォーマンス改善など日々様々な課題に取り組んでいます。 もし、少しでもSTORES.jp に興味を持っていただけたら、隔週木曜日の夜に会社紹介のイベント（ https://hey.jp/events/hello-hey/ ）を実施していますので、ぜひ一度遊びにきてください。明日、8日目の[Advent Calendar](https://adventar.org/calendars/4549)は、[おいちゃん](https://blog.inouetakuya.info/entry/2019/10/17/181307)の登場です！！