AWSリソースのセキュリティ監視環境をTerraformで構築する。

# 概要
AWSリソースのセキュリティ監視、解析環境をterraformで構築する方法をまとめています。

最終的な構成は以下の図のようになります。  
![kosezu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/16e0c173-0e5d-de55-55aa-77f4eb7d7ca6.png)


## Security Hubの設定
SecurityHubはAWSのセキュリティイベントを集約し、セキュリティの状態を監視するサービスです。例えばAWS ConfigやGuardDutyなどのセキュリティサービスの結果を集約し、それらの結果を一元的に確認することができます。  

また、「セキュリティ基準」と呼ばれるセキュリティのベストプラクティスに基づいて、リソースの設定をチェックすることができます。このチェックはAWS Configと連携して行われます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/9892ae71-450a-23a2-4655-f1267a45ce5a.png" width="450">

以下のように記述することで、Security Hubを有効化することができます。デフォルトで有効化されているセキュリティ基準を無効化している点に注意してください。
セキュリティ基準の有効化は、AWS Configの設定の後に行います。
```terraform:SecurityHubを有効化する
resource "aws_securityhub_account" "default" {
  #デフォルトで有効化されているセキュリティ基準を無効化
  enable_default_standards = false
}
```

## AWS Configの設定
AWS ConfigはAWSリソースの設定変更を監視するサービスです。Config Rulesと呼ばれるリソースの設定のルールを定義することで、そのルールから逸脱したリソースがあれば、それを検知することができます。

AWS Configはリソースの設定履歴をS3に保存することができます。この情報は、リソースの設定変更の履歴を確認する際に利用します。 
S3のアクセスログを保管するために、別途S3バケットを作成していますが、これは設定履歴を保管しているバケットが誤ってpublicになった時にその影響範囲を調査するために利用されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/0859c172-5103-25b7-76fe-108935dbf2d4.png" width="450">

### 保管用のS3バケットを作成する
AWS Configの設定履歴を保存するためのS3バケットを作成します。
```terraform:保管用のS3バケットを作成する
resource "aws_s3_bucket" "config_log_bucket" {
  bucket = "${var.name_prefix}-config-log-bucket"

  tags = {
    Name = "${var.tag_name}-config-log-bucket"
    group = "${var.tag_group}"
  }
}

resource "aws_s3_bucket_public_access_block" "config_log_bucket_public_access_block" {
  bucket = aws_s3_bucket.config_log_bucket.id

  block_public_acls       = true
  ignore_public_acls      = true
  block_public_policy     = true
  restrict_public_buckets = true
}

data "aws_iam_policy_document" "config_log_bucket_policy_document" {
  statement {
    actions = [
      "s3:PutObject"
    ]

    resources = [
      "${aws_s3_bucket.config_log_bucket.arn}/*",
    ]

    principals {
      type        = "Service"
      identifiers = ["config.amazonaws.com"]
    }
  }
  statement {
    actions = [
      "s3:GetBucketAcl",
      "s3:ListBucket"
    ]

    resources = [
      aws_s3_bucket.config_log_bucket.arn
    ]

    principals {
      type        = "Service"
      identifiers = ["config.amazonaws.com"]
    }
  }
}

resource "aws_s3_bucket_policy" "config_log_bucket_policy" {
  bucket = aws_s3_bucket.config_log_bucket.id
  policy = data.aws_iam_policy_document.config_log_bucket_policy_document.json
}

resource "aws_s3_bucket_lifecycle_configuration" "config_log_bucket_lifecycle_configuration" {
  bucket = aws_s3_bucket.config_log_bucket.id

  rule {
    id     = "transfer to glacier"
    status = "Enabled"

    transition {
      days          = 30
      storage_class = "GLACIER"
    }
  }

}

# アクセスログの送信先を定義
resource "aws_s3_bucket_logging" "config_log_bucket_logging" {
  bucket        = aws_s3_bucket.config_log_bucket.id
  target_bucket = aws_s3_bucket.config_log_bucket_bclg.id
  target_prefix = "config-log-bclg"
}
```

### S3バケットのアクセスログを保管するためのバケットを作成する
```terraform:S3バケットのアクセスログを保管するためのバケットを作成する
resource "aws_s3_bucket" "config_log_bucket_bclg" {
  bucket = "${var.name_prefix}-config-log-bucket-bclg"

  tags = {
    Name = "${var.tag_name}-config-log-bucket-bclg"
    group = "${var.tag_group}"
  }
}

resource "aws_s3_bucket_public_access_block" "config_log_bucket_bclg_public_access_block" {
  bucket = aws_s3_bucket.config_log_bucket_bclg.id

  block_public_acls       = true
  ignore_public_acls      = true
  block_public_policy     = true
  restrict_public_buckets = true
}

data "aws_iam_policy_document" "config_log_bucket_bclg_policy_document" {
  statement {
    actions = [
      "s3:PutObject",
    ]

    resources = [
      "${aws_s3_bucket.config_log_bucket_bclg.arn}/*",
    ]

    principals {
      type        = "Service"
      identifiers = ["logging.s3.amazonaws.com"]
    }
  }
  statement {
    actions = [
      "s3:GetBucketAcl"
    ]

    resources = [
      aws_s3_bucket.config_log_bucket_bclg.arn
    ]

    principals {
      type        = "Service"
      identifiers = ["logging.s3.amazonaws.com"]
    }
  }
}

resource "aws_s3_bucket_policy" "config_log_bucket_bclg_policy" {
  bucket = aws_s3_bucket.config_log_bucket_bclg.id
  policy = data.aws_iam_policy_document.config_log_bucket_bclg_policy_document.json
}

resource "aws_s3_bucket_lifecycle_configuration" "config_log_bucket_bclg_lifecycle_configuration" {
  bucket = aws_s3_bucket.config_log_bucket_bclg.id

  rule {
    id     = "transfer to glacier"
    status = "Enabled"

    transition {
      days          = 1
      storage_class = "GLACIER"
    }
  }

}
```

### AWS ConfigのIAMロールを作成する
AWS ConfigはAWSリソースを調査するためのサービスであるため、他リソースへ横断的にアクセスする必要があります。そのため、AWS Configには対応するポリシーを持ったIAMロールを作成する必要があります。  
IAMポリシーにはConfig用に用意されたマネージドポリシーが存在するので、それを利用しています。
```terraform:AWS ConfigのIAMロールを作成する
#ConfigのIAMロールを作成
resource "aws_iam_role" "config-role" {
  name = "${var.name_prefix}-config-role"

  assume_role_policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "config.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
})
}

#Configのマネージドポリシーをアタッチ
resource "aws_iam_role_policy_attachment" "config-role-policy" {
  role       = aws_iam_role.config-role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWS_ConfigRole"
}
```

### AWS Configの設定を行う
AWS Configの設定を行います。以下のように記述することで、AWS Configを有効化することができます。
```terraform:AWS Configの設定を行う
resource "aws_config_configuration_recorder_status" "default" {
  name       = "default"
  is_enabled = true
  depends_on = [aws_config_delivery_channel.default]
}

#調査対象のリソースを設定、ここでは全てのリソースを調査対象にしています
resource "aws_config_configuration_recorder" "default" {
  #上記で作成したIAMロールを指定
  role_arn = "${var.config_role_arn}"

  #全てのリソースを記録する
  recording_group {
    all_supported                 = true
    include_global_resource_types = true
    resource_types                = []
  }
}

#設定履歴を保存するS3バケットを指定
resource "aws_config_delivery_channel" "default" {
  name           = "default"
  #保存先のS3バケットを指定
  s3_bucket_name = "${var.config_log_bucket_name}"
}
```

上記の設定でAWS Configが有効化されますが、Config Rulesが設定されていない状態であるため、リソースのチェックは実行されません。今回の記事ではConfigサービスを主体としたルールを設定せず、Security Hubと連携して生成されるConfig Rulesを利用するに留めます。

運用の方針によって、個別のマネージドルールやカスタムルールを設定してください。

### SecurityHubと連携してConfig Rulesを設定する
SecurityHubのセキュリティ基準を有効化することで、その基準のチェックに必要なConfigRulesが自動的に作成されます。  
今回は「AWS 基礎セキュリティのベストプラクティス」を有効化しています。
```terraform:「AWS 基礎セキュリティのベストプラクティス」を有効化する
resource "aws_securityhub_standards_subscription" "aws_foundational_security_best_practices" {
  #上記で有効化したSecurityHubを指定
  depends_on = [aws_securityhub_account.default]

  #セキュリティ基準のARNを指定
  standards_arn = "arn:aws:securityhub:ap-northeast-1::standards/aws-foundational-security-best-practices/v/1.0.0"
}
```
「AWS 基礎セキュリティのベストプラクティス」を有効化すると以下のようにConfig Rulesが追加され、リソースのチェックが実行されます。
![config-rule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/06636817-e3c1-f1db-14b6-f25e31018920.png)


### Config Rulesのチェックの結果をSecurity Hubで確認する
Config Rulesのチェックの結果はSecurity Hubで確認することができます。  
この結果から、リソースの設定を修正するか、または無視してそのチェック自体を無効化することを判断していくことになります。
![config-rule-kekka.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/952cbc89-a00d-3b0b-87b1-f4d0a04ae324.png)


## Macieの設定
MacieはS3バケットに保存されているデータのセキュリティを監視するサービスです。有効化すれば、S3バケットに保存されているデータの中から、個人情報や機密情報などの特定のデータを検出することができます。  
また検出結果はSecurity Hubに連携されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/0ec78e83-fc5a-66a2-88af-52d6864f249e.png" width="450">


```terraform:Macieを有効化する
resource "aws_macie2_account" "default" {
  status                       = "ENABLED"
}
```
有効化すると以下のようにS3バケットに対するチェックが実行されます。
![macie.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/a9232bc7-924f-d382-dea9-74f07820dbc6.png)



## IAM Access Analyzerの設定
IAM Access Analyzerは、IAMポリシーによるリソースへのアクセス権限を分析するサービスです。有効化すれば、リソースに対するアクセス権限の分析が実行されます。
また検出結果はSecurity Hubに連携されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/991a9f0b-eb82-d0a2-538a-c238b8d0f044.png" width="450">


```terraform:IAM Access Analyzerを有効化する
resource "aws_accessanalyzer_analyzer" "default" {
  analyzer_name = "${var.name_prefix}-iam-access-analyzer"
}
```


## Inspectorの設定
InspectorはEC2やECRのイメージに対してアプリの観点から脆弱性のスキャンを実行するサービスです。有効化すれば、リソースに対する脆弱性のスキャンが実行されます。
また検出結果はSecurity Hubに連携されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/544bd6f8-6efd-e41d-ba77-65380d665a73.png" width="450">



今回はECRのイメージに対してスキャンを実行するため、以下のように記述しています。
```terraform:Inspectorを有効化する
resource "aws_inspector2_enabler" "ecr" {
  #アカウントIDとリソースタイプを指定
  account_ids    = ["${var.account_id}"]
  resource_types = ["ECR"]
}
```
有効化すると以下のようにECRのイメージに対するスキャンが実行されます。
![inspector.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/4f8dfcee-1117-3c17-ee11-2c5be38cbf57.png)


## GuardDutyの設定
GuardDutyはAWSリソースのログを監視し、不正なアクセスを検知するサービスです。有効化すれば、リソースに対する不正なアクセスの検知が実行されます。

GuardDutyはCloudTrail、VPC Flow Logs、DNSログなどのログの情報をもとにセキュリティイベントを検知します。そのため、これらのログを有効化しておく必要があります。また、検知された際の調査のためにログを保存するS3バケットを作成しておく必要があります。
![guard-duty-zu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/951ebe91-f0b5-a615-c86c-6d0a7a70b3eb.png)


### CloudTrailの設定
CloudTrailはAWSリソースの操作ログを記録するサービスです。
CloudTrailはアカウント設定時からログの出力が有効化されていますが、そのログの保存期間は90日間となっています。そのためログの長期保存を考慮してS3バケットにログが転送されるように設定します。
バケットポリシーにはCloudTrailからのログの取得と保存を許可するポリシーを設定している点に注意してください。
```terraform:CloudTrailのログを保存するS3バケットを作成する
resource "aws_s3_bucket" "log_bucket_cloud_trail" {
  bucket = "${var.name_prefix}-log-bucket-cloud-trail"

  tags = {
    Name = "${var.tag_name}-log-bucket-cloud-trail"
    group = "${var.tag_group}"
  }
}

resource "aws_s3_bucket_public_access_block" "log_bucket_cloud_trail_public_access_block" {
  bucket = aws_s3_bucket.log_bucket_cloud_trail.id

  #外部からの読み込みを許可しない
  block_public_acls       = true
  ignore_public_acls      = true
  block_public_policy     = true
  restrict_public_buckets = true
}

#CloudWatch LogsからS3へのログ出力を許可する
data "aws_iam_policy_document" "log_bucket_cloud_trail_policy_document" {
  statement {
    actions = [
      "s3:PutObject",
    ]

    resources = [
      "${aws_s3_bucket.log_bucket_cloud_trail.arn}/*"
    ]

    principals {
      type        = "Service"
      identifiers = ["cloudtrail.amazonaws.com"]
    }
  }
  statement {
    actions = [
      "s3:GetBucketAcl"
    ]

    resources = [
      aws_s3_bucket.log_bucket_cloud_trail.arn
    ]

    principals {
      type        = "Service"
      identifiers = ["cloudtrail.amazonaws.com"]
    }
  }
}

resource "aws_s3_bucket_policy" "log_bucket_cloud_trail_policy" {
  bucket = aws_s3_bucket.log_bucket_cloud_trail.id
  policy = data.aws_iam_policy_document.log_bucket_cloud_trail_policy_document.json
}
```
CloudTrailのログをS3へ転送するために以下のように設定します。
```terraform:CloudTrailのログをS3バケットに転送する
resource "aws_cloudtrail" "default" {
  name                          = "default"
  s3_bucket_name                = "${var.s3_bucket_log_cloud_trail_id}"
  s3_key_prefix                 = "${var.name_prefix}/cloudtrail/"
  include_global_service_events = false
}
```

### VPC Flow Logsの設定
VPC Flow LogsはVPC内のリソースの通信ログを記録するサービスです。

VPC Flow Logsのログを保存するためのS3バケットを作成します。
```terraform:VPC Flow Logsのログを保存するS3バケットを作成する
resource "aws_s3_bucket" "log_bucket_vpc_flow" {
  bucket = "${var.name_prefix}-log-bucket-vpc-flow"

  tags = {
    Name = "${var.tag_name}-log-bucket-vpc-flow"
    group = "${var.tag_group}"
  }
}

resource "aws_s3_bucket_public_access_block" "log_bucket_vpc_flow_public_access_block" {
  bucket = aws_s3_bucket.log_bucket_vpc_flow.id

  block_public_acls       = true
  ignore_public_acls      = true
  block_public_policy     = true
  restrict_public_buckets = true
}

data "aws_iam_policy_document" "log_bucket_vpc_flow_policy_document" {
  statement {
    actions = [
      "s3:PutObject",
    ]

    resources = [
      "${aws_s3_bucket.log_bucket_vpc_flow.arn}/*"
    ]

    principals {
      type        = "Service"
      identifiers = ["delivery.logs.amazonaws.com"]
    }
  }
  statement {
    actions = [
      "s3:GetBucketAcl"
    ]

    resources = [
      aws_s3_bucket.log_bucket_vpc_flow.arn
    ]

    principals {
      type        = "Service"
      identifiers = ["delivery.logs.amazonaws.com"]
    }
  }
}

resource "aws_s3_bucket_policy" "log_bucket_vpc_flow_policy" {
  bucket = aws_s3_bucket.log_bucket_vpc_flow.id
  policy = data.aws_iam_policy_document.log_bucket_vpc_flow_policy_document.json
}
```
VPC Flow Logsのログを有効化するとともに、S3へ転送するように設定します。
```terraform:VPC Flow Logsの出力を有効化
resource "aws_flow_log" "default" {
  log_destination      = "${var.s3_bucket_log_vpc_flow_arn}"
  log_destination_type = "s3"
  traffic_type         = "ALL"
  vpc_id               = "${var.vpc_id}"
}
```

### DNSクエリログの設定
クエリログに関してはterraformでの設定ができないため、AWSコンソールから設定します。
以下の記事などを参照に、設定を行ってください。

https://blog.serverworks.co.jp/route53/resolver-query-logs

### GuardDutyの有効化
GuardDutyは初期設定では無効化されているため、有効化します。
```terraform:GuardDutyを有効化する
resource "aws_guardduty_detector" "default" {
  enable = true
}
```
有効化すると以下のようにセキュリティイベントの検知が実行されます。

![guardduty.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/feefef55-a058-da80-8b9e-7081e18d0e0d.png)



## AWS Healthについて
AWS HealthはAWSのサービスの障害情報を提供するサービスです。
セキュリティに関する通知はSecurity Hubに連携されるようにデフォルトで設定されています。
![health-zu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/35d823bf-c0c5-e1bf-7528-5c7d149fb81a.png)


連携の有無については以下のように確認することができます。
![health.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/e405f16c-bed8-efeb-ba2a-ffdce34bdb4e.png)



## Detectiveの設定
DetectiveはGuiardDutyで検知されたセキュリティイベントを解析するためのサービスです。
![detective-zu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/20335a2d-192c-9b35-7c4a-11f8049a66dc.png)

```terraform:Detectiveを有効化する
resource "aws_detective_graph" "default" {}
```

## Athenaの設定
CloudTrailのログやConfig構成情報を解析する際は、Athenaを利用します。Athenaの利用方法についてはここでは説明しません。
![athena-zu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/ce34e805-2920-ee1f-c1fc-942707247ce4.png)


## セキュリティイベントの通知の設定
セキュリティイベントはSecurityHubに集約されるように設定されていますが、即座にセキュリティイベントに対応できるようにするために、運用者へメールで新規のセキュリティイベントが通知されるように設定します。

以下の図のように、EventBridgeルールを利用して、SecurityHubに新規のセキュリティイベントが届いたことを検知して、それを起点としてSNSトピックからメールを送信するように設定します。
![sns-zu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987556/fbbe399f-cedd-8902-7080-ed5cef9dda03.png)

まずはSNSトピックを作成します。送信先には運用者のメールアドレスを指定します。また、EventBridgeからメッセージを受け取るためにSNSトピックポリシーを設定します。
```terraform:SNSトピックを作成する
#セキュリティイベントを通知するためのSNSトピックを作成
resource "aws_sns_topic" "security_alart_topic" {
  name = "${var.name_prefix}-security-alart-topic"
}
#送信先に運用者のメールアドレスを指定
resource "aws_sns_topic_subscription" "security_alart_topic_subscription" {
  topic_arn = aws_sns_topic.security_alart_topic.arn
  protocol  = "email"
  endpoint  = "${var.email}"
}

# EventBridgeからの操作を受け付けるために、SNSトピックポリシーを設定
resource "aws_sns_topic_policy" "default" {
  arn    = aws_sns_topic.security_alart_topic.arn
  policy = data.aws_iam_policy_document.sns_topic_policy.json
}

data "aws_iam_policy_document" "sns_topic_policy" {
  statement {
    effect  = "Allow"
    actions = ["SNS:Publish"]

    principals {
      type        = "Service"
      identifiers = ["events.amazonaws.com"]
    }

    resources = [aws_sns_topic.security_alart_topic.arn]
  }
}
```

次にEventBridgeルールを作成します。以下のように記述することで、SecurityHubに新規のセキュリティイベントが届いたことを検知して、それを起点としてSNSトピックにメッセージを送信するように設定します。
```terraform:EventBridgeルールを作成する
resource "aws_cloudwatch_event_rule" "security_hub" {
  name        = "security_hub_event"
  description = "security_hub_event"

  event_pattern = jsonencode({
    "source": [
        "aws.securityhub"
    ],
    #新規のセキュリティイベントが届いたことを検知
    "detail-type": [
        "Security Hub Findings - Imported"
    ]
    })
}

resource "aws_cloudwatch_event_target" "security_hub_to_sns" {
  rule      = aws_cloudwatch_event_rule.security_hub.name
  #SNSトピックを指定
  target_id = "SendToSNS"
  arn       = "${var.sns_arn}"
}
```

以上でAWSリソースのセキュリティ監視環境の構築は完了です。

## 参考文献
本記事は以下の書籍、記事を参考にさせていただきました。
- [AWSではじめるクラウドセキュリティ　クラウドで学ぶセキュリティ設計/実装](https://amzn.asia/d/cXcNIJ0)
- [AWS運用入門　押さえておきたいAWSの基本と運用ノウハウ](https://amzn.asia/d/csXAXv4)
- [AWS継続的セキュリティ実践ガイド ログの収集／分析による監視体制の構築](https://amzn.asia/d/6IWd4dE)
- [Amazon ECRとInspectorのイメージスキャン機能の違い](https://qiita.com/hayao_k/items/889f2a6cdc4e377fb634)
- [AWS Configの初期設定とマネージドルールを Terraform で設定して、非準拠項目を確認して対処してみる](https://qiita.com/tonishy/items/5f1f03057c1b2e56f13a)
- [Amazon VPC フローログを使ってみる](https://qiita.com/mksamba/items/d8fe465dd31183822af9)