Skip to content

Commit

Permalink
Partition and restrict S3 paths across SFTP users (#9)
Browse files Browse the repository at this point in the history
* Adds support for restricted SFTP users so they can only access their home directory

* Auto Format

* Changes name of sftp_restricted to restricted_home

* Auto Format

* PR updates. Now creates multiple IAM labels based on incoming user list

* Auto Format

Co-authored-by: cloudpossebot <11232728+cloudpossebot@users.noreply.github.com>
  • Loading branch information
bradj and cloudpossebot committed Aug 9, 2021
1 parent 5074c43 commit b17ec48
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 53 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ Available targets:
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13 |
| <a name="requirement_local"></a> [local](#requirement\_local) | >= 1.2 |
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 2.2 |

## Providers

Expand All @@ -157,17 +155,17 @@ Available targets:
| Name | Type |
|------|------|
| [aws_eip.sftp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_iam_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_policy.s3_access_for_sftp_users](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_iam_role.s3_access_for_sftp_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_route53_record.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_transfer_server.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_server) | resource |
| [aws_transfer_ssh_key.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_ssh_key) | resource |
| [aws_transfer_user.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_user) | resource |
| [aws_iam_policy_document.allows_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.s3_access_for_sftp_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_s3_bucket.landing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_bucket) | data source |

## Inputs
Expand All @@ -192,6 +190,7 @@ Available targets:
| <a name="input_name"></a> [name](#input\_name) | Solution name, e.g. 'app' or 'jenkins' | `string` | `null` | no |
| <a name="input_namespace"></a> [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `null` | no |
| <a name="input_regex_replace_chars"></a> [regex\_replace\_chars](#input\_regex\_replace\_chars) | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.<br>If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
| <a name="input_restricted_home"></a> [restricted\_home](#input\_restricted\_home) | Restricts SFTP users so they only have access to their home directories. | `bool` | `true` | no |
| <a name="input_s3_bucket_name"></a> [s3\_bucket\_name](#input\_s3\_bucket\_name) | This is the bucket that the SFTP users will use when managing files | `string` | n/a | yes |
| <a name="input_security_group_description"></a> [security\_group\_description](#input\_security\_group\_description) | The Security Group description. | `string` | `"AWS Transfer Server Security Group"` | no |
| <a name="input_security_group_enabled"></a> [security\_group\_enabled](#input\_security\_group\_enabled) | Whether to create default Security Group for AWS Transfer Server. | `bool` | `true` | no |
Expand Down
9 changes: 4 additions & 5 deletions docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13 |
| <a name="requirement_local"></a> [local](#requirement\_local) | >= 1.2 |
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 2.2 |

## Providers

Expand All @@ -27,17 +25,17 @@
| Name | Type |
|------|------|
| [aws_eip.sftp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_iam_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_policy.s3_access_for_sftp_users](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_iam_role.s3_access_for_sftp_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_route53_record.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_transfer_server.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_server) | resource |
| [aws_transfer_ssh_key.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_ssh_key) | resource |
| [aws_transfer_user.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/transfer_user) | resource |
| [aws_iam_policy_document.allows_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.s3_access_for_sftp_users](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_s3_bucket.landing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/s3_bucket) | data source |

## Inputs
Expand All @@ -62,6 +60,7 @@
| <a name="input_name"></a> [name](#input\_name) | Solution name, e.g. 'app' or 'jenkins' | `string` | `null` | no |
| <a name="input_namespace"></a> [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `null` | no |
| <a name="input_regex_replace_chars"></a> [regex\_replace\_chars](#input\_regex\_replace\_chars) | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.<br>If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
| <a name="input_restricted_home"></a> [restricted\_home](#input\_restricted\_home) | Restricts SFTP users so they only have access to their home directories. | `bool` | `true` | no |
| <a name="input_s3_bucket_name"></a> [s3\_bucket\_name](#input\_s3\_bucket\_name) | This is the bucket that the SFTP users will use when managing files | `string` | n/a | yes |
| <a name="input_security_group_description"></a> [security\_group\_description](#input\_security\_group\_description) | The Security Group description. | `string` | `"AWS Transfer Server Security Group"` | no |
| <a name="input_security_group_enabled"></a> [security\_group\_enabled](#input\_security\_group\_enabled) | Whether to create default Security Group for AWS Transfer Server. | `bool` | `true` | no |
Expand Down
9 changes: 5 additions & 4 deletions examples/vpc/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,11 @@ module "example" {
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}]
s3_bucket_name = module.s3_bucket.bucket_id
sftp_users = var.sftp_users
subnet_ids = [module.dynamic_subnets.public_subnet_ids[0]]
vpc_id = module.vpc.vpc_id
s3_bucket_name = module.s3_bucket.bucket_id
sftp_users = var.sftp_users
subnet_ids = [module.dynamic_subnets.public_subnet_ids[1]]
vpc_id = module.vpc.vpc_id
restricted_home = true

context = module.this.context
}
70 changes: 42 additions & 28 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ locals {

is_vpc = var.vpc_id != null
security_group_enabled = module.this.enabled && var.security_group_enabled
user_names = keys(var.sftp_users)
user_names_map = { for idx, user in local.user_names : idx => user }
}

data "aws_s3_bucket" "landing" {
Expand Down Expand Up @@ -40,10 +42,20 @@ resource "aws_transfer_user" "default" {
for_each = local.enabled ? var.sftp_users : {}

server_id = join("", aws_transfer_server.default[*].id)
role = join("", aws_iam_role.default[*].arn)
role = aws_iam_role.s3_access_for_sftp_users[index(local.user_names, each.value.user_name)].arn

home_directory = "/${var.s3_bucket_name}/${each.value.user_name}"
user_name = each.value.user_name
user_name = each.value.user_name

home_directory_type = var.restricted_home ? "LOGICAL" : "PATH"

dynamic "home_directory_mappings" {
for_each = var.restricted_home ? [1] : []

content {
entry = "/"
target = "/${var.s3_bucket_name}/$${Transfer:UserName}"
}
}

tags = module.this.tags
}
Expand Down Expand Up @@ -94,16 +106,6 @@ resource "aws_route53_record" "main" {
]
}

# IAM
module "iam_label" {
source = "cloudposse/label/null"
version = "0.24.1"

attributes = ["transfer", "s3"]

context = module.this.context
}

module "logging_label" {
source = "cloudposse/label/null"
version = "0.24.1"
Expand All @@ -126,8 +128,8 @@ data "aws_iam_policy_document" "assume_role_policy" {
}
}

data "aws_iam_policy_document" "allows_s3" {
count = local.enabled ? 1 : 0
data "aws_iam_policy_document" "s3_access_for_sftp_users" {
for_each = local.enabled ? local.user_names_map : {}

statement {
sid = "AllowListingOfUserFolder"
Expand Down Expand Up @@ -157,7 +159,7 @@ data "aws_iam_policy_document" "allows_s3" {
]

resources = [
"${join("", data.aws_s3_bucket.landing[*].arn)}/*"
"${join("", data.aws_s3_bucket.landing[*].arn)}/${each.value}/*"
]
}
}
Expand All @@ -180,26 +182,38 @@ data "aws_iam_policy_document" "logging" {
}
}

resource "aws_iam_policy" "default" {
count = local.enabled ? 1 : 0
module "iam_label" {
for_each = local.enabled ? local.user_names_map : {}

name = module.iam_label.id
policy = join("", data.aws_iam_policy_document.allows_s3[*].json)
source = "cloudposse/label/null"
version = "0.24.1"

attributes = ["transfer", "s3", each.value]

context = module.this.context
}

resource "aws_iam_policy" "logging" {
count = local.enabled ? 1 : 0
resource "aws_iam_policy" "s3_access_for_sftp_users" {
for_each = local.enabled ? local.user_names_map : {}

name = module.logging_label.id
policy = join("", data.aws_iam_policy_document.logging[*].json)
name = module.iam_label[index(local.user_names, each.value)].id
policy = data.aws_iam_policy_document.s3_access_for_sftp_users[index(local.user_names, each.value)].json
}

resource "aws_iam_role" "default" {
count = local.enabled ? 1 : 0
resource "aws_iam_role" "s3_access_for_sftp_users" {
for_each = local.enabled ? local.user_names_map : {}

name = module.iam_label[index(local.user_names, each.value)].id

name = module.iam_label.id
assume_role_policy = join("", data.aws_iam_policy_document.assume_role_policy[*].json)
managed_policy_arns = [join("", aws_iam_policy.default[*].arn)]
managed_policy_arns = [aws_iam_policy.s3_access_for_sftp_users[index(local.user_names, each.value)].arn]
}

resource "aws_iam_policy" "logging" {
count = local.enabled ? 1 : 0

name = module.logging_label.id
policy = join("", data.aws_iam_policy_document.logging[*].json)
}

resource "aws_iam_role" "logging" {
Expand Down
6 changes: 6 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ variable "sftp_users" {
description = "List of SFTP usernames and public keys"
}

variable "restricted_home" {
type = bool
description = "Restricts SFTP users so they only have access to their home directories."
default = true
}

variable "force_destroy" {
type = bool
description = "Forces the AWS Transfer Server to be destroyed"
Expand Down
12 changes: 1 addition & 11 deletions versions.tf
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
terraform {
required_version = ">= 0.13"

required_providers {
# Update these to reflect the actual requirements of your module
local = {
source = "hashicorp/local"
version = ">= 1.2"
}
random = {
source = "hashicorp/random"
version = ">= 2.2"
}
}
required_providers {}
}

0 comments on commit b17ec48

Please sign in to comment.