Skip to content

Commit

Permalink
AWS Transfer Family - Session Policy (#87)
Browse files Browse the repository at this point in the history
* feat: prevent confused deputy attack

* Continuous Integration - terraform fmt and terraform-docs

* added default session policy

* Merge branch 'aws_transfer_family_session_policy' of https://github.com/zachreborn/terraform-modules into aws_transfer_family_session_policy

* correctly escaped out of the session policy

* logic for the default_session_policy

* added validation to the aws_transfer_family module

* Continuous Integration - terraform fmt and terraform-docs

* added `s3_bucket_arn` output to transfer_family

* Continuous Integration - terraform fmt and terraform-docs
  • Loading branch information
zachreborn authored Nov 22, 2024
1 parent 80308d8 commit 1c3b88c
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 11 deletions.
7 changes: 5 additions & 2 deletions modules/aws/transfer_family/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ _For more examples, please refer to the [Documentation](https://github.com/zachr
| [aws_transfer_server.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_server) | resource |
| [aws_transfer_ssh_key.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_ssh_key) | resource |
| [aws_transfer_user.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_user) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |

## Inputs

Expand All @@ -130,7 +132,7 @@ _For more examples, please refer to the [Documentation](https://github.com/zachr
| <a name="input_as2_transports"></a> [as2\_transports](#input\_as2\_transports) | (Optional) The transport method for AS2 messages. Valid values are HTTP. | `list(string)` | `null` | no |
| <a name="input_certificate"></a> [certificate](#input\_certificate) | (Optional) The ARN of the AWS Certificate Manager certificate to use with the server | `string` | `null` | no |
| <a name="input_directory_id"></a> [directory\_id](#input\_directory\_id) | (Optional) The ID of the AWS Directory Service directory that you want to associate with the server | `string` | `null` | no |
| <a name="input_endpoint_type"></a> [endpoint\_type](#input\_endpoint\_type) | (Optional) The type of endpoint that you want your server to use. Valid values are VPC and PUBLIC | `string` | `"PUBLIC"` | no |
| <a name="input_endpoint_type"></a> [endpoint\_type](#input\_endpoint\_type) | (Optional) The type of endpoint that you want your server to use. Valid values are PUBLIC, VPC, and VPC\_ENDPOINT. Default value is PUBLIC. | `string` | `"PUBLIC"` | no |
| <a name="input_function"></a> [function](#input\_function) | (Optional) The ARN of the AWS Lambda function that is invoked for user authentication | `string` | `null` | no |
| <a name="input_host_key"></a> [host\_key](#input\_host\_key) | (Optional) The RSA, ECDSA, or ED25519 private key. This must be created ahead of time. | `string` | `null` | no |
| <a name="input_identity_provider_type"></a> [identity\_provider\_type](#input\_identity\_provider\_type) | (Optional) The mode of authentication enabled for this service. Valid values are SERVICE\_MANAGED or API\_GATEWAY | `string` | `"SERVICE_MANAGED"` | no |
Expand All @@ -150,14 +152,15 @@ _For more examples, please refer to the [Documentation](https://github.com/zachr
| <a name="input_tags"></a> [tags](#input\_tags) | (Optional) Key-value mapping of resource tags | `map(string)` | <pre>{<br/> "terraform": "true"<br/>}</pre> | no |
| <a name="input_tls_session_resumption_mode"></a> [tls\_session\_resumption\_mode](#input\_tls\_session\_resumption\_mode) | (Optional) Specifies the mode of the TLS session resumption. Valid values are: DISABLED, ENABLED, and ENFORCED. | `string` | `null` | no |
| <a name="input_url"></a> [url](#input\_url) | (Optional) The URL of the file transfer protocol endpoint that is used to authentication users through an API\_GATEWAY. | `string` | `null` | no |
| <a name="input_users"></a> [users](#input\_users) | (Optional) A map of user names and their configuration | <pre>map(object({<br/> home_directory = optional(string) # Cannot be set if home_directory_type is set to "LOGICAL".<br/> home_directory_type = optional(string, "LOGICAL") # Default is "LOGICAL"<br/> policy = optional(string) # Set for a custom session policy see https://docs.aws.amazon.com/transfer/latest/userguide/requirements-roles.html#session-policy for more information<br/> public_key = optional(string) # The public key portion of an SSH key pair<br/> username = string<br/> }))</pre> | `{}` | no |
| <a name="input_users"></a> [users](#input\_users) | (Optional) A map of user names and their configuration | <pre>map(object({<br/> home_directory = optional(string) # The landing directory for a user. Cannot be set if home_directory_type is set to "LOGICAL".<br/> home_directory_type = optional(string, "LOGICAL") # The type of landing directory. Valid values are `PATH` and `LOGICAL`. Defaults to `LOGICAL`.<br/> policy = optional(string) # Set for a custom session policy see https://docs.aws.amazon.com/transfer/latest/userguide/requirements-roles.html#session-policy for more information<br/> public_key = optional(string) # The public key portion of an SSH key pair. See https://docs.aws.amazon.com/transfer/latest/userguide/key-management.html for supported key algorithms.<br/> username = string # The username of the user.<br/> }))</pre> | `{}` | no |
| <a name="input_vpc_endpoint_id"></a> [vpc\_endpoint\_id](#input\_vpc\_endpoint\_id) | (Optional) The ID of the VPC endpoint. This property can only be used when endpoint\_type is set to VPC. | `string` | `null` | no |
| <a name="input_vpc_id"></a> [vpc\_id](#input\_vpc\_id) | (Optional) The ID of the VPC that is used for the transfer server. This property can only be used when endpoint\_type is set to VPC. | `string` | `null` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_s3_bucket_arn"></a> [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | The ARN of the S3 bucket |
| <a name="output_server_arn"></a> [server\_arn](#output\_server\_arn) | The ARN of the transfer family server |
| <a name="output_server_endpoint"></a> [server\_endpoint](#output\_server\_endpoint) | The endpoint of the transfer family server |
| <a name="output_server_host_key_fingerprint"></a> [server\_host\_key\_fingerprint](#output\_server\_host\_key\_fingerprint) | The RSA private key fingerprint of the transfer family server |
Expand Down
61 changes: 57 additions & 4 deletions modules/aws/transfer_family/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,55 @@ terraform {
# Data Sources
###########################

data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

###########################
# Locals
###########################
locals {
# The default session policy used by the transfer family server and each user. This policy allows each user access to ONLY their home directory.
default_session_policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Sid = "AllowListingOfUserFolder",
Action = [
"s3:ListBucket",
"s3:GetBucketLocation"
],
Effect = "Allow",
Resource = [
"arn:aws:s3:::$${Transfer:HomeBucket}"
]
Condition = {
StringLike = {
"s3:prefix" : [
"$${Transfer:HomeDirectory}/*",
"$${Transfer:HomeDirectory}"
]
}
}
},
{
Sid = "HomeDirObjectAccess",
Action = [
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetObject",
"s3:GetObjectACL",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:PutObjectACL"
],
Effect = "Allow",
Resource = [
"arn:aws:s3:::$${Transfer:HomeBucket}/$${Transfer:HomeDirectory}/*"
]
}
]
})
}

###########################
# Module Configuration
Expand Down Expand Up @@ -107,11 +152,11 @@ module "transfer_family_iam_role_policy" {
{
Sid = "HomeDirObjectAccess",
Action = [
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetObject",
"s3:GetObjectACL",
"s3:GetObjectVersion",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:PutObject",
"s3:PutObjectACL"
],
Expand All @@ -131,6 +176,14 @@ module "transfer_family_iam_role" {
Statement = [
{
Action = "sts:AssumeRole",
Condition = {
StringEquals = {
"aws:SourceAccount" : "${data.aws_caller_identity.current.account_id}"
},
ArnLike = {
"aws:SourceArn" : "arn:aws:transfer:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:user/*"
}
},
Effect = "Allow",
Principal = {
Service = "transfer.amazonaws.com"
Expand All @@ -153,14 +206,14 @@ resource "aws_transfer_user" "this" {

home_directory = each.value.home_directory
home_directory_type = each.value.home_directory_type
policy = each.value.policy
policy = each.value.home_directory_type == "LOGICAL" ? null : (each.value.policy == null ? local.default_session_policy : each.value.policy)
role = module.transfer_family_iam_role.arn
server_id = aws_transfer_server.this.id
tags = var.tags
user_name = each.value.username

dynamic "home_directory_mappings" {
# Disables the dynamic block of home_directory_mappings if home_directory_type is not "LOGICAL".
# Disables the dynamic block of home_directory_mappings if home_directory_type is not `LOGICAL`.
for_each = each.value.home_directory_type == "LOGICAL" ? [1] : []
content {
entry = "/"
Expand Down
5 changes: 5 additions & 0 deletions modules/aws/transfer_family/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
# Resource Outputs
###########################

output "s3_bucket_arn" {
description = "The ARN of the S3 bucket"
value = module.bucket.s3_bucket_arn
}

output "server_arn" {
description = "The ARN of the transfer family server"
value = aws_transfer_server.this.arn
Expand Down
14 changes: 9 additions & 5 deletions modules/aws/transfer_family/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ variable "directory_id" {
}

variable "endpoint_type" {
description = "(Optional) The type of endpoint that you want your server to use. Valid values are VPC and PUBLIC"
description = "(Optional) The type of endpoint that you want your server to use. Valid values are PUBLIC, VPC, and VPC_ENDPOINT. Default value is PUBLIC."
type = string
default = "PUBLIC"
validation {
condition = can(regex("^(PUBLIC|VPC|VPC_ENDPOINT)$", var.endpoint_type))
error_message = "The value of endpoint_type must be either PUBLIC, VPC, or VPC_ENDPOINT."
}
}

variable "function" {
Expand Down Expand Up @@ -222,11 +226,11 @@ variable "lifecycle_rules" {
variable "users" {
description = "(Optional) A map of user names and their configuration"
type = map(object({
home_directory = optional(string) # Cannot be set if home_directory_type is set to "LOGICAL".
home_directory_type = optional(string, "LOGICAL") # Default is "LOGICAL"
home_directory = optional(string) # The landing directory for a user. Cannot be set if home_directory_type is set to "LOGICAL".
home_directory_type = optional(string, "LOGICAL") # The type of landing directory. Valid values are `PATH` and `LOGICAL`. Defaults to `LOGICAL`.
policy = optional(string) # Set for a custom session policy see https://docs.aws.amazon.com/transfer/latest/userguide/requirements-roles.html#session-policy for more information
public_key = optional(string) # The public key portion of an SSH key pair
username = string
public_key = optional(string) # The public key portion of an SSH key pair. See https://docs.aws.amazon.com/transfer/latest/userguide/key-management.html for supported key algorithms.
username = string # The username of the user.
}))
default = {}
}
Expand Down

0 comments on commit 1c3b88c

Please sign in to comment.