diff --git a/README.md b/README.md index 0351399..6d62d03 100644 --- a/README.md +++ b/README.md @@ -44,12 +44,19 @@ Comment in these badges if they apply to the repository. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [accessors\_read\_write](#input\_accessors\_read\_write) | List of accessors that are allowed to read & write. | `list(string)` | `[]` | no | +| [access\_points](#input\_access\_points) | List of access points to create. |
map(object({
posix_user = optional(object({
gid = number
uid = number
secondary_gids = optional(list(number))
}))

root_directory = optional(object({
path = string

creation_info = optional(object({
owner_gid = number
owner_uid = number
permissions = string
}))
}))
}))
| `{}` | no | +| [aws\_iam\_principals](#input\_aws\_iam\_principals) | AWS IAM principals which will be allowed to access the file system via the EFS policy. | `list(string)` | `[]` | no | | [bypass\_policy\_lockout\_safety\_check](#input\_bypass\_policy\_lockout\_safety\_check) | A flag to indicate whether to bypass the aws\_efs\_file\_system\_policy lockout safety check. | `bool` | `false` | no | +| [enable\_customer\_managed\_kms](#input\_enable\_customer\_managed\_kms) | If enabled, will create a customer managed KMS key for at-rest encryption. | `bool` | `false` | no | | [enable\_enhanced\_backups](#input\_enable\_enhanced\_backups) | Enable enhanced backups. | `bool` | `false` | no | | [encrypted](#input\_encrypted) | If true, the disk will be encrypted. | `bool` | `true` | no | +| [enforce\_read\_only\_default](#input\_enforce\_read\_only\_default) | Enforce read-only access to the file system. Identity-based policies can override these default permissions. | `bool` | `false` | no | +| [enforce\_transit\_encryption](#input\_enforce\_transit\_encryption) | Enforce in-transit encryption for all clients. | `bool` | `true` | no | +| [kms\_key\_id](#input\_kms\_key\_id) | The ARN of the AWS KMS to encrypt the file system. Defaults to the AWS managed KMS key. | `string` | `null` | no | | [name](#input\_name) | The name of the file system. | `string` | n/a | yes | | [performance\_mode](#input\_performance\_mode) | The file system performance mode. Can be either `generalPurpose` or `maxIO`. | `string` | `"generalPurpose"` | no | +| [prevent\_anonymous\_access](#input\_prevent\_anonymous\_access) | Prevent anonymous access to the file system. | `bool` | `false` | no | +| [prevent\_root\_access\_default](#input\_prevent\_root\_access\_default) | Prevent root access to the file system. Identity-based policies can override these default permissions. | `bool` | `false` | no | | [private\_subnets](#input\_private\_subnets) | A list of private subnets inside the VPC. | `list(string)` | n/a | yes | | [provisioned\_throughput\_in\_mibps](#input\_provisioned\_throughput\_in\_mibps) | The throughput, measured in MiB/s, that you want to provision for the file system. | `number` | `0` | no | | [security\_groups](#input\_security\_groups) | A list of security group IDs to associate with the file system. | `list(string)` | n/a | yes | @@ -78,12 +85,12 @@ Comment in these badges if they apply to the repository. ## Resources -- resource.aws_efs_file_system.main (main.tf#1) -- resource.aws_efs_file_system_policy.main (main.tf#44) -- resource.aws_efs_mount_target.main (main.tf#37) -- resource.random_uuid.main (main.tf#52) -- data source.aws_caller_identity.current (data.tf#1) -- data source.aws_iam_policy_document.main (data.tf#3) +- resource.aws_efs_access_point.main (main.tf#44) +- resource.aws_efs_file_system.main (main.tf#3) +- resource.aws_efs_file_system_policy.main (main.tf#37) +- resource.aws_efs_mount_target.main (main.tf#28) +- resource.random_uuid.main (main.tf#1) +- data source.aws_iam_policy_document.main (data.tf#1) # Examples ### Basic Example diff --git a/data.tf b/data.tf index 6f3e52e..ca68e27 100644 --- a/data.tf +++ b/data.tf @@ -1,28 +1,50 @@ -data "aws_caller_identity" "current" {} - data "aws_iam_policy_document" "main" { - statement { - sid = "Allow Access to EFS" - effect = "Allow" - resources = [aws_efs_file_system.main.arn] - - actions = [ - "elasticfilesystem:ClientMount", - "elasticfilesystem:ClientWrite", - ] - - condition { - test = "Bool" - variable = "aws:SecureTransport" - values = ["true"] + dynamic "statement" { + for_each = [true] + + content { + sid = "AccessRules" + resources = [aws_efs_file_system.main.arn] + effect = "Allow" + + principals { + type = "AWS" + identifiers = var.aws_iam_principals + } + + actions = compact([ + !var.prevent_root_access_default ? "elasticfilesystem:ClientRootAccess" : null, + !var.enforce_read_only_default ? "elasticfilesystem:ClientWrite" : null, + !var.prevent_anonymous_access ? "elasticfilesystem:ClientMount" : null, + ]) + + condition { + test = "Bool" + variable = "elasticfilesystem:AccessedViaMountTarget" + values = ["true"] + } } + } + + dynamic "statement" { + for_each = var.enforce_transit_encryption ? [true] : [] + + content { + sid = "EnforceInTransitEncryption" + resources = [aws_efs_file_system.main.arn] + effect = "Deny" + actions = ["*"] + + principals { + type = "AWS" + identifiers = ["*"] + } - principals { - type = "AWS" - identifiers = concat( - ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"], - var.accessors_read_write - ) + condition { + test = "Bool" + variable = "aws:SecureTransport" + values = ["false"] + } } } } diff --git a/main.tf b/main.tf index c245d3d..54dc61a 100644 --- a/main.tf +++ b/main.tf @@ -1,21 +1,15 @@ -resource "aws_efs_file_system" "main" { +resource "random_uuid" "main" {} - # Creation token +resource "aws_efs_file_system" "main" { creation_token = random_uuid.main.result # Encryption - encrypted = var.encrypted - - # KMS - kms_key_id = module.kms.key_arn + encrypted = var.encrypted + kms_key_id = var.enable_customer_managed_kms ? module.kms[0].key_arn : var.kms_key_id - # Performance Mode - performance_mode = var.performance_mode - - # Throughput mode - throughput_mode = var.throughput_mode - - # Throughput provision + # Performance + performance_mode = var.performance_mode + throughput_mode = var.throughput_mode provisioned_throughput_in_mibps = var.provisioned_throughput_in_mibps lifecycle_policy { @@ -26,17 +20,16 @@ resource "aws_efs_file_system" "main" { transition_to_primary_storage_class = var.transition_to_primary_storage_class } - tags = merge( - var.tags, - { - "Name" = var.name - } - ) + tags = merge(var.tags, { + "Name" = var.name + }) } resource "aws_efs_mount_target" "main" { - count = length(var.private_subnets) - file_system_id = aws_efs_file_system.main.id + count = length(var.private_subnets) + + file_system_id = aws_efs_file_system.main.id + security_groups = var.security_groups subnet_id = var.private_subnets[count.index] } @@ -44,12 +37,47 @@ resource "aws_efs_mount_target" "main" { resource "aws_efs_file_system_policy" "main" { file_system_id = aws_efs_file_system.main.id + policy = data.aws_iam_policy_document.main.json bypass_policy_lockout_safety_check = var.bypass_policy_lockout_safety_check - - policy = data.aws_iam_policy_document.main.json } -resource "random_uuid" "main" {} +resource "aws_efs_access_point" "main" { + for_each = var.access_points + + file_system_id = aws_efs_file_system.main.id + + dynamic "posix_user" { + for_each = each.value.posix_user != null ? [each.value.posix_user] : [] + + content { + uid = posix_user.value.uid + gid = posix_user.value.gid + secondary_gids = posix_user.value.secondary_gids + } + } + + dynamic "root_directory" { + for_each = each.value.root_directory != null ? [each.value.root_directory] : [] + + content { + path = root_directory.value.path + + dynamic "creation_info" { + for_each = root_directory.value.creation_info != null ? [root_directory.value.creation_info] : [] + + content { + owner_uid = creation_info.value.owner_uid + owner_gid = creation_info.value.owner_gid + permissions = creation_info.value.permissions + } + } + } + } + + tags = merge(var.tags, { + "Name" = "${var.name}-${each.key}" + }) +} module "backup" { count = var.enable_enhanced_backups ? 1 : 0 @@ -60,16 +88,18 @@ module "backup" { vault_name = var.name backup_name = var.name - resources = [ - aws_efs_file_system.main.arn - ] + service = "efs" + resources = [aws_efs_file_system.main.arn] - service = "efs" + tags = var.tags } module "kms" { + count = var.enable_customer_managed_kms ? 1 : 0 + source = "geekcell/kms/aws" version = ">= 1.0.0, < 2.0.0" alias = format("alias/efs/%s", var.name) + tags = var.tags } diff --git a/variables.tf b/variables.tf index 1b1878b..bb95891 100644 --- a/variables.tf +++ b/variables.tf @@ -24,6 +24,18 @@ variable "encrypted" { type = bool } +variable "kms_key_id" { + description = "The ARN of the AWS KMS to encrypt the file system. Defaults to the AWS managed KMS key." + default = null + type = string +} + +variable "enable_customer_managed_kms" { + description = "If enabled, will create a customer managed KMS key for at-rest encryption." + default = false + type = bool +} + variable "name" { description = "The name of the file system." type = string @@ -69,8 +81,54 @@ variable "transition_to_primary_storage_class" { type = string } -variable "accessors_read_write" { +variable "access_points" { + default = {} + description = "List of access points to create." + type = map(object({ + posix_user = optional(object({ + gid = number + uid = number + secondary_gids = optional(list(number)) + })) + + root_directory = optional(object({ + path = string + + creation_info = optional(object({ + owner_gid = number + owner_uid = number + permissions = string + })) + })) + })) +} + +variable "aws_iam_principals" { + description = "AWS IAM principals which will be allowed to access the file system via the EFS policy." default = [] - description = "List of accessors that are allowed to read & write." type = list(string) } + +variable "prevent_root_access_default" { + description = "Prevent root access to the file system. Identity-based policies can override these default permissions." + default = false + type = bool +} + +variable "enforce_read_only_default" { + description = "Enforce read-only access to the file system. Identity-based policies can override these default permissions." + default = false + type = bool +} + +variable "prevent_anonymous_access" { + description = "Prevent anonymous access to the file system." + default = false + type = bool +} + +variable "enforce_transit_encryption" { + description = "Enforce in-transit encryption for all clients." + default = true + type = bool +}