Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,39 @@ This consists of using CloudFront/S3 with a Custom Domain to host the MTA-STS po
## How to use this Module

This module assumes AWS Account with access to Route53, CloudFront, S3, and ACM, which also hosts the DNS (in Route53) for the domain you wish to deploy MTA-STS/TLS-RPT.
The providers are defined here to allow resources to be provisioned in both `us-east-1` and a local region (`eu-west-2` in this example). This method also allows additional providers to be defined for additional AWS accounts / profiles, if required.
Note some variables (such as `cf_waf_web_acl`, `cf_price_class`, `mode`, `tags`, etc.) are optional. `See variables.tf` for defaults.

```terraform
provider "aws" {
alias = "useast1"
region = "us-east-1"
shared_config_files = ["___/.aws/conf"]
shared_credentials_files = ["___/.aws/creds"]
profile = "myprofile"
}

provider "aws" {
alias = "myregion"
region = "eu-west-2"
shared_config_files = ["___/.aws/conf"]
shared_credentials_files = ["___/.aws/creds"]
profile = "myprofile"
}

module "mtastspolicy_examplecom" {
source = "github.com/ukncsc/terraform-aws-mtasts"
zone_id = "Z00AAAAAAA0A0A"
domain = "example.com"
mx = ["mail.example.com"]
mode = "testing"
reporting_email = "tlsreporting@example.com"
cf_price_class = "PriceClass_200"
cf_waf_web_acl = "arn:aws:waf___"
tags = { "Terraform_source_repo" = "my-terraform-mta-sts-repo" }
providers = {
aws.useast1 = aws.useast1
aws.account = aws.myregion
}

}
```
112 changes: 68 additions & 44 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
data "aws_caller_identity" "current" {}
data "aws_caller_identity" "current" {
provider = aws.account
}

locals {
bucketname = "mta-sts.${data.aws_caller_identity.current.account_id}.${var.domain}"
policydomain = "mta-sts.${var.domain}"
policyhash = md5(format("%s%s%s", join("", var.mx), var.mode, var.max_age))
bucketname = "${data.aws_caller_identity.current.account_id}.${var.domain}"
}

provider "aws" {
alias = "useast1"
region = "us-east-1"
s3_origin_id = "myS3Origin"
tags = merge(
{
"Service" = "MTA-STS"
"Domain" = var.domain
},
var.tags
)
}

resource "aws_acm_certificate" "cert" {
domain_name = local.policydomain
validation_method = "DNS"
tags = var.tags
tags = local.tags
provider = aws.useast1
}

data "aws_route53_zone" "zone" {
zone_id = var.zone_id
name = var.domain
provider = aws.useast1
}

resource "aws_route53_record" "cert_validation" {
name = tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_name
type = tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_type
zone_id = data.aws_route53_zone.zone.id
records = [tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_value]
ttl = 60
name = tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_name
type = tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_type
zone_id = data.aws_route53_zone.zone.id
records = [tolist(aws_acm_certificate.cert.domain_validation_options)[0].resource_record_value]
ttl = 60
provider = aws.useast1
}

resource "aws_acm_certificate_validation" "cert" {
Expand All @@ -37,26 +44,40 @@ resource "aws_acm_certificate_validation" "cert" {
}

resource "aws_s3_bucket" "policybucket" {
bucket = local.bucketname
acl = "private"
bucket = local.bucketname
tags = local.tags
provider = aws.account
}

resource "aws_s3_bucket_object" "mtastspolicyfile" {
resource "aws_s3_bucket_acl" "policybucket_acl" {
bucket = aws_s3_bucket.policybucket.id
acl = "private"
provider = aws.account
}

resource "aws_s3_object" "mtastspolicyfile" {
key = ".well-known/mta-sts.txt"
bucket = aws_s3_bucket.policybucket.id
content = <<EOF
version: STSv1
mode: ${var.mode}
${join("", formatlist("mx: %s\n", var.mx))}max_age: ${var.max_age}
EOF
content = templatefile("${path.module}/mta-sts.templatefile",
{
max_age = var.max_age
mode = var.mode
mx_lines = join("\n", formatlist("mx: %s", var.mx))
}
)
content_type = "text/plain"
}

locals {
s3_origin_id = "myS3Origin"
tags = local.tags
provider = aws.account
}

resource "aws_cloudfront_distribution" "s3_distribution" {
aliases = [local.policydomain]
enabled = true
price_class = var.cf_price_class
web_acl_id = var.cf_waf_web_acl
tags = local.tags
provider = aws.account

origin {
domain_name = aws_s3_bucket.policybucket.bucket_regional_domain_name
origin_id = local.s3_origin_id
Expand All @@ -65,9 +86,6 @@ resource "aws_cloudfront_distribution" "s3_distribution" {
origin_access_identity = aws_cloudfront_origin_access_identity.policybucketoai.cloudfront_access_identity_path
}
}
enabled = true

aliases = [local.policydomain]

default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
Expand Down Expand Up @@ -102,10 +120,12 @@ resource "aws_cloudfront_distribution" "s3_distribution" {
}

resource "aws_cloudfront_origin_access_identity" "policybucketoai" {
comment = "OAI for MTA-STS policy bucket (${var.domain})"
comment = "OAI for MTA-STS policy bucket (${var.domain})"
provider = aws.account
}

data "aws_iam_policy_document" "s3_policy" {
provider = aws.account
statement {
actions = ["s3:GetObject"]
resources = ["${aws_s3_bucket.policybucket.arn}/*"]
Expand All @@ -118,14 +138,16 @@ data "aws_iam_policy_document" "s3_policy" {
}

resource "aws_s3_bucket_policy" "policybucketpolicy" {
bucket = aws_s3_bucket.policybucket.id
policy = data.aws_iam_policy_document.s3_policy.json
bucket = aws_s3_bucket.policybucket.id
policy = data.aws_iam_policy_document.s3_policy.json
provider = aws.account
}

resource "aws_route53_record" "cloudfrontalias" {
name = local.policydomain
type = "A"
zone_id = data.aws_route53_zone.zone.id
name = local.policydomain
type = "A"
zone_id = data.aws_route53_zone.zone.id
provider = aws.account

alias {
evaluate_target_health = true
Expand All @@ -135,22 +157,24 @@ resource "aws_route53_record" "cloudfrontalias" {
}

resource "aws_route53_record" "smtptlsreporting" {
zone_id = data.aws_route53_zone.zone.id
name = "_smtp._tls.${var.domain}"
type = "TXT"
ttl = "300"
count = length(var.reporting_email) > 0 ? 1 : 0
zone_id = data.aws_route53_zone.zone.id
name = "_smtp._tls.${var.domain}"
type = "TXT"
ttl = "300"
count = length(var.reporting_email) > 0 ? 1 : 0
provider = aws.account

records = [
"v=TLSRPTv1;rua=mailto:${var.reporting_email}",
]
}

resource "aws_route53_record" "mtastspolicydns" {
zone_id = data.aws_route53_zone.zone.id
name = "_mta-sts.${var.domain}"
type = "TXT"
ttl = "300"
zone_id = data.aws_route53_zone.zone.id
name = "_mta-sts.${var.domain}"
type = "TXT"
ttl = "300"
provider = aws.account

records = [
"v=STSv1; id=${local.policyhash}",
Expand Down
4 changes: 4 additions & 0 deletions mta-sts.templatefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
version: STSv1
mode: ${mode}
${mx_lines}
max_age: ${max_age}
11 changes: 9 additions & 2 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
variable "zone_id" {
variable "cf_price_class" {
type = string
description = "Route53 zone hosting the domain MTA-STS/TLS-RPT is being deployed for."
default = "PriceClass_100"
description = "The price class for the MTA STS CloudFront distribution. Options: PriceClass_100 (North America and Europe), PriceClass_200 (North America, Europe, Asia, Middle East, and Africa) or PriceClass_All (all edge locations)."
}

variable "cf_waf_web_acl" {
type = string
default = null
description = "AWS WAF web ACL to associate with the CloudFront distribution."
}

variable "domain" {
Expand Down
1 change: 1 addition & 0 deletions versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
configuration_aliases = [ aws.account, aws.useast1 ]
}
}
}