Skip to content

Commit

Permalink
Add MTA-STS module and use it in aws-management (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
VJftw authored May 2, 2024
1 parent d2bd743 commit d10d589
Show file tree
Hide file tree
Showing 12 changed files with 325 additions and 0 deletions.
19 changes: 19 additions & 0 deletions deployment/management/mta-sts/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
subinclude("//build/defs:terraform")

terraform_root(
name = "aws",
srcs = [
"main.tf",
],
account_auths = {
"//accounts/aws:vjp-management_auth": {
"branches": {
"main": "administrator",
},
"pull_request": "reader",
},
},
modules = [
"//modules/mta-sts/aws:aws",
],
)
16 changes: 16 additions & 0 deletions deployment/management/mta-sts/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
provider "aws" {
profile = "vjp-management"
region = "us-east-1"
}

module "mta-sts" {
source = "//modules/mta-sts/aws:aws"

providers = {
aws = aws
}

domain = "vjpatel.me"
tls_report_email_address = "smtp-tls-reports@vjpatel.me"
mta_sts_id = "1714671831024"
}
7 changes: 7 additions & 0 deletions modules/mta-sts/aws/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
subinclude("//build/defs:terraform")

terraform_module(
name = "aws",
srcs = glob(["*"]),
visibility = ["//deployment/management/..."],
)
13 changes: 13 additions & 0 deletions modules/mta-sts/aws/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

data "aws_route53_zone" "this" {
name = var.domain
private_zone = false
}
8 changes: 8 additions & 0 deletions modules/mta-sts/aws/mta-sts.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: STSv1
mode: testing
mx: aspmx.l.google.com
mx: alt3.aspmx.l.google.com
mx: alt4.aspmx.l.google.com
mx: alt1.aspmx.l.google.com
mx: alt2.aspmx.l.google.com
max_age: 604800
11 changes: 11 additions & 0 deletions modules/mta-sts/aws/mta_sts.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resource "aws_route53_record" "mta_sts" {
zone_id = data.aws_route53_zone.this.zone_id

name = "_mta-sts.${var.domain}"
type = "TXT"
ttl = "300"

records = [
"v=STSv1;id=${var.mta_sts_id};",
]
}
Empty file added modules/mta-sts/aws/outputs.tf
Empty file.
119 changes: 119 additions & 0 deletions modules/mta-sts/aws/policy_cloudront.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
data "aws_iam_policy_document" "this_origin" {
statement {
sid = "S3GetObjectForCloudFront"

actions = ["s3:GetObject"]
resources = ["${aws_s3_bucket.this.arn}/*"]

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

resource "aws_cloudfront_distribution" "this" {
enabled = true

aliases = [
"mta-sts.${var.domain}",
]

is_ipv6_enabled = true
comment = ""
default_root_object = "index.html"
price_class = "PriceClass_All"

depends_on = [aws_s3_bucket.this]

origin {
origin_id = "s3_website"
domain_name = aws_s3_bucket_website_configuration.this.website_endpoint

custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "http-only"
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
}
}

default_cache_behavior {
compress = true
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD", "OPTIONS"]
target_origin_id = "s3_website"

viewer_protocol_policy = "redirect-to-https"

cache_policy_id = aws_cloudfront_cache_policy.this.id
response_headers_policy_id = aws_cloudfront_response_headers_policy.this.id
}

viewer_certificate {
acm_certificate_arn = aws_acm_certificate.this.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
cloudfront_default_certificate = false
}

restrictions {
geo_restriction {
restriction_type = "none"
}
}

}

resource "aws_cloudfront_cache_policy" "this" {
name = "mta-sts"
comment = ""
default_ttl = 60
max_ttl = 3600
min_ttl = 1
parameters_in_cache_key_and_forwarded_to_origin {
enable_accept_encoding_brotli = true
enable_accept_encoding_gzip = true
cookies_config {
cookie_behavior = "none"
}
headers_config {
header_behavior = "none"
}
query_strings_config {
query_string_behavior = "none"
}
}
}

resource "aws_cloudfront_response_headers_policy" "this" {
name = "mta-sts"

security_headers_config {
content_security_policy {
override = true
content_security_policy = "default-src 'none';"
}

content_type_options {
override = true
}

frame_options {
override = true
frame_option = "DENY"
}

referrer_policy {
override = true
referrer_policy = "same-origin"
}

xss_protection {
override = true
protection = true
mode_block = true
}
}

}
77 changes: 77 additions & 0 deletions modules/mta-sts/aws/policy_s3.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
locals {
policy_domain = "mta-sts.${var.domain}"
policy_url = "${local.policy_domain}/.well-known/mta-sts.txt"
}

resource "aws_s3_bucket" "this" {
bucket = "${replace(var.domain, ".", "")}-mta-sts"

force_destroy = true
}

resource "aws_s3_object" "policy_txt" {
bucket = aws_s3_bucket.this.bucket
key = ".well-known/mta-sts.txt"
source = "${path.module}/mta-sts.txt"

etag = filemd5("${path.module}/mta-sts.txt")
}

resource "aws_s3_bucket_acl" "this" {
bucket = aws_s3_bucket.this.id
acl = "public-read"
}

resource "aws_s3_bucket_website_configuration" "this" {
bucket = aws_s3_bucket.this.id

index_document {
suffix = "index.html"
}

error_document {
key = "error.html"
}
}

resource "aws_s3_bucket_cors_configuration" "this" {
bucket = aws_s3_bucket.this.id

cors_rule {
allowed_headers = ["*"]
allowed_methods = ["GET"]
allowed_origins = ["*"]
expose_headers = ["ETag"]
max_age_seconds = 3600
}
}

resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

resource "aws_s3_bucket_policy" "this" {
bucket = aws_s3_bucket.this.id
policy = data.aws_iam_policy_document.this.json
}

data "aws_iam_policy_document" "this" {
statement {
sid = "AllowAllRead"
actions = ["s3:GetObject"]

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

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

}
30 changes: 30 additions & 0 deletions modules/mta-sts/aws/policy_tls.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
resource "aws_acm_certificate" "this" {
domain_name = "mta-sts.${var.domain}"
validation_method = "DNS"

lifecycle {
create_before_destroy = true
}
}

resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.this.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}

allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = data.aws_route53_zone.this.zone_id
}

resource "aws_acm_certificate_validation" "this" {
certificate_arn = aws_acm_certificate.this.arn
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}
11 changes: 11 additions & 0 deletions modules/mta-sts/aws/smtp_tls.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resource "aws_route53_record" "smtp_tls" {
zone_id = data.aws_route53_zone.this.zone_id

name = "_smtp._tls.${var.domain}"
type = "TXT"
ttl = "300"

records = [
"v=TLSRPTv1;rua=mailto:${var.tls_report_email_address};",
]
}
14 changes: 14 additions & 0 deletions modules/mta-sts/aws/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
variable domain {
type = string
description = "The domain to configure MTA-STS for."
}

variable tls_report_email_address {
type = string
description = "The email address to send SMTP TLS reports to."
}

variable mta_sts_id {
type = string
description = "The id for the text record in MTA-STS."
}

0 comments on commit d10d589

Please sign in to comment.