Skip to content

Commit

Permalink
[ISSUE-14] Restrict access to AWS S3 content by an origin access iden…
Browse files Browse the repository at this point in the history
…tity (#17)

* [ISSUE-14] Restrict access to AWS S3 content by an origin access identity

* [ISSUE-14] Restrict access to AWS S3 content by an origin access identity

* [ISSUE-14] Update CF S3 domain names

* Removing redirection bucket

* Removing redirection bucket

* Removing redirection bucket

* [ISSUE-14] Add origin access identity

* [ISSUE-14] Add provider

* [ISSUE-14] Point www DNS record to the CF distribution

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Remove policy to log bucket

* [ISSUE-14] Add policy to log bucket

* [ISSUE-14] Fix build
  • Loading branch information
mfcaro authored Jul 23, 2021
1 parent 536eccd commit 7caeb6e
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 414 deletions.
37 changes: 5 additions & 32 deletions README.md

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion examples/test/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module "test_website" {
}

website_domain_name = "test.com"
enable_cloudfront = true
create_acm_certificate = true

aws_accounts_with_read_view_log_bucket = ["mock_account"]
}
45 changes: 35 additions & 10 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ terraform {
}
}


#------------------------------------------------------------------------------
# Locals
#------------------------------------------------------------------------------
Expand All @@ -24,7 +23,9 @@ locals {
#------------------------------------------------------------------------------
# S3 Bucket for logs
#------------------------------------------------------------------------------
resource "aws_s3_bucket" "log_bucket" {
# tfsec issues ignored
# - AWS017: The bucket objects could be read if compromised
resource "aws_s3_bucket" "log_bucket" { # tfsec:ignore:AWS017
provider = aws.main

bucket = "${var.name_prefix}-log-bucket"
Expand All @@ -35,19 +36,41 @@ resource "aws_s3_bucket" "log_bucket" {
mfa_delete = var.log_bucket_versioning_mfa_delete
}

server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}

tags = merge({
Name = "${var.name_prefix}-logs"
}, var.tags)
}

data "aws_iam_policy_document" "log_bucket_access_policy" {
provider = aws.main

statement {
sid = "Allow access to logs bucket to current account"

actions = [
"s3:List*",
"s3:Get*",
]

resources = [
aws_s3_bucket.log_bucket.arn,
"${aws_s3_bucket.log_bucket.arn}/*",
]

principals {
type = "AWS"
identifiers = formatlist("arn:aws:iam::%s:root", var.aws_accounts_with_read_view_log_bucket)
}
}
}

resource "aws_s3_bucket_policy" "log_bucket_access_policy" {
provider = aws.main

bucket = aws_s3_bucket.log_bucket.id
policy = data.aws_iam_policy_document.log_bucket_access_policy.json
}

resource "aws_s3_bucket_public_access_block" "log_bucket_public_access_block" {
provider = aws.main

Expand Down Expand Up @@ -119,3 +142,5 @@ resource "aws_acm_certificate_validation" "cert_validation" {
certificate_arn = aws_acm_certificate.cert[0].arn
validation_record_fqdns = [for record in aws_route53_record.acm_certificate_validation_records : record.fqdn]
}


143 changes: 16 additions & 127 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -46,178 +46,67 @@ output "website_bucket_website_domain" {
value = aws_s3_bucket.website.website_domain
}

#------------------------------------------------------------------------------
# S3 Bucket for redirection from URL with www to the one without www
#------------------------------------------------------------------------------
output "www_website_bucket_id" {
description = "The name of the bucket."
value = aws_s3_bucket.www_website.id
}

output "www_website_bucket_arn" {
description = "The ARN of the bucket. Will be of format arn:aws:s3:::bucketname."
value = aws_s3_bucket.www_website.arn
}

output "www_website_bucket_domain_name" {
description = "The bucket domain name. Will be of format bucketname.s3.amazonaws.com."
value = aws_s3_bucket.www_website.bucket_domain_name
}

output "www_website_bucket_regional_domain_name" {
description = "The bucket region-specific domain name. The bucket domain name including the region name, please refer here for format. Note: The AWS CloudFront allows specifying S3 region-specific endpoint when creating S3 origin, it will prevent redirect issues from CloudFront to S3 Origin URL."
value = aws_s3_bucket.www_website.bucket_regional_domain_name
}

output "www_website_bucket_hosted_zone_id" {
description = "The Route 53 Hosted Zone ID for this bucket's region."
value = aws_s3_bucket.www_website.hosted_zone_id
}

output "www_website_bucket_region" {
description = "The AWS region this bucket resides in."
value = aws_s3_bucket.www_website.region
}

output "www_website_bucket_tags_all" {
description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block."
value = aws_s3_bucket.www_website.tags_all
}

output "www_website_bucket_website_endpoint" {
description = "The website endpoint, if the bucket is configured with a website. If not, this will be an empty string."
value = aws_s3_bucket.www_website.website_endpoint
}

output "www_website_bucket_website_domain" {
description = "The domain of the website endpoint, if the bucket is configured with a website. If not, this will be an empty string. This is used to create Route 53 alias records."
value = aws_s3_bucket.www_website.website_domain
}

#------------------------------------------------------------------------------
# Cloudfront for S3 Bucket Website
#------------------------------------------------------------------------------
output "cloudfront_website_id" {
description = "The identifier for the distribution. For example: EDFDVBD632BHDS5."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].id : null
value = aws_cloudfront_distribution.website.id
}

output "cloudfront_website_arn" {
description = "The ARN (Amazon Resource Name) for the distribution. For example: arn:aws:cloudfront::123456789012:distribution/EDFDVBD632BHDS5, where 123456789012 is your AWS account ID."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].arn : null
value = aws_cloudfront_distribution.website.arn
}

output "cloudfront_website_caller_reference" {
description = "Internal value used by CloudFront to allow future updates to the distribution configuration."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].caller_reference : null
value = aws_cloudfront_distribution.website.caller_reference
}

output "cloudfront_website_status" {
description = "The current status of the distribution. Deployed if the distribution's information is fully propagated throughout the Amazon CloudFront system."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].status : null
value = aws_cloudfront_distribution.website.status
}

output "cloudfront_website_tags_all" {
description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].tags_all : null
value = aws_cloudfront_distribution.website.tags_all
}

output "cloudfront_website_trusted_key_groups" {
description = "List of nested attributes for active trusted key groups, if the distribution is set up to serve private content with signed URLs"
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].trusted_key_groups : null
value = aws_cloudfront_distribution.website.trusted_key_groups
}

output "cloudfront_website_trusted_signers" {
description = "List of nested attributes for active trusted signers, if the distribution is set up to serve private content with signed URLs"
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].trusted_signers : null
value = aws_cloudfront_distribution.website.trusted_signers
}

output "cloudfront_website_domain_name" {
description = "The domain name corresponding to the distribution. For example: d604721fxaaqy9.cloudfront.net."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].domain_name : null
value = aws_cloudfront_distribution.website.domain_name
}

output "cloudfront_website_last_modified_time" {
description = "The date and time the distribution was last modified."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].last_modified_time : null
value = aws_cloudfront_distribution.website.last_modified_time
}

output "cloudfront_website_in_progress_validation_batches" {
description = "The number of invalidation batches currently in progress."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].in_progress_validation_batches : null
value = aws_cloudfront_distribution.website.in_progress_validation_batches
}

output "cloudfront_website_etag" {
description = "The current version of the distribution's information. For example: E2QWRUHAPOMQZL."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].etag : null
value = aws_cloudfront_distribution.website.etag
}

output "cloudfront_website_hosted_zone_id" {
description = "The CloudFront Route 53 zone ID that can be used to route an Alias Resource Record Set to. This attribute is simply an alias for the zone ID Z2FDTNDATAQYW2."
value = var.enable_cloudfront ? aws_cloudfront_distribution.website[0].hosted_zone_id : null
}

#------------------------------------------------------------------------------
# Cloudfront for S3 Bucket for WWW Website Redirection
#------------------------------------------------------------------------------
output "cloudfront_www_website_id" {
description = "The identifier for the distribution. For example: EDFDVBD632BHDS5."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].id : null
}

output "cloudfront_www_website_arn" {
description = "The ARN (Amazon Resource Name) for the distribution. For example: arn:aws:cloudfront::123456789012:distribution/EDFDVBD632BHDS5, where 123456789012 is your AWS account ID."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].arn : null
}

output "cloudfront_www_website_caller_reference" {
description = "Internal value used by CloudFront to allow future updates to the distribution configuration."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].caller_reference : null
}

output "cloudfront_www_website_status" {
description = "The current status of the distribution. Deployed if the distribution's information is fully propagated throughout the Amazon CloudFront system."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].status : null
}

output "cloudfront_www_website_tags_all" {
description = "A map of tags assigned to the resource, including those inherited from the provider default_tags configuration block."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].tags_all : null
}

output "cloudfront_www_website_trusted_key_groups" {
description = "List of nested attributes for active trusted key groups, if the distribution is set up to serve private content with signed URLs"
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].trusted_key_groups : null
}

output "cloudfront_www_website_trusted_signers" {
description = "List of nested attributes for active trusted signers, if the distribution is set up to serve private content with signed URLs"
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].trusted_signers : null
}

output "cloudfront_www_website_domain_name" {
description = "The domain name corresponding to the distribution. For example: d604721fxaaqy9.cloudfront.net."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].domain_name : null
}

output "cloudfront_www_website_last_modified_time" {
description = "The date and time the distribution was last modified."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].last_modified_time : null
}

output "cloudfront_www_website_in_progress_validation_batches" {
description = "The number of invalidation batches currently in progress."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].in_progress_validation_batches : null
}

output "cloudfront_www_website_etag" {
description = "The current version of the distribution's information. For example: E2QWRUHAPOMQZL."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].etag : null
}

output "cloudfront_www_website_hosted_zone_id" {
description = "The CloudFront Route 53 zone ID that can be used to route an Alias Resource Record Set to. This attribute is simply an alias for the zone ID Z2FDTNDATAQYW2."
value = var.enable_cloudfront ? aws_cloudfront_distribution.www_website[0].hosted_zone_id : null
value = aws_cloudfront_distribution.website.hosted_zone_id
}

#------------------------------------------------------------------------------
Expand All @@ -240,22 +129,22 @@ output "hosted_zone_tags_all" {

output "route_53_record_website_name" {
description = "The name of the record."
value = var.enable_cloudfront ? aws_route53_record.website_cloudfront_record[0].name : aws_route53_record.website_s3_record[0].name
value = aws_route53_record.website_cloudfront_record.name
}

output "route_53_record_website_fqdn" {
description = "FQDN built using the zone domain and name."
value = var.enable_cloudfront ? aws_route53_record.website_cloudfront_record[0].fqdn : aws_route53_record.website_s3_record[0].fqdn
value = aws_route53_record.website_cloudfront_record.fqdn
}

output "route_53_record_www_website_name" {
description = "The name of the record."
value = var.enable_cloudfront ? aws_route53_record.www_website_cloudfront_record[0].name : aws_route53_record.www_website_s3_record[0].name
value = aws_route53_record.www_website_record.name
}

output "route_53_record_www_website_fqdn" {
description = "FQDN built using the zone domain and name."
value = var.enable_cloudfront ? aws_route53_record.www_website_cloudfront_record[0].fqdn : aws_route53_record.www_website_s3_record[0].fqdn
value = aws_route53_record.www_website_record.fqdn
}

#------------------------------------------------------------------------------
Expand Down
14 changes: 6 additions & 8 deletions templates/s3_website_bucket_policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Sid": "OAIAccessOnly",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::${bucket_name}/*"
]
"Principal": {
"AWS": "${cf_oai_arn}"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::${bucket_name}/*"
}
]
}
30 changes: 6 additions & 24 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ variable "log_bucket_versioning_mfa_delete" {
default = false
}

variable "aws_accounts_with_read_view_log_bucket" {
description = "List of AWS accounts with read permissions to log bucket"
type = list(string)
default = []
}

#------------------------------------------------------------------------------
# Website
#------------------------------------------------------------------------------
Expand Down Expand Up @@ -130,24 +136,12 @@ variable "www_website_versioning_mfa_delete" {
#------------------------------------------------------------------------------
# Cloudfront
#------------------------------------------------------------------------------
variable "enable_cloudfront" {
description = "Indicates if Cloudfront should be created or not"
type = bool
default = true
}

variable "comment_for_cloudfront_website" {
description = "Comment for the Website CloudFront Distribution"
type = string
default = ""
}

variable "comment_for_cloudfront_www_website" {
description = "Comment for the WWW Website CloudFront Distribution"
type = string
default = ""
}

variable "cloudfront_viewer_protocol_policy" {
description = "Use this element to specify the protocol that users can use to access the files in the origin specified by TargetOriginId when a request matches the path pattern in PathPattern. One of allow-all, https-only, or redirect-to-https. Defautls to redirect-to-https"
type = string
Expand Down Expand Up @@ -202,24 +196,12 @@ variable "cloudfront_website_retain_on_delete" {
default = false
}

variable "cloudfront_www_website_retain_on_delete" {
description = "(Optional) - Disables the distribution instead of deleting it when destroying the resource through Terraform. If this is set, the distribution needs to be deleted manually afterwards. Defaults to false."
type = bool
default = false
}

variable "cloudfront_website_wait_for_deployment" {
description = "(Optional) - If enabled, the resource will wait for the distribution status to change from InProgress to Deployed. Setting this tofalse will skip the process. Defaults to true."
type = bool
default = true
}

variable "cloudfront_www_website_wait_for_deployment" {
description = "(Optional) - If enabled, the resource will wait for the distribution status to change from InProgress to Deployed. Setting this tofalse will skip the process. Defaults to true."
type = bool
default = true
}

#------------------------------------------------------------------------------
# ACM Certificate
#------------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit 7caeb6e

Please sign in to comment.