diff --git a/org-logging/README.md b/org-logging/README.md index bedbe74..bb60bb0 100644 --- a/org-logging/README.md +++ b/org-logging/README.md @@ -141,6 +141,8 @@ No modules. |------|------| | [aws_iam_policy.logging_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | | [aws_iam_role.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_kms_alias.key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | +| [aws_kms_key.key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | | [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | | [aws_s3_bucket_acl.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource | | [aws_s3_bucket_logging.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource | @@ -167,6 +169,11 @@ No modules. | [aws_iam_policy_document.additional_logging_deadletter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.additional_logging_sqs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | 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.empty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.key_admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.key_orig](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.key_policy_combined](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.logging_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.logging_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.logging_deadletter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -192,8 +199,10 @@ No modules. | [enable\_organization](#input\_enable\_organization) | Enable Logging as an organization trail. This will only work in the organization master account | `bool` | `false` | 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\_admin\_roles](#input\_kms\_admin\_roles) | AWS KMS Key administrative role(s) which have full access to the key. The root user is included by default. | `list(string)` | `[]` | no | | [kms\_key\_arn](#input\_kms\_key\_arn) | AWS Logging 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 | +| [kms\_policy\_document](#input\_kms\_policy\_document) | AWS KMS Key Policy Document JSON, merged with admin policy document | `string` | `null` | no | | [name](#input\_name) | Name to apply to Cloudtrail, S3, SNS and SQS | `string` | `null` | no | | [organization\_id](#input\_organization\_id) | AWS Organization ID | `string` | `""` | 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 | @@ -204,6 +213,9 @@ No modules. | Name | Description | |------|-------------| | [additional\_sqs\_info](#output\_additional\_sqs\_info) | Additional SQS ARNs and IDs (main, deadletter) | +| [kms\_alias\_name](#output\_kms\_alias\_name) | Org Logging Key Alias name | +| [kms\_key\_arn](#output\_kms\_key\_arn) | Org Logging Key ARN | +| [kms\_key\_id](#output\_kms\_key\_id) | Org Logging Key ID | | [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | Created S3 Bucket ARN | | [s3\_bucket\_id](#output\_s3\_bucket\_id) | Created S3 Bucket ID | | [sns\_arn](#output\_sns\_arn) | SNS ARN | diff --git a/org-logging/kms.tf b/org-logging/kms.tf new file mode 100644 index 0000000..99450bf --- /dev/null +++ b/org-logging/kms.tf @@ -0,0 +1,265 @@ +locals { + name = var.name == null ? format("%v-%v", lookup(local._defaults["org_logging"], "name"), local.region) : var.name + kms_key_name = format("%v%v", local._prefixes["kms"], 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 = var.kms_admin_roles + kms_policy_document = var.kms_policy_document != null ? var.kms_policy_document : data.aws_iam_policy_document.empty.json + +} + +resource "aws_kms_key" "key" { + description = "KMS CMK for Organization Logging" + enable_key_rotation = true + policy = data.aws_iam_policy_document.key_policy_combined.json + + tags = merge( + local.base_tags, + var.tags, + { + "boc:aws:region" = local.region + Name = local.name + }, + ) +} + +resource "aws_kms_alias" "key" { + name = "alias/${local.kms_key_name}" + target_key_id = aws_kms_key.key.key_id +} + +data "aws_iam_policy_document" "key_policy_combined" { + source_policy_documents = [ + data.aws_iam_policy_document.key.json, + data.aws_iam_policy_document.key_admin.json, + local.kms_policy_document + ] +} + +data "aws_iam_policy_document" "key_orig" { + policy_id = "Logging KMS Access" + statement { + sid = "EnableIAMUserPermissions" + effect = "Allow" + actions = ["kms:*"] + resources = ["*"] + principals { + type = "AWS" + identifiers = [local.kms_admin_root] + } + } + statement { + sid = "AllowOrgLoggingEncryptLogs" + effect = "Allow" + actions = ["kms:GenerateDataKey*"] + resources = ["*"] + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com", "logs.amazonaws.com", "logs.${local.region}.amazonaws.com"] + } + condition { + test = "StringLike" + variable = "kms:EncryptionContext:aws:cloudtrail:arn" + # values = [format("arn:%v:cloudtrail:%v:%v:trail/*",local.partition,local.region,local.account_id] + values = [format("arn:%v:cloudtrail:*:%v:trail/*", local.partition, local.account_id)] + } + } + statement { + sid = "AllowOrgLoggingKeyActivities" + 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 = [format("arn:%v:cloudtrail:%v:%v:trail/*",local.partition,local.region,local.account_id] + values = [format("arn:%v:cloudtrail:*:%v:trail/*", local.partition, local.account_id)] + } + } + 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 = [format("arn:%v:cloudtrail:%v:%v:trail/*",local.partition,local.region,local.account_id] + values = [format("arn:%v:cloudtrail:*:%v:trail/*", local.partition, local.account_id)] + } + } + 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 = [format("ec2.%v.amazonaws.com", local.region)] + } + } +} + +data "aws_iam_policy_document" "key_admin" { + dynamic "statement" { + for_each = length(local.kms_admin_roles) > 0 ? [1] : [] + content { + sid = "BuiltinKMSAdminRoles" + effect = "Allow" + actions = ["kms:*"] + resources = ["*"] + principals { + type = "AWS" + identifiers = local.kms_admin_roles + } + } + } +} + +data "aws_iam_policy_document" "empty" {} + + +#--- +# key policy for clodutrail +# https://docs.aws.amazon.com/awscloudtrail/latest/userguide/create-kms-key-policy-for-cloudtrail.html +# can't use aws_cloudtrail.this.arn as it makes for a circular reference +# +# from aws-setup-s3-object-logging +#--- +data "aws_iam_policy_document" "key" { + policy_id = "object-logging-cloud-trail" + # manage key by root and other principals + statement { + sid = "IAMPermissionsAccessKMSManagement" + effect = "Allow" + actions = ["kms:*"] + resources = ["*"] + principals { + type = "AWS" + identifiers = [local.kms_admin_root] + } + } + # let cloudtrial, logs, sns, and sqs find key + statement { + sid = "KMSDescribeKeyFromServices" + effect = "Allow" + actions = ["kms:DescribeKey"] + resources = ["*"] + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com", "sns.amazonaws.com", "sqs.amazonaws.com"] + } + } + statement { + sid = "OrgLoggingKMSEncryptAccess" + effect = "Allow" + actions = [ + "kms:Decrypt*", + "kms:Encrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey", + ] + resources = ["*"] + principals { + type = "Service" + identifiers = ["cloudtrail.amazonaws.com"] + } + # condition { + # test = "StringLike" + # variable = "kms:EncryptionContext:aws:cloudtrail:arn" + # values = [format("arn:%v:cloudtrail:*:%v:trail/*", local.partition, local.account_id)] + # } + } + # https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html + statement { + sid = "Cloudwatch" + effect = "Allow" + actions = [ + "kms:Decrypt*", + "kms:Encrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:Describe*" + ] + resources = ["*"] + principals { + type = "Service" + identifiers = ["logs.amazonaws.com", "logs.${local.region}.amazonaws.com"] + } + condition { + test = "StringLike" + variable = "kms:EncryptionContext:aws:logs:arn" + values = [format("arn:%v:logs:%v:%v:log-group:*", local.partition, local.region, local.account_id)] + } + } + # https://aws.amazon.com/blogs/compute/encrypting-messages-published-to-amazon-sns-with-aws-kms/ + # https://docs.aws.amazon.com/sns/latest/dg/sns-key-management.html#sns-what-permissions-for-sse + # https://docs.aws.amazon.com/sns/latest/dg/sns-enable-encryption-for-topic-sqs-queue-subscriptions.html + statement { + sid = "ServiceMSAccess" + effect = "Allow" + actions = [ + "kms:Decrypt*", + "kms:GenerateDataKey*", + "kms:Describe*" + ] + resources = ["*"] + principals { + type = "Service" + identifiers = ["sns.amazonaws.com", "sqs.amazonaws.com"] + } + } +} + +# producers and consumers +# https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-key-management.html#compatibility-with-aws-services diff --git a/org-logging/outputs.kms.tf b/org-logging/outputs.kms.tf new file mode 100644 index 0000000..f5e2fb6 --- /dev/null +++ b/org-logging/outputs.kms.tf @@ -0,0 +1,14 @@ +output "kms_key_id" { + description = "Org Logging Key ID" + value = aws_kms_key.key.id +} + +output "kms_key_arn" { + description = "Org Logging Key ARN" + value = aws_kms_key.key.arn +} + +output "kms_alias_name" { + description = "Org Logging Key Alias name" + value = aws_kms_alias.key.arn +} diff --git a/org-logging/variables.kms.tf b/org-logging/variables.kms.tf new file mode 100644 index 0000000..7a5dbab --- /dev/null +++ b/org-logging/variables.kms.tf @@ -0,0 +1,23 @@ +variable "name" { + description = "Name to apply to Org Logging KMS Key (default: k-inf-org-logging)" + type = string + default = null +} + +variable "kms_policy_document" { + description = "AWS KMS Key Policy Document JSON, merged with admin policy document" + type = string + default = null +} + +variable "kms_admin_roles" { + description = "AWS KMS Key administrative role(s) which have full access to the key. The root user is included by default." + type = list(string) + default = [] +} + +variable "component_tags" { + description = "Additional tags for Components (s3, kms, ddb)" + type = map(map(string)) + default = { "s3" = {}, "kms" = {}, "ddb" = {} } +}