diff --git a/cloudtrail/README.md b/cloudtrail/README.md index a5f32ab..37b4092 100644 --- a/cloudtrail/README.md +++ b/cloudtrail/README.md @@ -58,21 +58,24 @@ No modules. | Name | Type | |------|------| | [aws_cloudtrail.cloudtrail](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudtrail) | resource | -| [aws_cloudtrail.trail](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudtrail) | resource | +| [aws_cloudtrail.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudtrail) | resource | | [aws_cloudwatch_log_group.inf-cloudtrail](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_iam_policy.cloudtrail_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.cloudtrail](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_kms_key.cloudtrail_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | -| [aws_s3_bucket.trail](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | +| [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket_policy.policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource | | [aws_s3_bucket_public_access_block.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | | [null_resource.policy_delay](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | | [aws_arn.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/arn) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.cloudtrail_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.cloudtrail_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.cloudtrail_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.cloudwatch_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_kms_key.incoming_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/kms_key) | data source | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | ## Inputs @@ -87,6 +90,7 @@ No modules. | [component\_tags](#input\_component\_tags) | Additional tags for Components (s3, kms, ddb) | `map(map(string))` |
{
"ddb": {},
"kms": {},
"s3": {}
}
| no | | [enable\_sns](#input\_enable\_sns) | Flag to enable or disable the creation of SNS for Cloudtrail (TBD) | `bool` | `false` | no | | [enable\_sqs](#input\_enable\_sqs) | Flag to enable or disable the creation of SQS attached to SNS for Cloudtrail, used for Splunk ingestion (TBD) | `bool` | `false` | no | +| [kms\_key\_arn](#input\_kms\_key\_arn) | AWS CloudTrail KMS ARN to be used for encrypting the ClouldTrail, S3 Bucket, and SQS | `string` | n/a | yes | | [kms\_key\_management\_identifiers](#input\_kms\_key\_management\_identifiers) | AWS IAM ARNs (roles, groups, users) for full access to the created KMS Key for this bucket | `list(string)` | `[]` | no | | [name](#input\_name) | Name to apply to Cloudtrail, S3, SNS and SQS | `string` | `null` | no | | [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component (efs, s3, ebs, kms, role, policy, security-group). This should be used primarily for common infrastructure things | `map(string)` | `{}` | no | diff --git a/cloudtrail/cloudtrail.tf b/cloudtrail/cloudtrail.tf new file mode 100644 index 0000000..2661a1c --- /dev/null +++ b/cloudtrail/cloudtrail.tf @@ -0,0 +1,20 @@ +resource "aws_cloudtrail" "this" { + name = local.name + s3_bucket_name = aws_s3_bucket.this.id + s3_key_prefix = var.cloudtrail_bucket_prefix + include_global_service_events = true + is_multi_region_trail = false + enable_log_file_validation = true + enable_logging = true + kms_key_id = var.kms_key_arn + # sns_topic_name = aws_sns_topic.cloudtrail.arn + # cloud_watch_logs_group_arn = aws_cloudwatch_log_group.inf-cloudtrail.arn + # cloud_watch_logs_role_arn = aws_iam_role.inf-cloudtrail.arn + + tags = merge( + local.base_tags, + var.tags, + { "Name" = local.name }, + ) + depends_on = [aws_s3_bucket_policy.bucket_policy] +} diff --git a/cloudtrail/cloudwatch.tf b/cloudtrail/cloudwatch.tf new file mode 100644 index 0000000..f885d17 --- /dev/null +++ b/cloudtrail/cloudwatch.tf @@ -0,0 +1,29 @@ +data "aws_iam_policy_document" "cloudwatch_policy" { + statement { + sid = "AWSCloudTrailCreateLogStream" + effect = "Allow" + actions = ["logs:CreateLogStream"] + resources = [local.cloudwatch_resources] + } + + statement { + sid = "AWSCloudTrailPutLogEvents" + effect = "Allow" + actions = ["logs:PutLogEvents"] + resources = [local.cloudwatch_resources] + } +} + +resource "aws_cloudwatch_log_group" "this" { + name = local.name + + # kms_key_id = var.kms_key_id + # kms_key_id = var.kms_key_arn + kms_key_id = data.aws_kms_key.incoming_key.id + retention_in_days = 7 + + tags = merge( + local.common_tags, + map("Name", local.name), + ) +} diff --git a/cloudtrail/kms.tf.off b/cloudtrail/kms.tf.off new file mode 100644 index 0000000..63f49fa --- /dev/null +++ b/cloudtrail/kms.tf.off @@ -0,0 +1,166 @@ +#--- +# kms key +#--- +resource "aws_kms_key" "key" { + description = "Encrypt CloudTrail objects and streams" + enable_key_rotation = true + policy = data.aws_iam_policy_document.key.json + + tags = merge( + local.common_tags, + map("boc:aws:region", local.region), + map("Name", var.kms_key), + ) + lifecycle { + ignore_changes = [tags["boc:tf_module_version"]] + } +} + +resource "aws_kms_alias" "key" { + name = "alias/${var.kms_key}" + target_key_id = aws_kms_key.key.key_id +} + +output "kms_key_id" { + description = "Cloudtrail Key ID" + value = aws_kms_key.key.id +} +output "kms_key_arn" { + description = "Cloudtrail Key ARN" + value = aws_kms_key.key.arn +} + +data "aws_iam_policy_document" "key" { + policy_id = "inf-cloudtrail KMS access" + statement { + sid = "EnableIAMUserPermissions" + effect = "Allow" + actions = ["kms:*"] + resources = ["*"] + principals { + type = "AWS" + + identifiers = [ + # data.aws_caller_identity.current.arn, + "arn:${data.aws_arn.current.partition}:iam::${var.account_id}:root", + # "arn:${data.aws_arn.current.partition}:sts::${var.account_id}:assumed-role/r-inf-cloud-admin/${var.tag_creator}", + ] + } + } + + statement { + sid = "AllowCloudTrailEncryptLogs" + effect = "Allow" + actions = ["kms:GenerateDataKey*"] + resources = ["*"] + + principals { + type = "Service" + # identifiers = ["cloudtrail.amazonaws.com"] + identifiers = ["cloudtrail.amazonaws.com", "logs.amazonaws.com", "logs.${local.region}.amazonaws.com"] + } + + condition { + test = "StringLike" + variable = "kms:EncryptionContext:aws:cloudtrail:arn" + values = ["arn:${data.aws_arn.current.partition}:cloudtrail:*:${var.account_id}:trail/*"] + } + } + + statement { + sid = "AllowCloudTrailKeyActivities" + effect = "Allow" + actions = [ + "kms:Describe*", + "log:AssociateKmsKey", + "log:DisassociateKmsKey" + ] + resources = ["*"] + + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com", "logs.amazonaws.com", "logs.${local.region}.amazonaws.com"] + } + } + + statement { + sid = "AllowPrincipalsDecryptLogFiles" + effect = "Allow" + + principals { + type = "AWS" + identifiers = ["*"] + } + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncryptFrom" + ] + resources = ["*"] + + condition { + test = "StringEquals" + variable = "kms:CallerAccount" + values = [var.account_id] + } + + condition { + test = "StringLike" + variable = "kms:EncryptionContext:aws:cloudtrail:arn" + values = ["arn:${data.aws_arn.current.partition}:cloudtrail:*:${var.account_id}:trail/*"] + } + } + + statement { + sid = "EnableCrossAccountDecryptLogFiles" + effect = "Allow" + + principals { + type = "AWS" + identifiers = ["*"] + } + + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncryptFrom" + ] + resources = ["*"] + + condition { + test = "StringEquals" + variable = "kms:CallerAccount" + values = [var.account_id] + } + + condition { + test = "StringLike" + variable = "kms:EncryptionContext:aws:cloudtrail:arn" + values = ["arn:${data.aws_arn.current.partition}:cloudtrail:*:${var.account_id}:trail/*"] + } + } + + statement { + sid = "AllowAliasCreationDuringSetup" + effect = "Allow" + actions = ["kms:CreateAlias"] + resources = ["*"] + + principals { + type = "AWS" + identifiers = ["*"] + } + + condition { + test = "StringEquals" + variable = "kms:CallerAccount" + values = [var.account_id] + } + + condition { + test = "StringEquals" + variable = "kms:ViaService" + values = ["ec2.${local.region}.amazonaws.com}"] + } + } +} diff --git a/cloudtrail/main.tf b/cloudtrail/main.tf index 1157b2a..e6df390 100644 --- a/cloudtrail/main.tf +++ b/cloudtrail/main.tf @@ -55,120 +55,14 @@ locals { partition = data.aws_arn.current.partition name = var.name == null ? format("%v-%v", lookup(local._defaults["cloudtrail"], "name"), local.region) : var.name - kms_key_arn = var.kms_key_arn kms_key_name = format("k-%v", local.name) kms_admin_root = format("arn:%v:iam::%v:root", local.partition, local.account_id) - kms_admin_roles = compact(concat([local.kms_admin_root], var.kms_admin_roles)) + kms_admin_roles = compact(concat([var.kms_admin_root], var.kms_admin_roles)) kms_policy_document = var.kms_policy_document != null ? var.kms_policy_document : data.aws_iam_policy_document.empty.json bucket_name = var.name == null ? format("%v-%v-%v", lookup(local._defaults["cloudtrail"], "name"), local.account_id, local.region) : var.name } - -resource "aws_cloudtrail" "trail" { - name = local.name - s3_bucket_name = aws_s3_bucket.trail.id - s3_key_prefix = var.cloudtrail_bucket_prefix - include_global_service_events = rue - is_multi_region_trail = false - kms_key_id = local.kms_key_arn - enable_log_file_validation = true - - tags = merge( - local.base_tags, - var.tags, - { "Name" = local.name }, - ) - depends_on = [aws_s3_bucket_policy.policy] -} - - -resource "aws_s3_bucket" "trail" { - bucket = local.bucket_name - acl = "private" - force_destroy = false - - logging { - target_bucket = var.access_log_bucket - target_prefix = format("%s/%s/", var.access_log_bucket_prefix, local.name) - } - - tags = merge( - local.base_tags, - var.tags, - { "Name" = local.name }, - ) - - server_side_encryption_configuration { - rule { - apply_server_side_encryption_by_default { - kms_master_key_id = local.kms_key_arn - sse_algorithm = "aws:kms" - } - } - } -} - -#--- -# bucket policy (apply also encryption key usage here?) -# deny unencrypted uploads policy statement removed for default encryption -#--- -data "aws_iam_policy_document" "policy" { - statement { - sid = "AWSCloudTrailAclCheck" - effect = "Allow" - actions = ["s3:GetBucketAcl"] - principals { - type = "Service" - identifiers = ["cloudtrail.amazonaws.com"] - } - resources = [aws_s3_bucket.this.arn] - } - statement { - sid = "AWSCloudTrailWrite" - effect = "Allow" - actions = ["s3:PutObject"] - principals { - type = "Service" - identifiers = ["cloudtrail.amazonaws.com"] - } - resources = ["${aws_s3_bucket.this.arn}/${var.cloudtrail_bucket_prefix}/*"] - condition { - test = "StringEquals" - variable = "s3:x-amz-acl" - values = ["bucket-owner-full-control"] - } - } -} - -#--- -# apply policy to bucket and public access block policy to bucket -#--- -resource "aws_s3_bucket_policy" "policy" { - bucket = aws_s3_bucket.this.bucket - policy = data.aws_iam_policy_document.policy.json - depends_on = [null_resource.policy_delay] -} - -resource "aws_s3_bucket_public_access_block" "this" { - bucket = aws_s3_bucket.this.id - block_public_acls = true - block_public_policy = true - ignore_public_acls = true - restrict_public_buckets = true -} - -#--- -# 180s delay needed for bucket to create and policy to apply, before -# creating a cloudtrail to point to it -#--- -resource "null_resource" "policy_delay" { - triggers = { - bucket = aws_s3_bucket.this.id - # policy = aws_s3_bucket_policy.policy.id - } - provisioner "local-exec" { - when = create - command = "sleep 180" - } +data "aws_kms_key" "incoming_key" { + key_id = var.kms_key_arn } diff --git a/cloudtrail/s3-bucket-policy.tf.off b/cloudtrail/s3-bucket-policy.tf.off new file mode 100644 index 0000000..e092555 --- /dev/null +++ b/cloudtrail/s3-bucket-policy.tf.off @@ -0,0 +1,31 @@ +data "aws_iam_policy_document" "cloudtrail_s3" { + statement { + sid = "AWSCloudTrailWrite" + effect = "Allow" + resources = ["${aws_s3_bucket.cloudtrail.arn}/*"] + actions = ["s3:PutObject"] + + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com"] + } + + condition { + test = "StringLike" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + } + + statement { + sid = "AWSCloudTrailAclCheck" + effect = "Allow" + resources = [aws_s3_bucket.cloudtrail.arn] + actions = ["s3:GetBucketAcl"] + + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com"] + } + } +} diff --git a/cloudtrail/s3.tf b/cloudtrail/s3.tf new file mode 100644 index 0000000..134b874 --- /dev/null +++ b/cloudtrail/s3.tf @@ -0,0 +1,88 @@ +resource "aws_s3_bucket" "this" { + bucket = local.bucket_name + acl = "private" + force_destroy = false + + logging { + target_bucket = var.access_log_bucket + target_prefix = format("%s/%s/", var.access_log_bucket_prefix, local.name) + } + + tags = merge( + local.base_tags, + var.tags, + { "Name" = local.name }, + ) + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + kms_master_key_id = var.kms_key_arn + sse_algorithm = "aws:kms" + } + } + } +} + +#--- +# bucket policy (apply also encryption key usage here?) +# deny unencrypted uploads policy statement removed for default encryption +#--- +data "aws_iam_policy_document" "bucket_policy" { + statement { + sid = "AWSCloudTrailAclCheck" + effect = "Allow" + actions = ["s3:GetBucketAcl"] + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com"] + } + resources = [aws_s3_bucket.this.arn] + } + statement { + sid = "AWSCloudTrailWrite" + effect = "Allow" + actions = ["s3:PutObject"] + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com"] + } + resources = [format("%v/%v/*", aws_s3_bucket.this.arn, var.cloudtrail_bucket_prefix)] + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + } +} + +#--- +# apply policy to bucket and public access block policy to bucket +#--- +resource "aws_s3_bucket_policy" "policy" { + bucket = aws_s3_bucket.this.bucket + policy = data.aws_iam_policy_document.bucket_policy.json + depends_on = [null_resource.policy_delay] +} + +resource "aws_s3_bucket_public_access_block" "this" { + bucket = aws_s3_bucket.this.id + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +#--- +# 180s delay needed for bucket to create and policy to apply, before +# creating a cloudtrail to point to it +#--- +resource "null_resource" "policy_delay" { + triggers = { + bucket = aws_s3_bucket.this.id + } + provisioner "local-exec" { + when = create + command = "sleep 180" + } +} diff --git a/cloudtrail/s3.tf.off b/cloudtrail/s3.tf.off new file mode 100644 index 0000000..dc5ee5b --- /dev/null +++ b/cloudtrail/s3.tf.off @@ -0,0 +1,92 @@ +#--- +# s3 object logging bucket, with encryption +#--- +resource "aws_s3_bucket" "this" { + bucket = local.name + acl = "private" + force_destroy = false + + logging { + target_bucket = var.access_log_bucket + target_prefix = format("%s/%s/", var.access_log_bucket_prefix, local.name) + } + + tags = merge( + local.base_tags, + var.tags, + { "Name" = local.name }, + ) + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + kms_master_key_id = aws_kms_key.key.arn + sse_algorithm = "aws:kms" + } + } + } +} + +#--- +# bucket policy (apply also encryption key usage here?) +# deny unencrypted uploads policy statement removed for default encryption +#--- +data "aws_iam_policy_document" "policy" { + statement { + sid = "AWSCloudTrailAclCheck" + effect = "Allow" + actions = ["s3:GetBucketAcl"] + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com"] + } + resources = [aws_s3_bucket.this.arn] + } + statement { + sid = "AWSCloudTrailWrite" + effect = "Allow" + actions = ["s3:PutObject"] + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com"] + } + resources = ["${aws_s3_bucket.this.arn}/${var.cloudtrail_bucket_prefix}/*"] + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + } +} + +#--- +# apply policy to bucket and public access block policy to bucket +#--- +resource "aws_s3_bucket_policy" "policy" { + bucket = aws_s3_bucket.this.bucket + policy = data.aws_iam_policy_document.policy.json + depends_on = [null_resource.policy_delay] +} + +resource "aws_s3_bucket_public_access_block" "this" { + bucket = aws_s3_bucket.this.id + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +#--- +# 180s delay needed for bucket to create and policy to apply, before +# creating a cloudtrail to point to it +#--- +resource "null_resource" "policy_delay" { + triggers = { + bucket = aws_s3_bucket.this.id + # policy = aws_s3_bucket_policy.policy.id + } + provisioner "local-exec" { + when = create + command = "sleep 180" + } +} diff --git a/cloudtrail/variables.tf b/cloudtrail/variables.tf index b8cb955..3775f86 100644 --- a/cloudtrail/variables.tf +++ b/cloudtrail/variables.tf @@ -49,6 +49,11 @@ variable "kms_key_management_identifiers" { default = [] } +variable "kms_key_arn" { + description = "AWS CloudTrail KMS ARN to be used for encrypting the ClouldTrail, S3 Bucket, and SQS" + type = string +} + variable "enable_sns" { description = "Flag to enable or disable the creation of SNS for Cloudtrail (TBD)" type = bool