Skip to content

feat: Lacework CloudTrail should send logs to CloudWatchΒ #135

@TheShahin

Description

@TheShahin

Feature Request

Describe the Feature Request
The CloudTrail created by this Terraform module should support setting up a proper logging integration with CloudWatch.

Is your feature request related to a problem? Please describe
The created CloudTrail is non-compliant with CIS Benchmarks and is listed as a Medium severity in Lacework's generated reports for compliance with AWS ISO 27001:2013 and AWS ISO/IEC 27002:2022.

The non-compliance in question is lacework-global-55.

Describe Preferred Solution
The module creates resources that by default are compliant with CIS Benchmarks.

Add input variables cloudwatch_logs_encryption_enabled, cloudwatch_logs_encryption_key_arn, and cloudwatch_logs_iam_role_arn, and set them in the aws_cloudtrail resource. If no IAM role ARN is provided then one should be created by the module.

Additional Context
I think the changes needed are the following:

variables.tf:

variable "cloudwatch_logs_encryption_enabled" {
  type    = bool
  default = true
}

variable "cloudwatch_logs_encryption_key_arn" {
  type    = string
  default = ""
}

variable "cloudwatch_logs_iam_role_arn" {
  type    = string
  default = ""
}

main.tf:

locals {
  ...
  create_cloudwatch_iam_role = var.cloudwatch_logs_encryption_enabled && var.cloudwatch_logs_iam_role_arn == null
  
  cloudwatch_key_arn = var.cloudwatch_logs_encryption_enabled ? (length(var.cloudwatch_logs_encryption_key_arn) > 0 ? var.cloudwatch_logs_encryption_key_arn : aws_kms_key.lacework_kms_key[0].arn) : ""

  cloudwatch_logstream_arn = "${aws_cloudwatch_log_group.cloudtrail_log_group.arn}:log-stream:${data.aws_caller_identity.current.account_id}_CloudTrail_${data.aws_region.current.name}*" # Reference: https://docs.aws.amazon.com/awscloudtrail/latest/userguide/send-cloudtrail-events-to-cloudwatch-logs.html
}

data "aws_iam_policy_document" "kms_key_policy" {
  ...

  dynamic "statement" {
    for_each = (var.cloudwatch_logs_encryption_enabled && length(var.cloudwatch_logs_encryption_key_arn) == 0) ? [1] : []
    content {
      sid    = "Allow CloudWatch service to encrypt/decrypt"
      effect = "Allow"

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

      resources = ["*"]
      
      principals {
        type = "Service"
        identifiers = [
          "logs.${data.aws_region.current.name}.amazonaws.com",
        ]
      }

      condition {
        test     = "ArnEquals"
        variable = "kms:EncryptionContext:aws:logs:arn"
        values = [
          "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:${var.cloudtrail_name}",
        ]
      }
    }
  }
}

resource "aws_cloudwatch_log_group" "cloudtrail_log_group" {
  name              = var.cloudtrail_name
  kms_key_id        = local.cloudwatch_key_arn
  retention_in_days = 90
}

data "aws_iam_policy_document" "cloudtrail_assume_role" {
  count = local.create_cloudwatch_iam_role ? 1 : 0
  
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole",
    ]

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


data "aws_iam_policy_document" "cloudtrail_logging" {
  count = local.create_cloudwatch_iam_role ? 1 : 0

  statement {
    sid    = "AWSCloudTrailCreateLogStream"
    effect = "Allow"

    actions = [
      "logs:CreateLogStream",
    ]

    resources = [
      local.cloudwatch_logstream_resource,
    ]
  }

  statement {
    sid    = "AWSCloudTrailPutLogEvents"
    effect = "Allow"

    actions = [
      "logs:PutLogEvents",
    ]

    resources = [
      local.cloudwatch_logstream_resource,
    ]
  }
}

resource "aws_iam_policy" "cloudtrail_logging" {
  count = local.create_cloudwatch_iam_role ? 1 : 0
  
  name        = var.cloudtrail_name
  policy      = data.aws_iam_policy_document.cloudtrail_logging[count.index].json
  description = "Allows CloudTrail to create log streams and to put logs in CloudWatch"
}

resource "aws_iam_role" "cloudtrail_logging" {
  count = local.create_cloudwatch_iam_role ? 1 : 0

  name               = var.cloudtrail_name
  assume_role_policy = data.aws_iam_policy_document.cloudtrail_assume_role[count.index].json
}

resource "aws_iam_role_policy_attachment" "cloudtrail_logging" {
  count = local.create_cloudwatch_iam_role ? 1 : 0

  role       = aws_iam_role.cloudtrail_logging[count.index].name
  policy_arn = aws_iam_policy.cloudtrail_logging[count.index].arn
}

resource "aws_cloudtrail" "lacework_cloudtrail" {
  ...
  enable_logging             = true
  cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.cloudtrail_log_group.arn}:*"
  cloud_watch_logs_role_arn  = local.create_cloudwatch_iam_role ? coalesce(var.cloudwatch_logs_iam_role_arn, aws_iam_role.cloudtrail_logging.arn) : null
  enable_log_file_validation = var.enable_log_file_validation
  ...
}

Please note that this code has not been properly tested. I've simply adjusted Terraform configurations that I've found elsewhere.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions