Help us understand the problem. What is going on with this article?

AWS Configの初期設定とマネージドルールを Terraform で設定して、非準拠項目を確認して対処してみる

1. はじめに

  • AWS Config の初期設定を Terraform (v0.12.9) を使って設定する。
  • ついでに、AWS Config マネージドルールを全く設定していなかったので、これも Terraform で設定して、現状の設定が AWS推奨のベストプラクティスに準拠しているかを確認してみる。
  • 非準拠対応をしてみる。

2. AWS Config 設定の目的

AWS Config の「記録をオン」に設定することで、指定したリソースの変更履歴やイベント履歴のデータを S3 へ格納するす(デフォルトの期間は7年)。このデータを利用して、例えば次のことができるようになる。

  • 「ダッシュボード」を使うと、履歴データを可視化してリソースの関係性を確認したり、設定変更やイベント発生を時系列で確認したりできる。
    ⇒ 簡単なトラブルシューティング、リソースの変更内容確認など。

  • 「高度なクエリ」を使うと、履歴データへクエリを発行して詳細な分析ができる。
    ⇒ 高度なトラブルシューティング、セキュリティ分析など。

  • 「Configルール」を使うと、リソースがAWS推奨のベストプラクティスに準拠しているかや、各企業独自のコンプライアンスに違反していないかを評価することができる。
    ⇒ セキュリティチェック、ベストプラクティス準拠チェック、コンプライアンス違反チェックなど。

参考
AWS Docs: AWS Configとは
AWS Docs: AWS Config の概念
AWS Docs: AWS Config の仕組み

3. AWS Config の初期設定を Terraform (v0.12.9) を使って設定する

3.1. Terraform構成

anyservice/
└── anyenv
    ├── awsconfig
    │   ├── backend.tf
    │   ├── main.tf
    │   ├── provider.tf -> ../../../provider.tf
    │   └── terraform_remote_state.tf -> ../../../terraform_remote_state.tf
    ├── iam
    │   ├── aws-config.tf
    │   ├── backend.tf
    │   ├── provider.tf -> ../../../provider.tf
    │   └── terraform_remote_state.tf -> ../../../terraform_remote_state.tf
    └── s3
        ├── awsconfig.tf
        ├── backend.tf
        └── provider.tf -> ../../../provider.tf

3.2. provider.tf, credentials

  • provider.tf
provider.tf
provider "aws" {
  version                 = "2.48.0"
  shared_credentials_file = "../../../credentials"
  profile                 = "terraform"
  region                  = "ap-northeast-1"
}
  • credentials
[terraform]
aws_access_key_id = チョメチョメ
aws_secret_access_key = チョメチョメ

3.3. 履歴データを格納する S3 バケットを作成

  • 作成概要

    • バケット名: awsconfig-AWSアカウントID
    • リージョン: ap-northeast-1
    • バージョニング: 有効
    • バケットポリシー設定
  • anyservice/anyenv/s3/backend.tf

backend.tf
terraform {
  required_version = ">= 0.12.9"
  backend "s3" {
    shared_credentials_file = "../../../credentials"
    profile                 = "terraform"
    region                  = "ap-northeast-1"
    bucket                  = "terraform-tfstate-チョメチョメ"
    key                     = "anyservice/anyenv/s3/terraform.tfstate"
  }
}
  • anyservice/anyenv/s3/awsconfig.tf
awsconfig.tf
data "aws_caller_identity" "current" {
}
resource "aws_s3_bucket" "awsconfig" {
  bucket        = "awsconfig-${data.aws_caller_identity.current.account_id}"
  acl           = "private"
  force_destroy = "false"
  region        = "ap-northeast-1"
  versioning {
    enabled = true
  }
}
data "aws_iam_policy_document" "s3bucket_policy-awsconfig" {
  version = "2012-10-17"
  statement {
    sid    = "AWSConfigBucketPermissionsCheck"
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["config.amazonaws.com"]
    }
    actions = [
      "s3:GetBucketAcl"
    ]
    resources = [
      "${aws_s3_bucket.awsconfig.arn}"
    ]
  }
  statement {
    sid    = "AWSConfigBucketDelivery"
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["config.amazonaws.com"]
    }
    actions = [
      "s3:PutObject"
    ]
    resources = [
      "${aws_s3_bucket.awsconfig.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/Config/*"
    ]
    condition {
      test     = "StringEquals"
      variable = "s3:x-amz-acl"
      values   = ["bucket-owner-full-control"]
    }
  }
}
resource "aws_s3_bucket_policy" "awsconfig" {
  bucket = "${aws_s3_bucket.awsconfig.bucket}"
  policy = "${data.aws_iam_policy_document.s3bucket_policy-awsconfig.json}"
}
output "awsconfig_arn" {
  value = aws_s3_bucket.awsconfig.arn
}
output "awsconfig_name" {
  value = aws_s3_bucket.awsconfig.id
}
  • terraform_remote_state.tf
terraform_remote_state.tf
data "terraform_remote_state" "S3" {
  backend = "s3"
  config = {
    shared_credentials_file = "../../../credentials"
    profile                 = "terraform"
    region                  = "ap-northeast-1"
    bucket                  = "terraform-tfstate-チョメチョメ"
    key                     = "anyservice/anyenv/s3/terraform.tfstate"
  }
}

3.4. IAMポリシー、ロールを作成する

  • 作成概要

    • IAMポリシー名: awsconfig
    • IAMロール名: awsconfig
    • 信頼されたエンティティ: config.amazonaws.com
    • アタッチするポリシー: AWSConfigRole(AWS 管理ポリシー), awsconfig(管理ポリシー)
  • anyservice/anyenv/iam/backend.tf

backend.tf
terraform {
  required_version = ">= 0.12.9"
  backend "s3" {
    shared_credentials_file = "../../../credentials"
    profile                 = "terraform"
    region                  = "ap-northeast-1"
    bucket                  = "terraform-tfstate-チョメチョメ"
    key                     = "anyservice/anyenv/iam/terraform.tfstate"
  }
}
  • anyservice/anyenv/iam/awsconfig.tf
awsconfig.tf
data "aws_caller_identity" "current" {
}
data "aws_iam_policy_document" "assume_role_policy-awsconfig" {
  version = "2012-10-17"
  statement {
    sid    = ""
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["config.amazonaws.com"]
    }
    actions = [
      "sts:AssumeRole"
    ]
  }
}
resource "aws_iam_role" "role_awsconfig" {
  name               = "awsconfig"
  path               = "/service-role/"
  assume_role_policy = "${data.aws_iam_policy_document.assume_role_policy-awsconfig.json}"
}
resource "aws_iam_role_policy_attachment" "awsconfig_AWSConfigRole" {
  role       = aws_iam_role.role_awsconfig.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSConfigRole"
}
data "aws_iam_policy_document" "iam_policy-awsconfig" {
  version = "2012-10-17"
  statement {
    effect = "Allow"
    actions = [
      "s3:PutObject*"
    ]
    resources = [
      "${data.terraform_remote_state.S3.outputs.awsconfig_arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"
    ]
    condition {
      test     = "StringLike"
      variable = "s3:x-amz-acl"
      values   = ["bucket-owner-full-control"]
    }
  }
  statement {
    effect = "Allow"
    actions = [
      "s3:GetBucketAcl"
    ]
    resources = [
      data.terraform_remote_state.S3.outputs.awsconfig_arn
    ]
  }
}
resource "aws_iam_policy" "iam_policy-awsconfig" {
  name   = "awsconfig"
  policy = "${data.aws_iam_policy_document.iam_policy-awsconfig.json}"
}
resource "aws_iam_role_policy_attachment" "awsconfig" {
  role       = aws_iam_role.role_awsconfig.name
  policy_arn = "${aws_iam_policy.iam_policy-awsconfig.arn}"
}
output "role_awsconfig_arn" {
  value = aws_iam_role.role_awsconfig.arn
}
  • terraform_remote_state.tf
terraform_remote_state.tf
data "terraform_remote_state" "IAM" {
  backend = "s3"
  config = {
    shared_credentials_file = "../../../credentials"
    profile                 = "terraform"
    region                  = "ap-northeast-1"
    bucket                  = "terraform-tfstate-チョメチョメ"
    key                     = "anyservice/anyenv/iam/terraform.tfstate"
  }
}

3.5. AWS Config を作成する

  • 作成概要

    • 名前: awsconfig-AWSアカウントID
    • AWS Config ロール名: awsconfig
    • 記録するリソースタイプ
      • このリージョンでサポートされているすべてのリソースを記録する
      • グローバルリソース (AWS IAM リソースなど) を含める
    • 配信方法:
      • S3バケット: awsconfig-アカウントID
      • SNS トピック名: - (今回は AWS Config から SNS への通知設定はしない)
    • データ保持期間: デフォルト(7年)
  • anyservice/anyenv/awsconfig/backend.tf

backend.tf
terraform {
  required_version = ">= 0.12.9"
  backend "s3" {
    shared_credentials_file = "../../../credentials"
    profile                 = "terraform"
    region                  = "ap-northeast-1"
    bucket                  = "terraform-tfstate-チョメチョメ"
    key                     = "anyservice/anyenv/awsconfig/terraform.tfstate"
  }
}
  • anyservice/anyenv/awsconfig/main.tf
main.tf
data "aws_caller_identity" "current" {
}
resource "aws_config_configuration_recorder" "awsconfig" {
  name     = "awsconfig-${data.aws_caller_identity.current.account_id}"
  role_arn = data.terraform_remote_state.IAM.outputs.role_awsconfig_arn
  recording_group {
    all_supported                 = "true"
    include_global_resource_types = "true"
  }
}
resource "aws_config_delivery_channel" "awsconfig" {
  name           = "awsconfig-${data.aws_caller_identity.current.account_id}"
  s3_bucket_name = data.terraform_remote_state.S3.outputs.awsconfig_name
  depends_on     = ["aws_config_configuration_recorder.awsconfig"]
  snapshot_delivery_properties {
    delivery_frequency = "One_Hour"
  }
}
  • AWS Config を作成すると、「記録はオン」となり、履歴データが S3 バケットへ配信される。
20200217-06.png

4. 「3.」で作成した設定の確認

4.1. 履歴データを格納する S3 バケット (awsconfig-AWSアカウントID)のバケットポリシー

20200217-01.png

4.2. IAMポリシー(awsconfig)

20200217-02.png

4.3. IAMロール(awsconfig)

20200217-03.png
20200217-04.png

4.4. AWS Config

20200217-05.png

5. AWS Config ルール

5.1. AWS Config ルールは2通りある

  • マネージドルール(AWS側で作成して提供しているルール)

    • 定義済みのカスタマイズ可能なルールであり、AWS リソースが一般的なベストプラクティスに準拠しているかどうかを評価するために AWS Config で使用します。
    • AWSが提供している AWS Config マネージドルールのリストの中から利用したいものを選んでルールの使用を有効にする。
    • ルールを有効化すると、AWS Config はリソースをルールの条件と比較します。この初期評価後は、評価がトリガーされるたびに AWS Config で評価が実行されます。
    • 評価のトリガーは2タイプある
      • 設定変更 – ルールの範囲に該当するリソースで設定が変更されると、AWS Config によって評価がトリガーされます。
      • 定期的 – 指定した間隔 (24 時間ごとなど) で、AWS Config でのルールの評価が実行されます。
  • カスタムルール(ユーザ側で作成するルール)

    • カスタムルールを作成して AWS Config に追加できます。各カスタムルールは AWS Lambda 関数と関連付けます。この関数には、AWS リソースがルールに準拠しているかどうかを評価するロジックが含まれています。
    • この関数をルールに関連付け、設定変更または定期的な間隔に応じてルールから関数を呼び出します。
    • 次に、関数はリソースがルールに準拠しているかどうかを評価し、評価結果を AWS Config に送信します。

引用
AWS Docs: AWS Config マネージドルール
AWS Docs: AWS Config カスタムルール

5.2. 今回使用するマネージドルール

  • 分析

  • コンピューティング

    • ebs-optimized-instance
      • EBS 最適化できる EC2 インスタンスに対して EBS 最適化が有効になっているかどうかを確認します。
    • ec2-stopped-instance
      • 許可されている日数よりも長く停止しているインスタンスがあるかどうかを確認します。
      • デフォルトは30日。ここでは、60日に設定する。
    • ec2-instance-managed-by-systems-manager
      • アカウントの Amazon EC2 インスタンスが AWS Systems Manager で管理されているかどうかを確認します。
    • ec2-security-group-attached-to-eni
      • セキュリティグループが EC2 インスタンスまたは Elastic Network Interface にアタッチされていることを確認します。
    • ec2-volume-inuse-check
      • EBS ボリュームが EC2 インスタンスにアタッチされているかどうかを確認します。
    • eip-attached
      • VPC に割り当てられたすべての Elastic IP アドレスが、EC2 インスタンスまたは使用中の Elastic Network Interface (ENI) にアタッチされているかどうかを確認します。
    • elb-logging-enabled
      • Application Load Balancer と クラシックロードバランサー でログ記録が有効になっているかどうかを確認します。
    • restricted-ssh
      • セキュリティグループの受信 SSH トラフィックがアクセス可能かどうかを確認します。
  • データベース

    • db-instance-backup-enabled
      • RDS DB インスタンスでバックアップが有効になっているかどうかを確認します。
    • dynamodb-throughput-limit-check
      • DynamoDB プロビジョンドスループットがアカウントの上限に近づいているかどうかを確認します。
    • rds-instance-public-access-check
      • Amazon Relational Database Service インスタンスがパブリックにアクセス可能でないかどうかを確認します。
    • rds-multi-az-support
      • RDS DB インスタンスの高可用性が有効になっているかどうかを確認します。
    • rds-snapshots-public-prohibited
      • Amazon Relational Database Service (Amazon RDS) スナップショットがパブリックかどうかを確認します。
  • マネジメントとガバナンス

    • cloud-trail-cloud-watch-logs-enabled
      • AWS CloudTrail 証跡がログを Amazon CloudWatch Logs に送信するように設定されているかどうかを確認します。
    • cloudtrail-enabled
      • AWS アカウントで AWS CloudTrail が有効になっているかどうかを確認します。
    • cloud-trail-encryption-enabled
      • AWS CloudTrail がサーバー側の暗号化 (SSE) AWS Key Management Service (AWS KMS) カスタマーマスターキー (CMK) 暗号化を使用するよう設定されているかどうかを確認します。
    • cloud-trail-log-file-validation-enabled
      • AWS CloudTrail でログを含むダイジェストファイルを作成するかどうかを確認します。
    • cloudtrail-s3-dataevents-enabled
      • 少なくとも 1 つの AWS CloudTrail 証跡がすべての S3 バケットの Amazon S3 データイベントをログ記録しているかどうかを確認します。
    • multi-region-cloud-trail-enabled
      • マルチリージョン AWS CloudTrail が少なくとも 1 つあることを確認します。
  • ネットワークとコンテンツ配信

    • internet-gateway-authorized-vpc-only
      • インターネットゲートウェイ (IGW) が承認された Amazon Virtual Private Cloud (VPC) にのみアタッチされていることを確認します。
      • authorizedVpcIds パラメータに、IGWのアタッチが許可されたVPCのidをリストする(複数ある場合はカンマ区切り)。
    • vpc-default-security-group-closed
      • いずれの Amazon Virtual Private Cloud (VPC) のデフォルトのセキュリティグループでもインバウンドとアウトバウンドのいずれのトラフィックも許可しないことを確認します。
    • vpc-flow-logs-enabled
      • Amazon Virtual Private Cloud フローログが見つかり、Amazon VPC に対して有効になっているかどうかを確認します。
  • セキュリティ、アイデンティティ、コンプライアンス

    • access-keys-rotated
      • アクティブなアクセスキーが、maxAccessKeyAge で指定された日数内にローテーションされるかどうかを確認します。
      • デフォルトは90日。ここでは180日に設定する。
    • acm-certificate-expiration-check
      • アカウントの ACM 証明書が、指定の日数内に有効期限切れとしてマークされているかどうかを確認します。
      • ここでは14日に設定する。
    • iam-group-has-users-check
      • IAM グループに少なくとも 1 つの IAM ユーザーが存在するかどうかを確認します。
    • iam-password-policy
      • IAM ユーザーのアカウントパスワードポリシーが、指定した要件を満たしているかどうかを確認します。
      • ここではパスワードポリシーを次のとおりに決める。
        • MaxPasswordAge = "90"
        • MinimumPasswordLength = "12"
        • PasswordReusePrevention = "3"
        • RequireLowercaseCharacters = "true"
        • RequireNumbers = "true"
        • RequireSymbols = "true"
        • RequireUppercaseCharacters = "true"
    • iam-root-access-key-check
      • ルートユーザーアクセスキーがあるかどうかを確認します。ユーザーアクセスキーが存在しない場合、ルールは COMPLIANT です。
    • iam-user-group-membership-check
      • IAM ユーザーが少なくとも 1 つの IAM グループのメンバーであるかどうかを確認します。
    • iam-user-no-policies-check
      • いずれの IAM ユーザーにもポリシーがアタッチされていないことを確認します。IAM ユーザーは、IAM グループまたはロールからアクセス許可を継承する必要があります。
    • iam-user-unused-credentials-check
      • IAMユーザーが、指定した日数以内に使用されたことのないパスワードまたはアクティブなアクセスキーを持っているかどうかを確認します。
      • デフォルトは90日。ここでは180日に設定する。
    • mfa-enabled-for-iam-console-access
      • コンソールパスワードを使用するすべての IAM ユーザーについて MFA が有効になっているかどうかを確認します。
    • root-account-mfa-enabled
      • AWS アカウントのユーザーが、ルートの認証情報を使用してサインインする際に MFA デバイスが必要かどうかを確認します。
  • ストレージ

6. AWS Config ルールの設定を Terraform (v0.12.9)を使って設定する

6.1. Terraform構成

「3.1. Terraform構成」に次のファイルを追加

  • AWS Config ルールを設定する terraform

    • anyservice/anyenv/awsconfig/config-rule.tf
  • AWS Config ルールからの通知を Amazon SNS へ送るための CloudWatch Events を設定する terraform

    • anyservice/anyenv/cloudwatchevents
anyservice/
└── anyenv
    ├── awsconfig
    │   ├── backend.tf
    │   ├── config-rule.tf
    │   ├── main.tf
    │   ├── provider.tf -> ../../../provider.tf
    │   └── terraform_remote_state.tf -> ../../../terraform_remote_state.tf
    ├── cloudwatchevents
    │   ├── awsconfig.tf
    │   ├── backend.tf
    │   ├── provider.tf -> ../../../provider.tf
    ├── iam
    │   ├── aws-config.tf
    │   ├── backend.tf
    │   ├── provider.tf -> ../../../provider.tf
    │   └── terraform_remote_state.tf -> ../../../terraform_remote_state.tf
    └── s3
        ├── awsconfig.tf
        ├── backend.tf
        └── provider.tf -> ../../../provider.tf

6.2. AWS Config ルールからの通知を Amazon SNS へ送るための CloudWatch Events を作成する

  • 作成概要

    • CloudWatch イベントルール名: AWSConfig
    • イベントパターン:
      • source: aws.config
      • detail-type: "Config Rules Compliance Change"
    • ターゲット: SNS トピック(適当なものを使うか、新規で作るか。AWS Chatbot にも対応している。)
  • anyservice/anyenv/cloudwatchevents/awsconfig.tf

awsconfig.tf
resource "aws_cloudwatch_event_rule" "awsconfig" {
  name = "AWSConfig"
  event_pattern = jsonencode(
    {
      detail-type = [
        "Config Rules Compliance Change",
      ]
      source = [
        "aws.config",
      ]
    }
  )
}
resource "aws_cloudwatch_event_target" "awsconfig" {
  rule      = "${aws_cloudwatch_event_rule.awsconfig.name}"
  arn       = "SNSトピックの ARN"
}

6.3. AWS Config ルールを作成する

  • 作成概要

    • 作成する Config ルール: 「5.2. 今回使用するマネージドルール」
    • 初回作成後(ルール適用直後)は、SNSに大量に通知が飛んでくる。
  • anyservice/anyenv/awsconfig/config-rule.tf

config-rule.tf
#------------------------------------------------------------
# AWS Config Rule
#------------------------------------------------------------
#
# Analytics
#
resource "aws_config_config_rule" "elasticache-redis-cluster-automatic-backup-check" {
  name = "elasticache-redis-cluster-automatic-backup-check"
  source {
    owner             = "AWS"
    source_identifier = "ELASTICACHE_REDIS_CLUSTER_AUTOMATIC_BACKUP_CHECK"
  }
}
#
# Compute
#
resource "aws_config_config_rule" "ebs-optimized-instance" {
  name = "ebs-optimized-instance"
  source {
    owner             = "AWS"
    source_identifier = "EBS_OPTIMIZED_INSTANCE"
  }
}
resource "aws_config_config_rule" "ec2-stopped-instance" {
  name = "ec2-stopped-instance"
  source {
    owner             = "AWS"
    source_identifier = "EC2_STOPPED_INSTANCE"
  }
  input_parameters = jsonencode(
    {
      AllowedDays = "60"
    }
  )
}
resource "aws_config_config_rule" "ec2-instance-managed-by-systems-manager" {
  name = "ec2-instance-managed-by-systems-manager"
  source {
    owner             = "AWS"
    source_identifier = "EC2_INSTANCE_MANAGED_BY_SSM"
  }
}
resource "aws_config_config_rule" "ec2-security-group-attached-to-eni" {
  name = "ec2-security-group-attached-to-eni"
  source {
    owner             = "AWS"
    source_identifier = "EC2_SECURITY_GROUP_ATTACHED_TO_ENI"
  }
}
resource "aws_config_config_rule" "ec2-volume-inuse-check" {
  name = "ec2-volume-inuse-check"
  source {
    owner             = "AWS"
    source_identifier = "EC2_VOLUME_INUSE_CHECK"
  }
}
resource "aws_config_config_rule" "eip-attached" {
  name = "eip-attached"
  source {
    owner             = "AWS"
    source_identifier = "EIP_ATTACHED"
  }
  scope {
    compliance_resource_types = [
      "AWS::EC2::EIP"
    ]
  }
}
resource "aws_config_config_rule" "elb-logging-enabled" {
  name = "elb-logging-enabled"
  source {
    owner             = "AWS"
    source_identifier = "ELB_LOGGING_ENABLED"
  }
  scope {
    compliance_resource_types = [
      "AWS::ElasticLoadBalancing::LoadBalancer",
      "AWS::ElasticLoadBalancingV2::LoadBalancer"
    ]
  }
}
resource "aws_config_config_rule" "restricted-ssh" {
  name = "restricted-ssh"
  source {
    owner             = "AWS"
    source_identifier = "INCOMING_SSH_DISABLED"
  }
  scope {
    compliance_resource_types = [
      "AWS::EC2::SecurityGroup"
    ]
  }
}
#
# Database
#
resource "aws_config_config_rule" "db-instance-backup-enabled" {
  name = "db-instance-backup-enabled"
  source {
    owner             = "AWS"
    source_identifier = "DB_INSTANCE_BACKUP_ENABLED"
  }
}
resource "aws_config_config_rule" "dynamodb-throughput-limit-check" {
  name = "dynamodb-throughput-limit-check"
  source {
    owner             = "AWS"
    source_identifier = "DYNAMODB_THROUGHPUT_LIMIT_CHECK"
  }
}
resource "aws_config_config_rule" "rds-instance-public-access-check" {
  name = "rds-instance-public-access-check"
  source {
    owner             = "AWS"
    source_identifier = "RDS_INSTANCE_PUBLIC_ACCESS_CHECK"
  }
  scope {
    compliance_resource_types = [
      "AWS::RDS::DBInstance"
    ]
  }
}
resource "aws_config_config_rule" "rds-multi-az-support" {
  name = "rds-multi-az-support"
  source {
    owner             = "AWS"
    source_identifier = "RDS_MULTI_AZ_SUPPORT"
  }
  scope {
    compliance_resource_types = [
      "AWS::RDS::DBInstance"
    ]
  }
}
resource "aws_config_config_rule" "rds-snapshots-public-prohibited" {
  name = "rds-snapshots-public-prohibited"
  source {
    owner             = "AWS"
    source_identifier = "RDS_SNAPSHOTS_PUBLIC_PROHIBITED"
  }
  scope {
    compliance_resource_types = [
      "AWS::RDS::DBInstance"
    ]
  }
}
#
# Management and Governance
#
resource "aws_config_config_rule" "cloud-trail-cloud-watch-logs-enabled" {
  name = "cloud-trail-cloud-watch-logs-enabled"
  source {
    owner             = "AWS"
    source_identifier = "CLOUD_TRAIL_CLOUD_WATCH_LOGS_ENABLED"
  }
}
resource "aws_config_config_rule" "cloudtrail-enabled" {
  name = "cloudtrail-enabled"
  source {
    owner             = "AWS"
    source_identifier = "CLOUD_TRAIL_ENABLED"
  }
  maximum_execution_frequency = "TwentyFour_Hours"
}
resource "aws_config_config_rule" "cloud-trail-encryption-enabled" {
  name = "cloud-trail-encryption-enabled"
  source {
    owner             = "AWS"
    source_identifier = "CLOUD_TRAIL_ENCRYPTION_ENABLED"
  }
}
resource "aws_config_config_rule" "cloud-trail-log-file-validation-enabled" {
  name = "cloud-trail-log-file-validation-enabled"
  source {
    owner             = "AWS"
    source_identifier = "CLOUD_TRAIL_LOG_FILE_VALIDATION_ENABLED"
  }
}
resource "aws_config_config_rule" "cloudtrail-s3-dataevents-enabled" {
  name = "cloudtrail-s3-dataevents-enabled"
  source {
    owner             = "AWS"
    source_identifier = "CLOUDTRAIL_S3_DATAEVENTS_ENABLED"
  }
}
resource "aws_config_config_rule" "multi-region-cloud-trail-enabled" {
  name = "multi-region-cloud-trail-enabled"
  source {
    owner             = "AWS"
    source_identifier = "MULTI_REGION_CLOUD_TRAIL_ENABLED"
  }
}
#
# Network and Content Delivery
#
resource "aws_config_config_rule" "internet-gateway-authorized-vpc-only" {
  name = "internet-gateway-authorized-vpc-only"
  source {
    owner             = "AWS"
    source_identifier = "INTERNET_GATEWAY_AUTHORIZED_VPC_ONLY"
  }
  input_parameters = jsonencode(
    {
      AuthorizedVpcIds = "vpc-チョメチョメ,vpc-チョメチョメ"
    }
  )
}
resource "aws_config_config_rule" "vpc-default-security-group-closed" {
  name = "vpc-default-security-group-closed"
  source {
    owner             = "AWS"
    source_identifier = "VPC_DEFAULT_SECURITY_GROUP_CLOSED"
  }
}
resource "aws_config_config_rule" "vpc-flow-logs-enabled" {
  name = "vpc-flow-logs-enabled"
  source {
    owner             = "AWS"
    source_identifier = "VPC_FLOW_LOGS_ENABLED"
  }
}
#
# Security, Identity & Compliance
#
resource "aws_config_config_rule" "access-keys-rotated" {
  name = "access-keys-rotated"
  source {
    owner             = "AWS"
    source_identifier = "ACCESS_KEYS_ROTATED"
  }
  input_parameters = jsonencode(
    {
      maxAccessKeyAge = "180"
    }
  )
}
resource "aws_config_config_rule" "acm-certificate-expiration-check" {
  name = "acm-certificate-expiration-check"
  source {
    owner             = "AWS"
    source_identifier = "ACM_CERTIFICATE_EXPIRATION_CHECK"
  }
  input_parameters = jsonencode(
    {
      daysToExpiration = "14"
    }
  )
  maximum_execution_frequency = "TwentyFour_Hours"
  scope {
    compliance_resource_types = [
      "AWS::ACM::Certificate",
    ]
  }
}
resource "aws_config_config_rule" "iam-group-has-users-check" {
  name = "iam-group-has-users-check"
  source {
    owner             = "AWS"
    source_identifier = "IAM_GROUP_HAS_USERS_CHECK"
  }
}
resource "aws_config_config_rule" "iam-password-policy" {
  name = "iam-password-policy"
  source {
    owner             = "AWS"
     source_identifier = "IAM_PASSWORD_POLICY"
   }
  depends_on  = ["aws_config_configuration_recorder.awsconfig"]
  description = ""
  input_parameters = jsonencode(
    {
      MaxPasswordAge             = "90"
      MinimumPasswordLength      = "12"
      PasswordReusePrevention    = "3"
      RequireLowercaseCharacters = "true"
      RequireNumbers             = "true"
      RequireSymbols             = "true"
      RequireUppercaseCharacters = "true"
    }
  )
  maximum_execution_frequency = "TwentyFour_Hours"
}
resource "aws_config_config_rule" "iam-root-access-key-check" {
  name = "iam-root-access-key-check"
  source {
    owner             = "AWS"
    source_identifier = "IAM_ROOT_ACCESS_KEY_CHECK"
  }
}
resource "aws_config_config_rule" "iam-user-group-membership-check" {
  name = "iam-user-group-membership-check"
  source {
    owner             = "AWS"
    source_identifier = "IAM_USER_GROUP_MEMBERSHIP_CHECK"
  }
}
resource "aws_config_config_rule" "iam-user-no-policies-check" {
  name = "iam-user-no-policies-check"
  source {
    owner             = "AWS"
    source_identifier = "IAM_USER_NO_POLICIES_CHECK"
  }
}
resource "aws_config_config_rule" "iam-user-unused-credentials-check" {
  name = "iam-user-unused-credentials-check"
  source {
    owner             = "AWS"
    source_identifier = "IAM_USER_UNUSED_CREDENTIALS_CHECK"
  }
  input_parameters = jsonencode(
    {
      maxCredentialUsageAge = "180"
    }
  )
}
resource "aws_config_config_rule" "mfa-enabled-for-iam-console-access" {
  name = "mfa-enabled-for-iam-console-access"
  source {
    owner             = "AWS"
    source_identifier = "MFA_ENABLED_FOR_IAM_CONSOLE_ACCESS"
  }
  description                 = ""
  maximum_execution_frequency = "One_Hour"
}
resource "aws_config_config_rule" "root-account-mfa-enabled" {
  name = "root-account-mfa-enabled"
  source {
    owner             = "AWS"
    source_identifier = "ROOT_ACCOUNT_MFA_ENABLED"
  }
  maximum_execution_frequency = "TwentyFour_Hours"
}
#
# Storage
#
resource "aws_config_config_rule" "ebs-snapshot-public-restorable-check" {
  name = "ebs-snapshot-public-restorable-check"
  source {
    owner             = "AWS"
    source_identifier = "EBS_SNAPSHOT_PUBLIC_RESTORABLE_CHECK"
  }
  maximum_execution_frequency = "TwentyFour_Hours"
}
resource "aws_config_config_rule" "elb-deletion-protection-enabled" {
  name = "elb-deletion-protection-enabled"
  source {
    owner             = "AWS"
    source_identifier = "ELB_DELETION_PROTECTION_ENABLED"
  }
}
resource "aws_config_config_rule" "s3-account-level-public-access-blocks" {

  name = "s3-account-level-public-access-blocks"
  source {
    owner             = "AWS"
    source_identifier = "S3_ACCOUNT_LEVEL_PUBLIC_ACCESS_BLOCKS"
  }
}
resource "aws_config_config_rule" "s3-bucket-public-write-prohibited" {
  name = "s3-bucket-public-write-prohibited"
  source {
    owner             = "AWS"
    source_identifier = "S3_BUCKET_PUBLIC_WRITE_PROHIBITED"
  }
  maximum_execution_frequency = "TwentyFour_Hours"
  scope {
    compliance_resource_types = [
      "AWS::S3::Bucket"
    ]
  }
}
resource "aws_config_config_rule" "s3-bucket-public-read-prohibited" {
  name = "s3-bucket-public-read-prohibited"
  source {
    owner             = "AWS"
    source_identifier = "S3_BUCKET_PUBLIC_READ_PROHIBITED"
  }
  maximum_execution_frequency = "TwentyFour_Hours"
  scope {
    compliance_resource_types = [
      "AWS::S3::Bucket"
    ]
  }
}

7. 「6.」で作成した設定の確認

7.1. CloudWatch イベントルール

20200218-01.png

7.2. AWS Config ルール

20200218-02.png

7.3. AWS Config ルール からの通知

※ AWS Chatbot から Slack へ飛ばしている

  • 準拠の例
20200218-03.png
  • 非準拠の例
20200218-04.png

7.4. AWS Config ダッシュボード

結構赤いw(非準拠多い・・)

20200218-05.png

8. 非準拠項目の確認例

  • access-keys-rotated を見てみる
20200218-06.png


9個の IAMユーザーが access-keys-rotated ルールに準拠していない。。

20200218-07.png


その中の IAMユーザーの dev-test-user を見てみると、「iam-user-group-membership-check」ルールと「iam-user-no-policies-check」ルールにも準拠していない。。

20200218-08.png

コンプライアンスタイムライン なんていうものもある

20200218-09.png

20200218-10.png

9. 非準拠項目の対応例

9.1. iam-user-group-membership-check ルールの非準拠対応

新規にIAMグループ testgroup を作成して、IAMユーザーの dev-test-user をそのグループに属してみる。

しばらく(数十分)すると、Config ルールから通知が届いた

20200218-11.png
  • 上の通知は、iam-user-group-membership-check ルールの通知で、IAMユーザー dev-test-user がIAMグループに属したのでルールに準拠したという通知。

  • 下の通知は、iam-group-has-users-check ルールの通知で、新規に作成した IAMグループ testgroup がIAMユーザー dev-test-user を持っているので、ルールに準拠しているという通知。

9.2. コンプライタイムラインの確認

IAM ユーザー dev-test-user の コンプライアンスタイムライン を確認すると、非準拠ルールが3個から2個に減った。

20200218-12.png

「関係」を見てみる

20200218-13.png

リンク踏むと、IAMユーザー dev-test-user の設定タイムラインページへ飛ぶ
設定タイムラインの「変更」を見ると、IAMグループ testgroup に属したことが確認できる。

20200218-14.png

IAM ユーザー dev-test-user の コンプライアンスタイムライン に戻って、「変更」を見てみる。
コンプライアンスタイムラインの「変更」を見ると、Configルール iam-user-group-membership-check が NON_COMPLIANT から COMPLIANT へ変わったことが確認できる。

20200218-15.png

10. IAMベストプラクティスにほぼほぼ準拠したIAMユーザ・グループ作成について

https://qiita.com/tonishy/items/f9cdacd1a51b92724d15

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした