Skip to content

Commit

Permalink
Always require TLS connection to S3 bucket (#139)
Browse files Browse the repository at this point in the history
* Always deny unencrypted HTTP connections

* tflint fixes
  • Loading branch information
Nuru authored May 31, 2023
1 parent 1d48b08 commit 99453cc
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 60 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ Available targets:
| [aws_s3_bucket_server_side_encryption_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_s3_bucket_versioning.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [local_file.terraform_backend_config](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
| [aws_iam_policy_document.prevent_unencrypted_uploads](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.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.replication_sts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
Expand Down
2 changes: 1 addition & 1 deletion docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
| [aws_s3_bucket_server_side_encryption_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_s3_bucket_versioning.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [local_file.terraform_backend_config](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
| [aws_iam_policy_document.prevent_unencrypted_uploads](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.replication](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.replication_sts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
Expand Down
2 changes: 1 addition & 1 deletion examples/complete/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 2.0"
version = ">= 4.9.0"
}
local = {
source = "hashicorp/local"
Expand Down
109 changes: 57 additions & 52 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ locals {

prevent_unencrypted_uploads = local.enabled && var.prevent_unencrypted_uploads

policy = local.prevent_unencrypted_uploads ? join(
"",
data.aws_iam_policy_document.prevent_unencrypted_uploads.*.json
) : ""
policy = one(data.aws_iam_policy_document.bucket_policy[*].json)

terraform_backend_config_file = format(
"%s/%s",
Expand Down Expand Up @@ -56,63 +53,71 @@ module "bucket_label" {

data "aws_region" "current" {}

data "aws_iam_policy_document" "prevent_unencrypted_uploads" {
count = local.prevent_unencrypted_uploads ? 1 : 0
data "aws_iam_policy_document" "bucket_policy" {
count = local.enabled ? 1 : 0

statement {
sid = "DenyIncorrectEncryptionHeader"
dynamic "statement" {
for_each = local.prevent_unencrypted_uploads ? ["true"] : []

effect = "Deny"
content {
sid = "DenyIncorrectEncryptionHeader"

principals {
identifiers = ["*"]
type = "AWS"
}
effect = "Deny"

actions = [
"s3:PutObject"
]
principals {
identifiers = ["*"]
type = "AWS"
}

resources = [
"${var.arn_format}:s3:::${local.bucket_name}/*",
]

condition {
test = "StringNotEquals"
variable = "s3:x-amz-server-side-encryption"
actions = [
"s3:PutObject"
]

values = [
"AES256",
"aws:kms"
resources = [
"${var.arn_format}:s3:::${local.bucket_name}/*",
]

condition {
test = "StringNotEquals"
variable = "s3:x-amz-server-side-encryption"

values = [
"AES256",
"aws:kms"
]
}
}
}

statement {
sid = "DenyUnEncryptedObjectUploads"
dynamic "statement" {
for_each = local.prevent_unencrypted_uploads ? ["true"] : []

effect = "Deny"
content {
sid = "DenyUnEncryptedObjectUploads"

principals {
identifiers = ["*"]
type = "AWS"
}
effect = "Deny"

actions = [
"s3:PutObject"
]
principals {
identifiers = ["*"]
type = "AWS"
}

resources = [
"${var.arn_format}:s3:::${local.bucket_name}/*",
]

condition {
test = "Null"
variable = "s3:x-amz-server-side-encryption"
actions = [
"s3:PutObject"
]

values = [
"true"
resources = [
"${var.arn_format}:s3:::${local.bucket_name}/*",
]

condition {
test = "Null"
variable = "s3:x-amz-server-side-encryption"

values = [
"true"
]
}
}
}

Expand Down Expand Up @@ -157,14 +162,14 @@ resource "aws_s3_bucket" "default" {
resource "aws_s3_bucket_policy" "default" {
count = local.bucket_enabled ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)
bucket = one(aws_s3_bucket.default[*].id)
policy = local.policy
}

resource "aws_s3_bucket_acl" "default" {
count = local.bucket_enabled && !var.bucket_ownership_enforced_enabled ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)
bucket = one(aws_s3_bucket.default[*].id)
acl = var.acl

# Default "bucket ownership controls" for new S3 buckets is "BucketOwnerEnforced", which disables ACLs.
Expand All @@ -175,7 +180,7 @@ resource "aws_s3_bucket_acl" "default" {
resource "aws_s3_bucket_versioning" "default" {
count = local.bucket_enabled ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)
bucket = one(aws_s3_bucket.default[*].id)

versioning_configuration {
status = "Enabled"
Expand All @@ -186,7 +191,7 @@ resource "aws_s3_bucket_versioning" "default" {
resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
count = local.bucket_enabled ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)
bucket = one(aws_s3_bucket.default[*].id)

rule {
apply_server_side_encryption_by_default {
Expand All @@ -198,7 +203,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
resource "aws_s3_bucket_logging" "default" {
count = local.bucket_enabled && length(var.logging) > 0 ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)
bucket = one(aws_s3_bucket.default[*].id)

target_bucket = var.logging[0].target_bucket
target_prefix = var.logging[0].target_prefix
Expand All @@ -207,7 +212,7 @@ resource "aws_s3_bucket_logging" "default" {
resource "aws_s3_bucket_public_access_block" "default" {
count = local.bucket_enabled && var.enable_public_access_block ? 1 : 0

bucket = one(aws_s3_bucket.default.*.id)
bucket = one(aws_s3_bucket.default[*].id)
block_public_acls = var.block_public_acls
ignore_public_acls = var.ignore_public_acls
block_public_policy = var.block_public_policy
Expand All @@ -218,7 +223,7 @@ resource "aws_s3_bucket_public_access_block" "default" {
# See https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html
resource "aws_s3_bucket_ownership_controls" "default" {
count = local.bucket_enabled ? 1 : 0
bucket = one(aws_s3_bucket.default.*.id)
bucket = one(aws_s3_bucket.default[*].id)

rule {
object_ownership = var.bucket_ownership_enforced_enabled ? "BucketOwnerEnforced" : "BucketOwnerPreferred"
Expand Down
6 changes: 3 additions & 3 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ output "s3_replication_role_arn" {
}

output "dynamodb_table_name" {
value = one(aws_dynamodb_table.with_server_side_encryption.*.name)
value = one(aws_dynamodb_table.with_server_side_encryption[*].name)
description = "DynamoDB table name"
}

output "dynamodb_table_id" {
value = one(aws_dynamodb_table.with_server_side_encryption.*.id)
value = one(aws_dynamodb_table.with_server_side_encryption[*].id)
description = "DynamoDB table ID"
}

output "dynamodb_table_arn" {
value = one(aws_dynamodb_table.with_server_side_encryption.*.arn)
value = one(aws_dynamodb_table.with_server_side_encryption[*].arn)
description = "DynamoDB table ARN"
}

Expand Down
4 changes: 2 additions & 2 deletions replication.tf
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ data "aws_iam_policy_document" "replication" {
"s3:ListBucket"
]
resources = [
join("", aws_s3_bucket.default.*.arn),
"${join("", aws_s3_bucket.default.*.arn)}/*"
join("", aws_s3_bucket.default[*].arn),
"${join("", aws_s3_bucket.default[*].arn)}/*"
]
}

Expand Down
1 change: 1 addition & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ variable "ignore_public_acls" {
}

variable "block_public_policy" {
type = bool
description = "Whether Amazon S3 should block public bucket policies for this bucket"
default = true
}
Expand Down

0 comments on commit 99453cc

Please sign in to comment.