[terraform] aws config

# 概要

AWS Configを利用するための一連のリソースを作成します。

- AWS Configの有効化
    - 設定はマネコンで有効化したとき同様のシンプルなものにします
- Config Rule
    - ここではサンプルとしてrestrected_sshルールと、修復アクションを設定します
- Slack通知用Event Bridge Rule

# S3バケット作成

- バケット名、バケットポリシーは、マネコンでconfigを作成したときに自動生成されるものを踏襲しています(変えてもOKです)
- 以下は任意です
    - SSE暗号化
    - パブリックアクセスブロックON

```terraform:main.tf
resource "aws_s3_bucket" "config" {
  bucket = "config-bucket-123456789012"
  server_side_encryption_configuration {
    rule {
      bucket_key_enabled = false

      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}

resource "aws_s3_bucket_public_access_block" "config" {
  bucket                  = aws_s3_bucket.config.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_policy" "config" {
  bucket = aws_s3_bucket.config.bucket
  policy = jsonencode(
    {
      Version = "2012-10-17",
      Statement = [
        {
          Sid = "AWSConfigBucketPermissionsCheck",
          Effect = "Allow",
          Principal = {
            "Service" = "config.amazonaws.com"
          },
          Action = "s3:GetBucketAcl",
          Resource = aws_s3_bucket.config.arn
        },
        {
          Sid = "AWSConfigBucketExistenceCheck",
          Effect = "Allow",
          Principal = {
            "Service" = "config.amazonaws.com"
          },
          Action = "s3:ListBucket",
          Resource = aws_s3_bucket.config.arn
        },
        {
          Sid = "AWSConfigBucketDelivery",
          Effect = "Allow",
          Principal = {
            "Service" = "config.amazonaws.com"
          },
          Action = "s3:PutObject",
          Resource =  "${aws_s3_bucket.config.arn}/AWSLogs/123456789012/Config/*",
           Condition = {
            StringEquals = {
              "s3:x-amz-acl" = "bucket-owner-full-control"
            }
          }
        }
      ]
    }
  )
}
```

# config作成

- マネコンでconfigを有効化するとサービスロールが自動生成されますが、terraformから有効化すると生成されないので明示的に作成します
- デリバリチャンネル名は、マネコンで作成したときに自動設定されるもの("default")を踏襲します

```
resource "aws_iam_service_linked_role" "config" {
  aws_service_name = "config.amazonaws.com"
}

resource "aws_config_delivery_channel" "main" {
  name           = "default"
  s3_bucket_name = aws_s3_bucket.config.bucket
}

resource "aws_config_configuration_recorder" "main" {
  role_arn = aws_iam_service_linked_role.config.arn

  recording_group {
    all_supported                 = true
    include_global_resource_types = true
    resource_types                = []
  }
}

resource "aws_config_configuration_recorder_status" "main" {
  name       = "default"
  is_enabled = true
  depends_on = [aws_config_delivery_channel.main]
}
```

# 修復アクション用IAMロール/ポリシー作成

- 詳細は省きます
- IAMロール
    - 信頼ポリシーで```ssm.amazonaws.com```サービスを許可します
- IAMポリシー
    - ```Resource = "*"```で```ec2:RevokeSecurityGroupIngress```を許可します

# configルール/修復アクション作成

- 修復アクション
    - 試行回数は最低限(1分後に1回)としています
    - 修復アクションのパラメータで、```GroupId: RESOURCE_ID```を指定することで、すべてのリソースが対象になります
    - AutomationAssumeRoleに作成したIAMロールのARNを指定します

```
resource "aws_config_config_rule" "restricted_ssh" {
  description = "Checks whether security groups that are in use disallow unrestricted incoming SSH traffic."
  input_parameters = jsonencode({})
  name             = "restricted-ssh"

  scope {
    compliance_resource_types = [
      "AWS::EC2::SecurityGroup",
    ]
  }

  source {
    owner             = "AWS"
    source_identifier = "INCOMING_SSH_DISABLED"
  }
}

resource "aws_config_remediation_configuration" "restricted_ssh" {
  automatic        = true
  config_rule_name = "restricted-ssh"
  maximum_automatic_attempts = 1
  retry_attempt_seconds      = 60
  target_id                  = "AWS-DisablePublicAccessForSecurityGroup"
  target_type                = "SSM_DOCUMENT"
  target_version             = "1"

  parameter {
    name         = "AutomationAssumeRole"
    static_value = aws_iam_role.auto_remediation.arn
  }
  parameter {
    name           = "GroupId"
    resource_value = "RESOURCE_ID"
  }
}
```

# Slack通知用Event Bridge Rule

- Chatbot - SNSトピック - Event Bridge Ruleを連携します
- Chatbot
    - Terraform未サポートのため手動作成します
    - Chatbot, SNSトピックの作成/連携は省略します
- Event Bridge Rule
    - Security Hubを有効化している場合は、Security Hubの通知のフィルタリングを検討します

```
resource "aws_cloudwatch_event_rule" "awsconfig" {
  event_pattern = jsonencode(
    {
      detail-type = [
        "Config Rules Compliance Change",
      ]
      source = [
        "aws.config",
      ]
    }
  )
  name = "config-notification-rule"
  is_enabled = true
}

resource "aws_cloudwatch_event_target" "config" {
  arn  = "arn:aws:sns:ap-northeast-1:123456789012:config-notification-topic"
  rule = aws_cloudwatch_event_rule.config.id
}
```