Преглед изворни кода

MultiAccount Logging Improvements

* Fixes the AWS config input to use a better named SNS/SQS pipeline
* Creates a bucket for ELB logs
* Fixes outputs
* Changes account_alerts SNS to be a separate thing.
Fred Damstra пре 5 година
родитељ
комит
d9f9290417

+ 2 - 2
base/account_standards/config.tf

@@ -37,7 +37,7 @@ data "aws_iam_policy_document" "awsconfig" {
     sid = "PublishAlertsToSNS"
     effect = "Allow"
     actions = [ "sns:Publish" ]
-    resources = [ "arn:${var.aws_partition}:sns:${var.aws_region}:${local.c2_account}:account-alerts" ]
+    resources = [ "arn:${var.aws_partition}:sns:${var.aws_region}:${local.c2_account}:config-notifications" ]
   }
 
   statement {
@@ -97,7 +97,7 @@ resource "aws_config_configuration_recorder" "awsconfig_recorder" {
 resource "aws_config_delivery_channel" "awsconfig_delivery_channel" {
   name           = "xdr-config-delivery-channel"
   s3_bucket_name = "xdr-config-${local.logging_environment}"
-  sns_topic_arn  = "arn:${var.aws_partition}:sns:${var.aws_region}:${local.c2_account}:account-alerts"
+  sns_topic_arn  = "arn:${var.aws_partition}:sns:${var.aws_region}:${local.c2_account}:config-notifications"
 
   snapshot_delivery_properties {
     delivery_frequency = "One_Hour"

+ 25 - 0
base/account_standards_c2/account_alerts.tf

@@ -0,0 +1,25 @@
+# An SNS queue for email alerts
+resource "aws_sns_topic" "account-alerts" {
+  name              = "account-alerts"
+  tags = merge(var.standard_tags, var.tags)
+}
+
+resource "aws_sns_topic_policy" "account-alerts" {
+  arn    = aws_sns_topic.account-alerts.arn
+  policy = data.aws_iam_policy_document.account-alerts.json
+}
+
+data "aws_iam_policy_document" "account-alerts" {
+  statement {
+    sid = "AllowAllAccountsToPublish"
+    actions = [ "SNS:Publish" ]
+    effect = "Allow"
+    resources = [ aws_sns_topic.account-alerts.arn ]
+    principals {
+      type = "AWS"
+      identifiers = [ for a in var.responsible_accounts[var.environment]: "arn:${var.aws_partition}:iam::${a}:root" ]
+    }
+  }
+}
+
+# Unfortunately, terraform does not support email destinations

+ 110 - 18
base/account_standards_c2/config_aggregator.tf

@@ -7,13 +7,13 @@ resource "aws_config_configuration_aggregator" "account" {
   }
 }
 
-resource "aws_sns_topic" "account-alerts" {
-  name              = "account-alerts"
-  #kms_master_key_id = "alias/aws/sns" # TODO
+resource "aws_sns_topic" "config-notifications" {
+  name              = "config-notifications"
+  #kms_master_key_id = aws_kms_key.config-notifications-key.id # TODO
 }
 
-resource "aws_sns_topic_policy" "account-alerts" {
-  arn    = aws_sns_topic.account-alerts.arn
+resource "aws_sns_topic_policy" "config-notifications" {
+  arn    = aws_sns_topic.config-notifications.arn
   policy = data.aws_iam_policy_document.config-sns.json
 }
 
@@ -22,23 +22,115 @@ data "aws_iam_policy_document" "config-sns" {
     sid = "AllowConfig"
     actions = [ "SNS:Publish" ]
     effect = "Allow"
-    resources = [ aws_sns_topic.account-alerts.arn ]
+    resources = [ aws_sns_topic.config-notifications.arn ]
     principals {
       type = "AWS"
       identifiers = [ for a in var.responsible_accounts[var.environment]: "arn:${var.aws_partition}:iam::${a}:root" ]
     }
   }
+}
+
+resource "aws_sqs_queue" "config-notifications" {
+  name                       = "config-notifications"
+  visibility_timeout_seconds = 300 # wait 5 minutes before allowing a different splunk instance to process the same message
+  message_retention_seconds  = 604800 # Keep a message in the queue for 7 days
+  receive_wait_time_seconds  = 0 # how long to wait for a message before returning
+  redrive_policy             = "{\"deadLetterTargetArn\":\"${aws_sqs_queue.dlq.arn}\",\"maxReceiveCount\":4}"
+  tags                       = merge(var.standard_tags, var.tags)
+  kms_master_key_id          = aws_kms_key.config-notifications-key.id
+  kms_data_key_reuse_period_seconds = 3600
+}
+
+data "aws_iam_policy_document" "config-notifications-sns-topic-can-publish" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      identifiers = [ "*" ]
+      type = "AWS"
+    }
+
+    actions = [ "SQS:SendMessage" ]
+
+    resources = [ aws_sqs_queue.config-notifications.arn ]
+
+    condition {
+      test = "ArnEquals"
+      values = [ aws_sns_topic.config-notifications.arn ]
+      variable = "aws:SourceArn"
+    }
+  }
+}
+
+// Dead Letter queue, use same parameters as main queue
+resource "aws_sqs_queue" "config-notifications-dlq" {
+  name                      = "config-notifications-dlq"
+  message_retention_seconds = 300
+  receive_wait_time_seconds = 0
+  tags                      = merge(var.standard_tags, var.tags)
+  kms_master_key_id         = aws_kms_key.config-notifications-key.id
+  kms_data_key_reuse_period_seconds = 3600
+}
+
+resource "aws_sqs_queue_policy" "config-notifications-can-publish" {
+  policy    = data.aws_iam_policy_document.config-notifications-sns-topic-can-publish.json
+  queue_url = aws_sqs_queue.config-notifications.id
+}
+
+resource "aws_sns_topic_subscription" "config-notifications-to-queue" {
+  topic_arn = aws_sns_topic.config-notifications.arn
+  protocol  = "sqs"
+  endpoint  = aws_sqs_queue.config-notifications.arn
+}
+
+resource "aws_kms_key" "config-notifications-key" {
+  description             = "Encryption of SNS and SQS queue for config change notifications"
+  policy                  = data.aws_iam_policy_document.config-notifications-kms-policy.json
+}
+
+data "aws_iam_policy_document" "config-notifications-kms-policy" {
+  statement {
+    sid = "AllowServices"
+    effect = "Allow"
+    principals {
+      identifiers = ["config.amazonaws.com", "sns.amazonaws.com", "sqs.amazonaws.com"]
+      type = "Service"
+    }
+    actions = [
+      "kms:GenerateDataKey",
+      "kms:Decrypt"
+    ]
+    resources = [ "*" ]
+  }
+  statement {
+    sid = "AllowOtherAccounts"
+    effect = "Allow"
+    principals {
+      type = "AWS"
+      identifiers = [ for a in var.responsible_accounts[var.environment]: "arn:${var.aws_partition}:iam::${a}:root" ]
+    }
+    actions = [
+      "kms:GenerateDataKey",
+      "kms:Encrypt"
+    ]
+    resources = [ "*" ]
+  }
+  # allow account to modify/manage key
+  statement {
+    sid = "AllowThisAccount"
+    effect = "Allow"
+    principals {
+      identifiers = ["arn:${var.aws_partition}:iam::${var.aws_account_id}:root"]
+      type = "AWS"
+    }
+    actions = [
+      "kms:*"
+    ]
+    resources = ["*"]
+  }
+}
 
-# This is for a service-linked role, but from https://docs.aws.amazon.com/config/latest/developerguide/sns-topic-policy.html:
-#   "AWS Config does not recommend using a service-linked role when using Amazon SNS topic from other accounts."
-#  statement {
-#    sid = "AllowConfigServer"
-#    effect = "Allow",
-#    principals {
-#      type = "AWS"
-#      resources = [ ]
-#    }
-#    actions = [ "SNS:Publish", ]
-#    resources = [ aws_sns_topic.account-alerts.arn ]
-#  }
+resource "aws_kms_alias" "config-notifications-key-alias" {
+  name          = "alias/config-notifications-key"
+  target_key_id = aws_kms_key.config-notifications-key.key_id
 }

+ 309 - 0
base/account_standards_c2/elb_bucket.tf

@@ -0,0 +1,309 @@
+# The centralized bucket for ELB Logging
+module "elb_logging_logging_bucket" {
+  source = "../../thirdparty/terraform-aws-s3logging-bucket"
+
+  bucket_name = "xdr-elb-${var.environment}-access-logs"
+  lifecycle_rules = list(
+    {
+      id                            = "expire-old-logs"
+      enabled                       = true
+      prefix                        = ""
+      expiration                    = 30
+      noncurrent_version_expiration = 30
+      abort_incomplete_multipart_upload_days = 7
+  })
+  tags = merge(var.standard_tags, var.tags)
+  versioning_enabled = true
+}
+
+resource "aws_s3_bucket" "elb_logging_bucket" {
+  bucket = "xdr-elb-${var.environment}"
+  acl    = "private"
+  tags   = merge(var.standard_tags, var.tags)
+
+  versioning {
+    enabled = true
+  }
+
+  logging {
+    target_bucket = module.elb_logging_logging_bucket.s3_bucket_name
+    target_prefix = "${var.aws_account_id}-${var.aws_region}-elblogs/"
+  }
+
+  server_side_encryption_configuration {
+    rule {
+      apply_server_side_encryption_by_default {
+        sse_algorithm = "aws:kms"
+        kms_master_key_id = aws_kms_key.elb_encryption.arn
+      }
+    }
+  }
+}
+
+resource "aws_s3_bucket_public_access_block" "aws_elb_bucket_block_public_access" {
+  block_public_acls       = true
+  block_public_policy     = true
+  bucket                  = aws_s3_bucket.elb_logging_bucket.id
+  ignore_public_acls      = true
+  restrict_public_buckets = true
+}
+
+data "aws_iam_policy_document" "aws_elb_bucket_policy" {
+  statement {
+    effect  = "Allow"
+    actions = ["s3:PutObject"]
+
+    principals {
+      type        = "AWS"
+      identifiers = [ for a in var.responsible_accounts[var.environment]: "arn:${var.aws_partition}:iam::${a}:root" ]
+    }
+
+    resources = ["arn:${var.aws_partition}:s3:::${aws_s3_bucket.elb_logging_bucket.bucket}/*"]
+  }
+
+  statement {
+    effect = "Allow"
+    actions = [ "s3:PutObject" ]
+    principals {
+      type = "Service"
+      identifiers = [ "delivery.logs.amazonaws.com" ]
+    }
+    resources = [ "arn:${var.aws_partition}:s3:::${aws_s3_bucket.elb_logging_bucket.bucket}/*" ]
+    condition {
+      test = "StringEquals"
+      variable = "s3:x-amz-acl"
+      values = [ "bucket-owner-full-control" ]
+    }
+  }
+
+  statement {
+    effect = "Allow"
+    actions = [ "s3:GetBucketAcl" ]
+    principals {
+      type = "Service"
+      identifiers = [ "delivery.logs.amazonaws.com" ]
+    }
+    resources = [ "arn:${var.aws_partition}:s3:::${aws_s3_bucket.elb_logging_bucket.bucket}" ]
+  }
+}
+
+resource "aws_s3_bucket_policy" "aws_elb_bucket_policy" {
+  bucket = aws_s3_bucket.elb_logging_bucket.id
+  policy = data.aws_iam_policy_document.aws_elb_bucket_policy.json
+
+  # Ordering bug, see https://github.com/terraform-providers/terraform-provider-aws/issues/7628
+  depends_on = [ aws_s3_bucket_public_access_block.aws_elb_bucket_block_public_access ]
+}
+
+resource "aws_kms_key" "elb_encryption" {
+  description             = "This key is used to encrypt ELB Logs"
+  deletion_window_in_days = 30
+  policy = data.aws_iam_policy_document.elb_encryption_key_policy.json
+  enable_key_rotation = true
+  tags = merge(var.standard_tags, var.tags)
+}
+
+resource "aws_kms_alias" "elb_encryption" {
+  name          = "alias/aws_elb_logs"
+  target_key_id = aws_kms_key.elb_encryption.key_id
+}
+
+data "aws_iam_policy_document" "elb_encryption_key_policy" {
+  statement {
+    actions   = ["kms:*"]
+    effect    = "Allow"
+    resources = ["*"]
+
+    principals {
+      type        = "AWS"
+      identifiers = ["arn:${var.aws_partition}:iam::${var.aws_account_id}:root"]
+    }
+  }
+
+  statement {
+    actions = [
+      "kms:Encrypt*",
+      "kms:GenerateDataKey*",
+    ]
+    effect    = "Allow"
+    resources = ["*"]
+
+    principals {
+      type        = "AWS"
+      identifiers = [ for a in var.responsible_accounts[var.environment]: "arn:${var.aws_partition}:iam::${a}:root" ]
+    }
+  }
+
+  statement {
+    actions = [
+      "kms:Encrypt*",
+      "kms:Decrypt*",
+      "kms:ReEncrypt*",
+      "kms:GenerateDataKey*",
+      "kms:Describe*",
+    ]
+    effect    = "Allow"
+    resources = ["*"]
+
+    principals {
+      type        = "Service"
+      identifiers = [ "delivery.logs.amazonaws.com"]
+    }
+  }
+
+  statement {
+    actions   = ["kms:Describe*"]
+    effect    = "Allow"
+    resources = ["*"]
+
+    principals {
+      type        = "Service"
+      identifiers = [ "delivery.logs.amazonaws.com" ]
+    }
+  }
+}
+
+#### SQS Queue for Splunk
+resource "aws_s3_bucket_notification" "on_new_elb_log" {
+  bucket = aws_s3_bucket.elb_logging_bucket.bucket
+
+  topic {
+    topic_arn = aws_sns_topic.new_elb_log_event.arn
+
+    events = [
+      "s3:ObjectCreated:*",
+    ]
+
+    filter_suffix = ""
+  }
+}
+
+resource "aws_sns_topic" "new_elb_log_event" {
+  name = "s3-notification-topic-${aws_s3_bucket.elb_logging_bucket.bucket}"
+  kms_master_key_id = aws_kms_key.new_object_key.id
+}
+
+resource "aws_sns_topic_policy" "elb_log" {
+  arn    = aws_sns_topic.new_elb_log_event.arn
+  policy = data.aws_iam_policy_document.elblog_bucket_can_publish.json
+}
+
+data "aws_iam_policy_document" "elblog_bucket_can_publish" {
+  statement {
+    actions = [
+      "SNS:Publish",
+    ]
+
+    effect = "Allow"
+
+    condition {
+      test     = "ArnLike"
+      variable = "aws:SourceArn"
+
+      values = [
+        aws_s3_bucket.elb_logging_bucket.arn
+      ]
+    }
+
+    principals {
+      type        = "AWS"
+      identifiers = ["*"]
+    }
+
+    resources = [
+      aws_sns_topic.new_elb_log_event.arn
+    ]
+
+    sid = "allowpublish"
+  }
+
+  statement {
+    actions = [
+      "SNS:Subscribe",
+      "SNS:Receive",
+    ]
+
+    effect = "Allow"
+
+    principals {
+      type        = "AWS"
+      identifiers = ["*"]
+    }
+
+    condition {
+      test     = "ArnEquals"
+      values   = [ aws_sqs_queue.new_elblog.arn ]
+      variable = "aws:SourceArn"
+    }
+
+    resources = [
+      aws_sns_topic.new_elb_log_event.arn
+    ]
+
+    sid = "sid_allow_subscribe"
+  }
+}
+
+resource "aws_sqs_queue" "new_elblog" {
+  name                       = "new-objects-for-${aws_s3_bucket.elb_logging_bucket.bucket}"
+  visibility_timeout_seconds = 300 # wait 5 minutes before allowing a different splunk instance to process the same message
+  message_retention_seconds  = 604800 # Keep a message in the queue for 7 days
+  receive_wait_time_seconds  = 0 # how long to wait for a message before returning
+  redrive_policy             = "{\"deadLetterTargetArn\":\"${aws_sqs_queue.elblog-dlg.arn}\",\"maxReceiveCount\":4}"
+  tags                       = merge(var.standard_tags, var.tags)
+  kms_master_key_id = aws_kms_key.new_object_key.id
+  kms_data_key_reuse_period_seconds = 3600
+}
+
+data "aws_iam_policy_document" "sns_topic_elblog_can_publish" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      identifiers = [
+        "*",
+      ]
+
+      type = "AWS"
+    }
+
+    actions = [
+      "SQS:SendMessage",
+    ]
+
+    resources = [
+      aws_sqs_queue.new_elblog.arn
+    ]
+
+    condition {
+      test = "ArnEquals"
+
+      values = [
+        aws_sns_topic.new_elb_log_event.arn
+      ]
+
+      variable = "aws:SourceArn"
+    }
+  }
+}
+
+// Dead Letter queue, use same parameters as main queue
+resource "aws_sqs_queue" "elblog-dlg" {
+  name                      = "new-objects-for-${aws_s3_bucket.elb_logging_bucket.bucket}-dlq"
+  message_retention_seconds = 300
+  receive_wait_time_seconds = 0
+  tags                      = merge(var.standard_tags, var.tags)
+  kms_master_key_id = aws_kms_key.new_object_key.id
+  kms_data_key_reuse_period_seconds = 3600
+}
+
+resource "aws_sqs_queue_policy" "elblog_bucket_can_publish" {
+  policy    = data.aws_iam_policy_document.sns_topic_elblog_can_publish.json
+  queue_url = aws_sqs_queue.new_elblog.id
+}
+
+resource "aws_sns_topic_subscription" "elblog_bucket_change_notification_to_queue" {
+  topic_arn = aws_sns_topic.new_elb_log_event.arn
+  protocol  = "sqs"
+  endpoint  = aws_sqs_queue.new_elblog.arn
+}

+ 10 - 2
base/account_standards_c2/outputs.tf

@@ -1,7 +1,15 @@
-output "s3_logging_bucket" {
-  value = module.s3_logging_bucket
+output "account_alerts_sns" {
+  value = aws_sns_topic.account-alerts
 }
 
 output "cloudtrail_logging_bucket" {
   value = module.cloudtrail_logging_bucket
 }
+
+output "config_logging_bucket" {
+  value = aws_s3_bucket.xdr_config_bucket
+}
+
+output "elb_logging_bucket" {
+  value = aws_s3_bucket.elb_logging_bucket
+}