| new file mode 100644 |
| index 000000000..60d92bec4 |
| --- /dev/null |
| +++ b/infra/flagship/mail.tf |
| @@ -0,0 +1,115 @@ |
| +# Outbound email for flagship tenants โ multi-tenant SES (platform spec |
| +# ยง7 svc/mail, v1 shape: per-app identities + configuration sets + scoped |
| +# SMTP users; the fronting service comes later). This account has |
| +# production SES access โ the reason it was adopted as the flagship |
| +# account (spec ยง12). |
| +# |
| +# First tenant: Kass. Its domain identity re-verifies here with NEW DKIM |
| +# tokens; the old account's DKIM records coexist in DNSimple until the |
| +# old account winds down. |
| + |
| +terraform { |
| + required_providers { |
| + dnsimple = { |
| + source = "dnsimple/dnsimple" |
| + version = "~> 1.5" |
| + } |
| + } |
| +} |
| + |
| +# Reads DNSIMPLE_TOKEN from the environment; never written to a file here. |
| +provider "dnsimple" { |
| + account = "74881" |
| +} |
| + |
| +resource "aws_ses_domain_identity" "kass" { |
| + domain = "kass.training" |
| +} |
| + |
| +resource "aws_ses_domain_dkim" "kass" { |
| + domain = aws_ses_domain_identity.kass.domain |
| +} |
| + |
| +resource "dnsimple_zone_record" "kass_dkim" { |
| + count = 3 |
| + zone_name = "kass.training" |
| + name = "${aws_ses_domain_dkim.kass.dkim_tokens[count.index]}._domainkey" |
| + type = "CNAME" |
| + value = "${aws_ses_domain_dkim.kass.dkim_tokens[count.index]}.dkim.amazonses.com" |
| + ttl = 3600 |
| +} |
| + |
| +resource "aws_sns_topic" "deliverability" { |
| + name = "carlos-ses-deliverability" |
| +} |
| + |
| +resource "aws_sns_topic_subscription" "deliverability_email" { |
| + topic_arn = aws_sns_topic.deliverability.arn |
| + protocol = "email" |
| + endpoint = "paul@rslw.com" |
| +} |
| + |
| +resource "aws_sns_topic_policy" "ses" { |
| + arn = aws_sns_topic.deliverability.arn |
| + policy = jsonencode({ |
| + Version = "2012-10-17" |
| + Statement = [{ |
| + Effect = "Allow" |
| + Principal = { Service = "ses.amazonaws.com" } |
| + Action = "sns:Publish" |
| + Resource = aws_sns_topic.deliverability.arn |
| + Condition = { StringEquals = { "AWS:SourceAccount" = local.account } } |
| + }] |
| + }) |
| +} |
| + |
| +# Per-tenant configuration set: reputation and suppression isolated per |
| +# app (the svc/mail promise, delivered by SES config rather than a |
| +# service front for now). |
| +resource "aws_sesv2_configuration_set" "kass_signin" { |
| + configuration_set_name = "kass-signin" |
| + delivery_options { tls_policy = "REQUIRE" } |
| + reputation_options { reputation_metrics_enabled = true } |
| + suppression_options { suppressed_reasons = ["BOUNCE", "COMPLAINT"] } |
| +} |
| + |
| +resource "aws_sesv2_configuration_set_event_destination" "kass_signin_sns" { |
| + configuration_set_name = aws_sesv2_configuration_set.kass_signin.configuration_set_name |
| + event_destination_name = "deliverability" |
| + event_destination { |
| + enabled = true |
| + matching_event_types = ["BOUNCE", "COMPLAINT", "REJECT", "RENDERING_FAILURE"] |
| + sns_destination { topic_arn = aws_sns_topic.deliverability.arn } |
| + } |
| +} |
| + |
| +# Send-only SMTP user for the Kass tenant. |
| +resource "aws_iam_user" "kass_smtp" { |
| + name = "carlos-kass-smtp" |
| +} |
| + |
| +resource "aws_iam_user_policy" "kass_smtp_send" { |
| + name = "send-only" |
| + user = aws_iam_user.kass_smtp.name |
| + policy = jsonencode({ |
| + Version = "2012-10-17" |
| + Statement = [{ |
| + Effect = "Allow" |
| + Action = ["ses:SendRawEmail", "ses:SendEmail"] |
| + Resource = "*" |
| + }] |
| + }) |
| +} |
| + |
| +resource "aws_iam_access_key" "kass_smtp" { |
| + user = aws_iam_user.kass_smtp.name |
| +} |
| + |
| +output "kass_smtp_user" { |
| + value = aws_iam_access_key.kass_smtp.id |
| +} |
| + |
| +output "kass_smtp_password" { |
| + value = aws_iam_access_key.kass_smtp.ses_smtp_password_v4 |
| + sensitive = true |
| +} |