/** * # CustomerManagedKmsKeys * * Module to create the following CMKs: * - allpurpose * - storage * - database * - secrets * - backup * - log * - notify * - ssm */ data "aws_region" "this" {} data "aws_caller_identity" "current" {} # Keys resource "aws_kms_key" "allpurpose" { count = var.create-allpurpose-key ? 1 : 0 description = "All purpose customer-managed KMS key" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.allpurpose.json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } resource "aws_kms_key" "storage" { count = var.create-storage-key ? 1 : 0 description = "Customer-managed KMS key for encrypting cloud storage such as EBS and S3" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.storage.json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } # Key use for EBS volumes on EKS nodes resource "aws_kms_key" "eks_ebs" { count = var.create-eksebs-key ? 1 : 0 description = "CMK for use with EBS volumes on EKS nodes" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.eksebs[0].json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } resource "aws_kms_key" "database" { count = var.create-database-key ? 1 : 0 description = "Customer-managed KMS key for encrypting cloud databases such as RDS, Elasticache, DynamoDB" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.database.json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } resource "aws_kms_key" "secret" { count = var.create-secret-key ? 1 : 0 description = "Customer-managed KMS key for encrypting secrets" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.secretsmanager.json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } resource "aws_kms_key" "backup" { count = var.create-backup-key ? 1 : 0 description = "Customer-managed KMS key for encrypting backup data" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.backup.json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } resource "aws_kms_key" "log" { count = var.create-log-key ? 1 : 0 description = "Customer-managed KMS key for cloudwatch logs and cloudtrail" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.log.json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } resource "aws_kms_key" "notify" { count = var.create-notify-key ? 1 : 0 description = "Customer-managed KMS key for encrypting notifications" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.notify.json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } resource "aws_kms_key" "notify" { count = var.create-ssm-key ? 1 : 0 description = "Customer-managed KMS key for encrypting ssm parameters" enable_key_rotation = var.enable_key_rotation rotation_period_in_days = var.rotation_period_in_days is_enabled = var.is_enabled policy = data.aws_iam_policy_document.ssm.json deletion_window_in_days = var.deletion_window_in_days customer_master_key_spec = "SYMMETRIC_DEFAULT" key_usage = "ENCRYPT_DECRYPT" multi_region = var.multi_region bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check } locals { prefix = var.name-prefix == null ? "" : "${var.name-prefix}-" } # Key aliases resource "aws_kms_alias" "allpurpose" { count = var.create-allpurpose-key ? 1 : 0 name = "alias/${local.prefix}allpurpose" target_key_id = aws_kms_key.allpurpose[0].id } resource "aws_kms_alias" "storage" { count = var.create-storage-key ? 1 : 0 name = "alias/${local.prefix}storage" target_key_id = aws_kms_key.storage[0].id } resource "aws_kms_alias" "eks_ebs" { count = var.create-eksebs-key ? 1 : 0 name = "alias/${local.prefix}eksebs" target_key_id = aws_kms_key.eks_ebs[0].id } resource "aws_kms_alias" "database" { count = var.create-database-key ? 1 : 0 name = "alias/${local.prefix}database" target_key_id = aws_kms_key.database[0].id } resource "aws_kms_alias" "backup" { count = var.create-backup-key ? 1 : 0 name = "alias/${local.prefix}backup" target_key_id = aws_kms_key.backup[0].id } resource "aws_kms_alias" "secret" { count = var.create-secret-key ? 1 : 0 name = "alias/${local.prefix}secret" target_key_id = aws_kms_key.secret[0].id } resource "aws_kms_alias" "log" { count = var.create-log-key ? 1 : 0 name = "alias/${local.prefix}log" target_key_id = aws_kms_key.log[0].id } resource "aws_kms_alias" "notify" { count = var.create-notify-key ? 1 : 0 name = "alias/${local.prefix}notify" target_key_id = aws_kms_key.notify[0].id } # Policies data "aws_iam_policy_document" "allpurpose" { source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "Allow use by AWS services" effect = "Allow" principals { identifiers = [ "delivery.logs.amazonaws.com" # vpc flow log ] type = "Service" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] } statement { sid = "Allow use of key by aws services" effect = "Allow" principals { identifiers = [data.aws_caller_identity.current.account_id] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] condition { test = "StringLike" values = [ "*.*.amazonaws.com" ] variable = "kms:ViaService" } } # this needs to be explicitly allowed for users and roles to be able to encrypt and decrypt data statement { sid = "Allow use of key by users and roles in same account" effect = "Allow" principals { identifiers = [data.aws_caller_identity.current.account_id] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] } statement { sid = "AllowAttachmentOfPersistentResources" effect = "Allow" principals { identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] type = "AWS" } actions = [ "kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant" ] resources = ["*"] condition { test = "Bool" values = ["true"] variable = "kms:GrantIsForAWSResource" } } } data "aws_iam_policy_document" "storage" { source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "Allow use by AWS services" effect = "Allow" principals { identifiers = [ "delivery.logs.amazonaws.com" # vpc flow log ] type = "Service" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] } statement { sid = "Allow use of key by s3 and ec2" effect = "Allow" principals { identifiers = [data.aws_caller_identity.current.account_id] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] condition { test = "StringLike" values = [ "ec2.*.amazonaws.com", "s3.*.amazonaws.com" ] variable = "kms:ViaService" } } # this needs to be explicitly allowed for users and roles to be able to encrypt and decrypt data statement { sid = "Allow use of key by users and roles in same account" effect = "Allow" principals { identifiers = [data.aws_caller_identity.current.account_id] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] } statement { sid = "AllowAttachmentOfPersistentResources" effect = "Allow" principals { identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] type = "AWS" } actions = [ "kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant" ] resources = ["*"] condition { test = "Bool" values = ["true"] variable = "kms:GrantIsForAWSResource" } } } data "aws_iam_policy_document" "database" { source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "Allow use by AWS services" effect = "Allow" principals { identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*", "kms:CreateGrant" ] resources = ["*"] condition { test = "StringLike" values = [ "rds.*.amazonaws.com", "elasticache.*.amazonaws.com", "dax.*.amazonaws.com", "dynamodb.*.amazonaws.com" ] variable = "kms:ViaService" } } statement { sid = "AllowAttachmentOfPersistentResources" effect = "Allow" principals { identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] type = "AWS" } actions = [ "kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant" ] resources = ["*"] condition { test = "Bool" values = ["true"] variable = "kms:GrantIsForAWSResource" } } } data "aws_iam_policy_document" "secretsmanager" { source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "Allow use of key by aws services" effect = "Allow" principals { identifiers = [data.aws_caller_identity.current.account_id] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] condition { test = "StringLike" values = [ "secretsmanager.*.amazonaws.com" ] variable = "kms:ViaService" } } # allow users in this account to encrypt and decrypt data statement { sid = "Allow use of key by users and roles in same account" effect = "Allow" principals { identifiers = [data.aws_caller_identity.current.account_id] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] } } # create an ASG service linked role if not already exist data "aws_iam_roles" "autoscaling" { count = var.create_asg_role ? 0 : 1 name_regex = "^AWSServiceRoleForAutoScaling$" } resource "aws_iam_service_linked_role" "autoscaling" { count = var.create_asg_role ? 1 : 0 aws_service_name = "autoscaling.amazonaws.com" description = "Service-linked role for AutoScaling" } locals { AsgServiceRoleArn = try(aws_iam_service_linked_role.autoscaling[0].arn, one(data.aws_iam_roles.autoscaling[0].arns)) } output "debug" { value = local.AsgServiceRoleArn } data "aws_iam_policy_document" "eksebs" { count = var.create-eksebs-key ? 1 : 0 source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "Allow use by EKS" effect = "Allow" principals { identifiers = [ local.AsgServiceRoleArn ] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] } statement { sid = "Allow grants by EKS" effect = "Allow" principals { identifiers = [ local.AsgServiceRoleArn ] type = "AWS" } actions = [ "kms:RevokeGrant", "kms:ListGrants", "kms:CreateGrant" ] resources = ["*"] condition { test = "Bool" values = ["true"] variable = "kms:GrantIsForAWSResource" } } } data "aws_iam_policy_document" "notify" { source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "Allow use by AWS services" effect = "Allow" principals { identifiers = [ "logs.${data.aws_region.this.id}.amazonaws.com", # cloudwatch log groups "backup.amazonaws.com", # Notifications "events.amazonaws.com" # Notifications ] type = "Service" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] } } data "aws_iam_policy_document" "log" { source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "Allow use by AWS services" effect = "Allow" principals { identifiers = [ "logs.${data.aws_region.this.id}.amazonaws.com", # cloudwatch log groups "apigateway.${data.aws_region.this.id}.amazonaws.com", # api gateway "delivery.logs.amazonaws.com", # vpc flow log "cloudtrail.amazonaws.com" # cloudtrail ] type = "Service" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:Describe*" ] resources = ["*"] } } # policy for awsbackup: https://docs.aws.amazon.com/aws-backup/latest/devguide/encryption.html data "aws_iam_policy_document" "backup" { source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "KmsPermissions" effect = "Allow" principals { identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] type = "AWS" } actions = [ "kms:ListKeys", "kms:DescribeKey", "kms:GenerateDataKey", "kms:ListAliases" ] resources = ["*"] } statement { sid = "KmsCreateGrantPermissions" effect = "Allow" principals { identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] type = "AWS" } actions = [ "kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant" ] resources = ["*"] condition { test = "ForAnyValue:StringEquals" values = ["aws:backup:backup-vault"] variable = "kms:EncryptionContextKeys" } condition { test = "Bool" values = [true] variable = "kms:GrantIsForAWSResource" } condition { test = "StringLike" values = ["backup.*.amazonaws.com"] variable = "kms:ViaService" } } } # base policies allowing full access to key admin and read access to all data "aws_iam_policy_document" "base" { source_policy_documents = [jsonencode( { "Id" : "CmkBasePolicy", "Version" : "2012-10-17", "Statement" : [ { "Sid" : "ReadPermissonForAll", "Effect" : "Allow", "Principal" : { "AWS" : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }, "Action" : [ "kms:DescribeKey", "kms:GetKeyPolicy", "kms:ListAliases", "kms:ListKeyPolicies", "kms:ListKeys", "kms:ListResourceTags", "kms:GetKeyRotationStatus", "kms:ListKeyRotations", "kms:ListGrants" ], "Resource" : "*" }, { "Sid" : "KeyAdministratorsAccess", "Effect" : "Allow", "Principal" : { "AWS" : var.key_administrator_arn != null ? var.key_administrator_arn : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }, "Action" : [ "kms:Create*", "kms:Describe*", "kms:Enable*", "kms:List*", "kms:Put*", "kms:Update*", "kms:Revoke*", "kms:Disable*", "kms:Get*", "kms:Delete*", "kms:TagResource", "kms:UntagResource", "kms:ScheduleKeyDeletion", "kms:CancelKeyDeletion", "kms:RotateKeyOnDemand" ], "Resource" : "*" } ] } )] } data "aws_iam_policy_document" "ssm" { source_policy_documents = [data.aws_iam_policy_document.base.json] statement { sid = "Allow access through SSM for all principals in the account that are authorized to use SSM" effect = "Allow" principals { identifiers = [data.aws_caller_identity.current.account_id] type = "AWS" } actions = [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey" ] resources = ["*"] condition { test = "StringLike" values = ["ssm.*.amazonaws.com"] variable = "kms:ViaService" } } } # data "aws_iam_policy_document" "this" { # source_policy_documents = var.source_policy_documents # override_policy_documents = var.override_policy_documents # # # Default policy - account wide access to all key operations # dynamic "statement" { # for_each = var.enable_default_policy ? [1] : [] # # content { # sid = "Default" # actions = ["kms:*"] # resources = ["*"] # # principals { # type = "AWS" # identifiers = ["arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root"] # } # } # } # # # Key owner - all key operations # dynamic "statement" { # for_each = var.enable_default_policy ? [1] : [] # # content { # sid = "KeyOwner" # actions = ["kms:*"] # resources = ["*"] # # principals { # type = "AWS" # identifiers = var.key_owners # } # } # } # # # Key administrators - https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-default-allow-administrators # dynamic "statement" { # for_each = length(var.key_administrators) > 0 ? [1] : [] # # content { # sid = "KeyAdministration" # actions = [ # "kms:Create*", # "kms:Describe*", # "kms:Enable*", # "kms:List*", # "kms:Put*", # "kms:Update*", # "kms:Revoke*", # "kms:Disable*", # "kms:Get*", # "kms:Delete*", # "kms:TagResource", # "kms:UntagResource", # "kms:ScheduleKeyDeletion", # "kms:CancelKeyDeletion", # ] # resources = ["*"] # # principals { # type = "AWS" # identifiers = var.key_administrators # } # } # } # # # Key users - https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-default-allow-users # dynamic "statement" { # for_each = length(var.key_users) > 0 ? [1] : [] # # content { # sid = "KeyUsage" # actions = [ # "kms:Encrypt", # "kms:Decrypt", # "kms:ReEncrypt*", # "kms:GenerateDataKey*", # "kms:DescribeKey", # ] # resources = ["*"] # # principals { # type = "AWS" # identifiers = var.key_users # } # } # } # # # Key service users - https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-service-integration # dynamic "statement" { # for_each = length(var.key_service_users) > 0 ? [1] : [] # # content { # sid = "KeyServiceUsage" # actions = [ # "kms:CreateGrant", # "kms:ListGrants", # "kms:RevokeGrant", # ] # resources = ["*"] # # principals { # type = "AWS" # identifiers = var.key_service_users # } # # condition { # test = "Bool" # variable = "kms:GrantIsForAWSResource" # values = [true] # } # } # } # # # Key cryptographic operations - https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-default.html#key-policy-users-crypto # dynamic "statement" { # for_each = length(var.key_symmetric_encryption_users) > 0 ? [1] : [] # # content { # sid = "KeySymmetricEncryption" # actions = [ # "kms:Decrypt", # "kms:DescribeKey", # "kms:Encrypt", # "kms:GenerateDataKey*", # "kms:ReEncrypt*", # ] # resources = ["*"] # # principals { # type = "AWS" # identifiers = var.key_symmetric_encryption_users # } # } # } # } # ################################################################################ # # Grant # ################################################################################ # # resource "aws_kms_grant" "this" { # for_each = { for k, v in var.grants : k => v if var.create } # # name = try(each.value.name, each.key) # key_id = aws_kms_key.this[0].key_id # grantee_principal = each.value.grantee_principal # operations = each.value.operations # # dynamic "constraints" { # for_each = length(lookup(each.value, "constraints", {})) == 0 ? [] : [each.value.constraints] # # content { # encryption_context_equals = try(constraints.value.encryption_context_equals, null) # encryption_context_subset = try(constraints.value.encryption_context_subset, null) # } # } # # retiring_principal = try(each.value.retiring_principal, null) # grant_creation_tokens = try(each.value.grant_creation_tokens, null) # retire_on_delete = try(each.value.retire_on_delete, null) # }