2
3

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

Last updated at Posted at 2024-02-06

概要

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

最終的な構成は以下の図のようになります。
kosezu.png

Security Hubの設定

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

また、「セキュリティ基準」と呼ばれるセキュリティのベストプラクティスに基づいて、リソースの設定をチェックすることができます。このチェックはAWS Configと連携して行われます。

以下のように記述することで、Security Hubを有効化することができます。デフォルトで有効化されているセキュリティ基準を無効化している点に注意してください。
セキュリティ基準の有効化は、AWS Configの設定の後に行います。

SecurityHubを有効化する
resource "aws_securityhub_account" "default" {
  #デフォルトで有効化されているセキュリティ基準を無効化
  enable_default_standards = false
}

AWS Configの設定

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

AWS Configはリソースの設定履歴をS3に保存することができます。この情報は、リソースの設定変更の履歴を確認する際に利用します。
S3のアクセスログを保管するために、別途S3バケットを作成していますが、これは設定履歴を保管しているバケットが誤ってpublicになった時にその影響範囲を調査するために利用されます。

保管用のS3バケットを作成する

AWS Configの設定履歴を保存するためのS3バケットを作成します。

保管用の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バケットのアクセスログを保管するためのバケットを作成する

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用に用意されたマネージドポリシーが存在するので、それを利用しています。

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を有効化することができます。

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 基礎セキュリティのベストプラクティス」を有効化しています。

「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

Config Rulesのチェックの結果をSecurity Hubで確認する

Config Rulesのチェックの結果はSecurity Hubで確認することができます。
この結果から、リソースの設定を修正するか、または無視してそのチェック自体を無効化することを判断していくことになります。
config-rule-kekka.png

Macieの設定

MacieはS3バケットに保存されているデータのセキュリティを監視するサービスです。有効化すれば、S3バケットに保存されているデータの中から、個人情報や機密情報などの特定のデータを検出することができます。
また検出結果はSecurity Hubに連携されます。

Macieを有効化する
resource "aws_macie2_account" "default" {
  status                       = "ENABLED"
}

有効化すると以下のようにS3バケットに対するチェックが実行されます。
macie.png

IAM Access Analyzerの設定

IAM Access Analyzerは、IAMポリシーによるリソースへのアクセス権限を分析するサービスです。有効化すれば、リソースに対するアクセス権限の分析が実行されます。
また検出結果はSecurity Hubに連携されます。

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

Inspectorの設定

InspectorはEC2やECRのイメージに対してアプリの観点から脆弱性のスキャンを実行するサービスです。有効化すれば、リソースに対する脆弱性のスキャンが実行されます。
また検出結果はSecurity Hubに連携されます。

今回はECRのイメージに対してスキャンを実行するため、以下のように記述しています。

Inspectorを有効化する
resource "aws_inspector2_enabler" "ecr" {
  #アカウントIDとリソースタイプを指定
  account_ids    = ["${var.account_id}"]
  resource_types = ["ECR"]
}

有効化すると以下のようにECRのイメージに対するスキャンが実行されます。
inspector.png

GuardDutyの設定

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

GuardDutyはCloudTrail、VPC Flow Logs、DNSログなどのログの情報をもとにセキュリティイベントを検知します。そのため、これらのログを有効化しておく必要があります。また、検知された際の調査のためにログを保存するS3バケットを作成しておく必要があります。
guard-duty-zu.png

CloudTrailの設定

CloudTrailはAWSリソースの操作ログを記録するサービスです。
CloudTrailはアカウント設定時からログの出力が有効化されていますが、そのログの保存期間は90日間となっています。そのためログの長期保存を考慮してS3バケットにログが転送されるように設定します。
バケットポリシーにはCloudTrailからのログの取得と保存を許可するポリシーを設定している点に注意してください。

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へ転送するために以下のように設定します。

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バケットを作成します。

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へ転送するように設定します。

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コンソールから設定します。
以下の記事などを参照に、設定を行ってください。

GuardDutyの有効化

GuardDutyは初期設定では無効化されているため、有効化します。

GuardDutyを有効化する
resource "aws_guardduty_detector" "default" {
  enable = true
}

有効化すると以下のようにセキュリティイベントの検知が実行されます。

guardduty.png

AWS Healthについて

AWS HealthはAWSのサービスの障害情報を提供するサービスです。
セキュリティに関する通知はSecurity Hubに連携されるようにデフォルトで設定されています。
health-zu.png

連携の有無については以下のように確認することができます。
health.png

Detectiveの設定

DetectiveはGuiardDutyで検知されたセキュリティイベントを解析するためのサービスです。
detective-zu.png

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

Athenaの設定

CloudTrailのログやConfig構成情報を解析する際は、Athenaを利用します。Athenaの利用方法についてはここでは説明しません。
athena-zu.png

セキュリティイベントの通知の設定

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

以下の図のように、EventBridgeルールを利用して、SecurityHubに新規のセキュリティイベントが届いたことを検知して、それを起点としてSNSトピックからメールを送信するように設定します。
sns-zu.png

まずはSNSトピックを作成します。送信先には運用者のメールアドレスを指定します。また、EventBridgeからメッセージを受け取るためにSNSトピックポリシーを設定します。

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トピックにメッセージを送信するように設定します。

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リソースのセキュリティ監視環境の構築は完了です。

参考文献

本記事は以下の書籍、記事を参考にさせていただきました。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3