module "default_tags" {
  source             = "git@github.com:theorchard/terraform-default-tags.git//?ref=2.0.0"
  environment        = var.environment
  application_family = var.application_family
  team_name          = var.team_name
  service_name       = var.service_name
}

# NB! terraform-internal-systems needs to be run before this

provider "aws" {
  region = "us-east-1"
  alias  = "us_east_1"

  default_tags {
    tags = module.default_tags.tags
  }
}

provider "aws" {
  region = "us-west-2"
  alias  = "us_west_2"

  default_tags {
    tags = module.default_tags.tags
  }
}

provider "aws" {
  region = "us-east-2"
  alias  = "us_east_2"

  default_tags {
    tags = module.default_tags.tags
  }
}

# Terraform backends cannot contain interpolations
terraform {
  backend "s3" {
    bucket  = "prod-songwhip-terraform-state"
    key     = "prod/gsirt/guardduty-logs/terraform.tfstate"
    region  = "us-east-1"
    encrypt = "true"
  }
}

data "aws_caller_identity" "current" {}

resource "aws_guardduty_detector" "guardduty_us_east_1" {
  provider = aws.us_east_1
  enable   = true
}

resource "aws_guardduty_detector" "guardduty_us_west_2" {
  provider = aws.us_west_2
  enable   = true
}

resource "aws_guardduty_detector" "guardduty_us_east_2" {
  provider = aws.us_east_2
  enable   = true
}

# S3 bucket for guardduty logs destination
module "guardduty_s3_bucket" {
  source = "git@github.com:theorchard/terraform-s3.git//modules/s3_bucket?ref=3.12.1"

  application_family      = var.application_family
  env                     = var.environment
  bucket_name             = "${var.product_name}-${var.service_name}"
  custom_logging_bucket   = "${var.environment}-${var.product_name}-s3-logs"
  bucket_policy_overrides = [data.aws_iam_policy_document.guardduty_bucket_policy_document.json]

  apply_server_side_encryption_by_default = {
    sse_algorithm = "AES256"
  }
}

# Custom policy for guardduty logs destination bucket
data "aws_iam_policy_document" "guardduty_bucket_policy_document" {
  statement {
    sid       = "Deny non-HTTPS access"
    effect    = "Deny"
    resources = ["${module.guardduty_s3_bucket.s3_bucket_arn_output}/*"]
    actions   = ["s3:*"]

    condition {
      test     = "Bool"
      variable = "aws:SecureTransport"
      values   = ["false"]
    }

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

  statement {
    sid       = "Deny incorrect encryption header"
    effect    = "Deny"
    resources = ["${module.guardduty_s3_bucket.s3_bucket_arn_output}/*"]
    actions   = ["s3:PutObject"]

    condition {
      test     = "StringNotEquals"
      variable = "s3:x-amz-server-side-encryption-aws-kms-key-id"
      values   = [aws_kms_key.guardduty_logs_kms_key.arn]
    }

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

  statement {
    sid       = "Deny unencrypted object uploads"
    effect    = "Deny"
    resources = ["${module.guardduty_s3_bucket.s3_bucket_arn_output}/*"]
    actions   = ["s3:PutObject"]

    condition {
      test     = "StringNotEquals"
      variable = "s3:x-amz-server-side-encryption"
      values   = ["aws:kms"]
    }

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

  statement {
    sid       = "Allow PutObject"
    effect    = "Allow"
    resources = ["${module.guardduty_s3_bucket.s3_bucket_arn_output}/*"]
    actions   = ["s3:PutObject"]

    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = [aws_guardduty_detector.guardduty_us_east_1.arn, aws_guardduty_detector.guardduty_us_west_2.arn, aws_guardduty_detector.guardduty_us_east_2.arn]
    }

    condition {
      test     = "StringEquals"
      variable = "aws:SourceAccount"
      values   = [data.aws_caller_identity.current.account_id]
    }

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

  statement {
    sid       = "Allow GetBucketLocation"
    effect    = "Allow"
    resources = ["arn:aws:s3:::${var.environment}-${var.product_name}-guardduty-logs"]
    actions   = ["s3:GetBucketLocation"]

    condition {
      test     = "StringEquals"
      variable = "aws:SourceArn"
      values   = [aws_guardduty_detector.guardduty_us_east_1.arn, aws_guardduty_detector.guardduty_us_west_2.arn, aws_guardduty_detector.guardduty_us_east_2.arn]
    }

    condition {
      test     = "StringEquals"
      variable = "aws:SourceAccount"
      values   = [data.aws_caller_identity.current.account_id]
    }

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

data "aws_iam_policy_document" "kms_data_policy" {
  # checkov:skip=CKV_AWS_109:It is a KMS key policy, so it applies only to a specific key.
  # checkov:skip=CKV_AWS_111:The same as above.
  statement {
    sid       = "Allow GuardDuty to encrypt findings"
    effect    = "Allow"
    resources = ["*"]
    actions   = ["kms:GenerateDataKey"]

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

  statement {
    sid       = "Enable IAM User Permissions"
    effect    = "Allow"
    resources = ["*"]
    actions   = ["kms:*"]

    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
    }
  }

  statement {
    sid       = "Allow use of the key"
    effect    = "Allow"
    resources = ["*"]

    actions = [
      "kms:Encrypt",
      "kms:Decrypt",
      "kms:ReEncrypt*",
      "kms:GenerateDataKey*",
      "kms:DescribeKey",
    ]

    principals {
      type = "AWS"

      identifiers = [
        "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty",
        "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/GSIRT_AWS_Monitoring_Role",
      ]
    }
  }

  statement {
    sid       = "Allow attachment of persistent resources"
    effect    = "Allow"
    resources = ["*"]

    actions = [
      "kms:CreateGrant",
      "kms:ListGrants",
      "kms:RevokeGrant",
    ]

    condition {
      test     = "Bool"
      variable = "kms:GrantIsForAWSResource"
      values   = ["true"]
    }

    principals {
      type = "AWS"

      identifiers = [
        "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty",
        "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/GSIRT_AWS_Monitoring_Role",
      ]
    }
  }

  statement {
    sid    = "Allow sns and s3 to use kms key"
    effect = "Allow"
    principals {
      type        = "Service"
      identifiers = ["s3.amazonaws.com", "sns.amazonaws.com"]
    }
    actions   = ["kms:GenerateDataKey", "kms:Decrypt"]
    resources = ["arn:aws:kms:${var.aws_region}:${data.aws_caller_identity.current.account_id}:key/${var.kms_key_id}"]
  }
}

resource "aws_kms_key" "guardduty_logs_kms_key" {
  description             = "${var.environment}-${var.service_name}-kms-key"
  policy                  = data.aws_iam_policy_document.kms_data_policy.json
  enable_key_rotation     = true
  deletion_window_in_days = 30

  tags = {
    environment        = var.environment
    service_name       = var.service_name
    application_family = var.application_family
    terraformed        = "true"
  }
}

resource "aws_kms_alias" "guardduty_logs_kms_key_alias" {
  name          = "alias/${var.environment}-${var.service_name}-data-key"
  target_key_id = aws_kms_key.guardduty_logs_kms_key.key_id
}

resource "aws_guardduty_publishing_destination" "s3_destination" {
  detector_id     = aws_guardduty_detector.guardduty_us_east_1.id
  destination_arn = module.guardduty_s3_bucket.s3_bucket_arn_output
  kms_key_arn     = aws_kms_key.guardduty_logs_kms_key.arn
}

resource "aws_guardduty_publishing_destination" "s3_destination_us_west_2" {
  provider        = aws.us_west_2
  detector_id     = aws_guardduty_detector.guardduty_us_west_2.id
  destination_arn = module.guardduty_s3_bucket.s3_bucket_arn_output
  kms_key_arn     = aws_kms_key.guardduty_logs_kms_key.arn
}

resource "aws_guardduty_publishing_destination" "s3_destination_us_east_2" {
  provider        = aws.us_east_2
  detector_id     = aws_guardduty_detector.guardduty_us_east_2.id
  destination_arn = module.guardduty_s3_bucket.s3_bucket_arn_output
  kms_key_arn     = aws_kms_key.guardduty_logs_kms_key.arn
}

module "guardduty_logs_sqs_queue_with_deadletter" {
  source = "git@github.com:theorchard/terraform-sqs.git?ref=2.5.0"

  environment                          = var.environment
  application_family                   = var.application_family
  queue_name                           = "${var.product_name}-${var.service_name}"
  sqs_kms_master_key_id                = aws_kms_alias.guardduty_logs_kms_key_alias.name
  sqs_deadletter_enabled               = true
  deadletter_max_receive_count         = 200
  deadletter_delay_seconds             = 10
  deadletter_max_message_size          = 2048
  deadletter_message_retention_seconds = 86400
  deadletter_receive_wait_time_seconds = 10
}

resource "aws_sns_topic" "guardduty_logs_sns_topic" {
  name                          = "${var.environment}-${var.product_name}-guardduty-logs"
  kms_master_key_id             = aws_kms_key.guardduty_logs_kms_key.key_id
  sqs_failure_feedback_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/SNSFailureFeedback"
  sqs_success_feedback_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/SNSSuccessFeedback"
}

data "aws_iam_policy_document" "guardduty_logs_sns_topic_policy" {

  statement {
    actions = [
      "sns:Publish",
    ]

    condition {
      test     = "ArnLike"
      variable = "aws:SourceArn"

      values = [
        module.guardduty_s3_bucket.s3_bucket_arn_output
      ]
    }

    effect = "Allow"

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

    resources = [
      aws_sns_topic.guardduty_logs_sns_topic.arn,
    ]
  }
}

resource "aws_sns_topic_policy" "guardduty_logs_sns_topic_policy" {
  arn    = aws_sns_topic.guardduty_logs_sns_topic.arn
  policy = data.aws_iam_policy_document.guardduty_logs_sns_topic_policy.json
}

resource "aws_sns_topic_subscription" "guardduty_logs_sns_sqs_target" {
  topic_arn              = aws_sns_topic.guardduty_logs_sns_topic.arn
  protocol               = "sqs"
  endpoint               = module.guardduty_logs_sqs_queue_with_deadletter.queue_arn
  endpoint_auto_confirms = true
}

# Policy document to allow SNS to send message to SQS
data "aws_iam_policy_document" "guardduty_logs_sqs_queue_policy_document" {
  statement {
    effect = "Allow"

    actions = [
      "sqs:SendMessage",
    ]

    principals {
      type        = "AWS"
      identifiers = ["*"]
    }

    resources = [
      module.guardduty_logs_sqs_queue_with_deadletter.queue_arn,
    ]

    condition {
      test     = "ArnLike"
      variable = "aws:SourceArn"

      values = [
        aws_sns_topic.guardduty_logs_sns_topic.arn
      ]
    }
  }
}

# Policy to allow SNS to send message to SQS
resource "aws_sqs_queue_policy" "guardduty_logs_sqs_queue_policy" {
  queue_url = module.guardduty_logs_sqs_queue_with_deadletter.queue_url
  policy    = data.aws_iam_policy_document.guardduty_logs_sqs_queue_policy_document.json
}

data "aws_iam_policy_document" "guardduty_logs_policy_document" {
  statement {
    effect = "Allow"

    actions = [
      "s3:GetObject",
      "s3:GetObjectVersion",
      "s3:ListBucket",
    ]

    resources = [
      module.guardduty_s3_bucket.s3_bucket_arn_output,
      "${module.guardduty_s3_bucket.s3_bucket_arn_output}/*"
    ]
  }

  statement {
    effect = "Allow"

    actions = [
      "sqs:ChangeMessageVisibility",
      "sqs:DeleteMessage",
      "sqs:GetQueueUrl",
      "sqs:GetQueueAttributes",
      "sqs:ListDeadLetterSourceQueues",
      "sqs:ListQueueTags",
      "sqs:ReceiveMessage",
    ]

    resources = [
      module.guardduty_logs_sqs_queue_with_deadletter.queue_arn,
    ]
  }

  statement {
    effect = "Allow"

    actions = [
      "sqs:ListQueues",
    ]

    resources = [
      "*"
    ]
  }

  statement {
    sid       = ""
    effect    = "Allow"
    resources = [aws_kms_key.guardduty_logs_kms_key.arn]

    actions = [
      "kms:GenerateDataKey",
      "kms:Decrypt",
    ]
  }
}

resource "aws_iam_policy" "guardduty_logs_policy" {
  name   = "${var.environment}-guardduty-logs-access-policy"
  policy = data.aws_iam_policy_document.guardduty_logs_policy_document.json
}

resource "aws_s3_bucket_notification" "guardduty_log_bucket_notification" {
  bucket = module.guardduty_s3_bucket.s3_bucket_name_output

  topic {
    topic_arn = aws_sns_topic.guardduty_logs_sns_topic.arn
    events    = ["s3:ObjectCreated:*"]
  }
}

# Role used by GSIRT
data "aws_iam_role" "gsirt_aws_monitoring_role" {
  name = "GSIRT_AWS_Monitoring_Role"
}

# Attach guardduty-logs-access-policy to GSIRT_AWS_Monitoring_Role
resource "aws_iam_role_policy_attachment" "gsirt_guardduty_logs_access_policy_attachement" {
  role       = data.aws_iam_role.gsirt_aws_monitoring_role.name
  policy_arn = aws_iam_policy.guardduty_logs_policy.arn
}
