Terraform v0.8.2でAmazon SESを設定してみた – IPアドレスフィルタおよびアクションの作成

2016.12.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、中山です。

少し前の話になりますが、Terraform v0.8.2がリリースされました。CHANGELOGはこちらです。さまざまなアップデートがあるのですが、個人的にはAmazon Simple Email Service(以下SES)への対応が嬉しいポイントの1つでした。というのもTerraformと同等の機能を提供しているCloudFormationは執筆時点(2016/12/28)でまだSESに対応してません。そのため、同じ環境を再現したい場合はマネジメントコンソールで頑張るか、AWS CLIでオレオレスクリプトを作成する必要がありました。両方共それなりにツライので、宣言的にSESの設定を記述できるのはとても重要なポイントだと思います。

2017年1月1日追記
よく確認したらこちらのエントリで紹介しているリソースはv0.7.0で導入されていました。。。申し訳ありません。v0.8.2で導入されたSES関連のリソースはこちらのエントリで紹介しています。

早速使ってみたので本エントリでご紹介したいと思います。今回は新規に導入されたリソースの内、メール受信に関わるものについてご紹介します。検証に利用したTerraformのバージョンは現在の最新版である0.8.2です。バージョンによって内容が異なる場合があります。その点ご了承ください。

対応しているリソース

メール受信関連のリソースは以下の通りです。

ご覧いただくと分かるかと思いますが、IPアドレスフィルタとアクション(とそれを定義するルールセット及びルール)に対応しています。今のところ検証用トークンの生成には非対応のようです(Externalデータソースを使えばできなくないですが)。aws_route53_recordリソースを使えばレコードセットの作成自体は可能ですが、そもそも登録するレコード自体はTerraformから作ることができないということになります。そのため、アイデンティティの作成は別途自分で実施する必要があります。

やってみる

今回はSESの各機能ごとに利用方法をご紹介します。本エントリ用に作成したコードはGitHubに置いておきました。ご自由にお使いください。

上述の通り、現在のところTerraformではアイデンティティの作成ができません。本エントリではすでに検証済みのアイデンティティが存在し、かつメール送受信のための各種レコードセットが設定済みの前提で進めます。設定方法については以下のエントリを参照してください。

IPアドレスフィルタ

はじめにメールを受信した際にIPアドレスベースの制限をするためのIPアドレスフィルタを作成してみます。関連するリソースは aws_ses_receipt_filter です。設定可能な引数は以下の通りです。

設定 内容 必須の有無
name IPアドレスフィルタ名 Yes
cidr 拒否または許可するCIDR Yes
policy CIDRを拒否または許可するかどうか Yes

今回の設定内容としては、はじめに全てのIPアドレスを 0.0.0.0/0 で拒否し、 ses_ip_range 変数で指定したSESのメール送信時に利用されているIPアドレスレンジを許可させています。SESのIPアドレスレンジについてはこちらのブログで詳しく解説されていますが、以下のコマンドで確認可能です。

$ dig +short amazonses.com TXT | grep -F 'v=spf1' | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}'
199.255.192.0/22
199.127.232.0/22
54.240.0.0/18

SESに関連するTerraformのコードは以下のようにしてみました。

  • ip-filter/ses.tf
# 全て拒否
resource "aws_ses_receipt_filter" "block" {
  name   = "block-all"
  cidr   = "0.0.0.0/0"
  policy = "Block"
}

# SES送信サーバで利用しているIPアドレスレンジを許可
resource "aws_ses_receipt_filter" "allow" {
  count  = "${length(var.ses_ip_range)}"
  name   = "allow-ses-ip-range-${count.index + 1}"
  cidr   = "${var.ses_ip_range[count.index]}"
  policy = "Allow"
}

Terraform実行後、IPアドレスフィルタの内容を確認してみます。

$ aws ses list-receipt-filters
{
    "Filters": [
        {
            "IpFilter": {
                "Policy": "Block",
                "Cidr": "0.0.0.0/0"
            },
            "Name": "block-all"
        },
        {
            "IpFilter": {
                "Policy": "Allow",
                "Cidr": "199.255.192.0/22"
            },
            "Name": "allow-ses-ip-range-1"
        },
        {
            "IpFilter": {
                "Policy": "Allow",
                "Cidr": "199.127.232.0/22"
            },
            "Name": "allow-ses-ip-range-2"
        },
        {
            "IpFilter": {
                "Policy": "Allow",
                "Cidr": "54.240.0.0/18"
            },
            "Name": "allow-ses-ip-range-3"
        }
    ]
}

正常に設定されているようです。動作確認としてSESからアイデンティティに登録されているメールアドレス宛にメールを送信してみます。

# 検証済みドメインを変数に代入
$ domain=<_YOUR_VERIFICATION_DOMAIN_>
# テストメールの送信
$ aws ses send-email \
  --from aaa@$domain \
  --to bbb@$domain \
  --subject subject \
  --text body
{
    "MessageId": "01000159433e374e-8579a7ea-4e73-428d-a34b-07688cc71b3f-000000"
}

メール送信後、正常にメールが受信できていれば成功です。続いてSES以外からメールを送信してみます。今回はGmailにしてみました。メール送信後、以下のようなエラーメッセージ付きのバウンスメールが届いたらIPアドレスが拒否されたと確認できます。

Delivery to the following recipient failed permanently:

     bbb@<_YOUR_VERIFICATION_DOMAIN_>

Technical details of permanent failure:
Google tried to deliver your message, but it was rejected by the server for the recipient domain <_YOUR_VERIFICATION_DOMAIN_> by inbound-smtp.us-east-1.amazonaws.com. [176.32.102.11].

The error that the other server returned was:
550 5.7.1 IP address blacklisted by recipient

アクション

続いてメールを受信した際に特定のルールにマッチしたらアクションを実行させるためのルールセットを作成してみます。関連リソースとその設定内容は以下の通りです。

  • aws_ses_receipt_rule_set
設定 内容 必須の有無
name ルールセット名 Yes
  • aws_ses_receipt_rule
設定 内容 必須の有無
name ルール名 Yes
rule_set_name 関連付けるルールセット名 Yes
after ルールの適用順をどのルールの後にするか No
enabled ルールを有効化するか No
recipients 検証済みメールアドレスまたはドメイン No
scan_enabled 受信したメールをウイルス/スパム用にスキャンするか No
tls_policy メール受信で利用される通信でTLSを必須にするか No
add_header_action ヘッダ追加アクションの設定(下記参照) No
bounce_action バウンスアクションの設定(下記参照) No
lambda_action Lambdaアクションの設定(下記参照) No
s3_action S3アクションの設定(下記参照) No
sns_action SNSアクションの設定(下記参照) No
stop_action ストップアクションの設定(下記参照) No
workmail_action WorkMailアクションの設定(下記参照) No
  • aws_ses_active_receipt_rule_set
設定 内容 必須の有無
name アクティブにするルールセット名 Yes

それぞれのアクションで設定可能な内容は以下の通りです。

  • add_header_action
設定 内容 必須の有無
header_name 追加するヘッダのキー Yes
header_value 追加するヘッダの値 Yes
postion ルール内でのアクションの順番 Yes
  • bounce_action
設定 内容 必須の有無
message バウンスメールに含めるメッセージ Yes
sender バウンスメールの送信元メールアドレス Yes
smtp_reply_code アクションにマッチさせるSMTP Replyコード Yes
status_code アクションにマッチさせるSMTPステータスコード No
topic_arn アクション実行時に通知するSNSトピックARN No
postion ルール内でのアクションの順番 Yes
  • lambda_action
設定 内容 必須の有無
function_arn InvokeさせるLambda関数のARN Yes
invocation_type Lambda関数の実行方法( Event または RequestResponse ) No
topic_arn アクション実行時に通知するSNSトピックARN No
postion ルール内でのアクションの順番 Yes
  • s3_action
設定 内容 必須の有無
bucket_name 受信したメールを保存するS3バケット名 Yes
kms_key_arn S3に保存する際にKMSで暗号化するか No
object_key_prefix S3に保存する際に利用するプレフィックス No
topic_arn アクション実行時に通知するSNSトピックARN No
postion ルール内でのアクションの順番 Yes
  • sns_action
設定 内容 必須の有無
topic_arn 通知するSNSトピックARN Yes
postion ルール内でのアクションの順番 Yes
  • stop_action
設定 内容 必須の有無
scope ストップアクションのスコープ Yes
topic_arn アクション実行時に通知するSNSトピックARN No
postion ルール内でのアクションの順番 Yes
  • workmail_action
設定 内容 必須の有無
organization_arn アクションと関連付けるWorkMailのARN Yes
topic_arn アクション実行時に通知するSNSトピックARN No
postion ルール内でのアクションの順番 Yes

今回は以前メール受信設定をAWS CLIでセットアップしたこちらのエントリと同じように以下の設定をしてみます。ただしコードが長くなるので全てのアクションを1つのルールに含めています。

  • テスト用ヘッダを追加
  • S3アクションで受信したメールをバケットに保存
  • SMTP Replyコード:550かつSMTPステータスコード:5.5.1の場合にバウンスアクションを実行
  • Lambda関数をInvoke
  • SNSトピックへ通知

  • rule-set/ses.tf

# ルールセットの作成
resource "aws_ses_receipt_rule_set" "ses" {
  rule_set_name = "tf-rule-set"
}

# ルールの作成
resource "aws_ses_receipt_rule" "ses" {
  name          = "tf-rule"
  rule_set_name = "${aws_ses_receipt_rule_set.ses.rule_set_name}"
  recipients    = ["${var.ses_config["verification_domain"]}"]
  enabled       = true
  scan_enabled  = true

  # ヘッダ追加アクション
  add_header_action {
    header_name  = "TestHeader"
    header_value = "Test"
    position     = 1
  }

  # バウンスアクション
  bounce_action {
    message         = "Mailbox does not exist"
    sender          = "bounce@${var.ses_config["verification_domain"]}"
    smtp_reply_code = 550
    topic_arn       = "${module.tf_sns_email.arn}"
    status_code     = "5.1.1"
    position        = 2
  }

  # Lambdaアクション
  lambda_action {
    function_arn    = "${aws_lambda_function.lambda.arn}"
    invocation_type = "${var.ses_config["invocation_type"]}"
    position        = 3
  }

  # S3アクション
  s3_action {
    bucket_name       = "${aws_s3_bucket.s3.id}"
    object_key_prefix = "${var.ses_config["object_key_prefix"]}"
    position          = 4
  }

  # SNSアクション
  sns_action {
    topic_arn = "${module.tf_sns_email.arn}"
    position  = 5
  }
}

# 作成したルールセットを有効化
resource "aws_ses_active_receipt_rule_set" "ses" {
  rule_set_name = "${aws_ses_receipt_rule_set.ses.rule_set_name}"
}

Terraform実行後以下のコマンドでルールセットの内容を確認できます。

$ aws ses describe-active-receipt-rule-set
{
    "Rules": [
        {
            "Name": "tf-rule",
            "Recipients": [
                "<_YOUR_VERIFICATION_DOMAIN_>"
            ],
            "Enabled": true,
            "ScanEnabled": true,
            "Actions": [
                {
                    "AddHeaderAction": {
                        "HeaderName": "TestHeader",
                        "HeaderValue": "Test"
                    }
                },
                {
                    "BounceAction": {
                        "Message": "Mailbox does not exist",
                        "Sender": "bounce@<_YOUR_VERIFICATION_DOMAIN_>",
                        "SmtpReplyCode": "550",
                        "TopicArn": "arn:aws:sns:us-east-1:************:<_YOUR_SNS_TOPIC_>",
                        "StatusCode": "5.1.1"
                    }
                },
                {
                    "LambdaAction": {
                        "InvocationType": "Event",
                        "FunctionArn": "arn:aws:lambda:us-east-1:************:function:<_YOUR_LAMBDA_FUNCTION_>"
                    }
                },
                {
                    "S3Action": {
                        "KmsKeyArn": "",
                        "ObjectKeyPrefix": "ses",
                        "BucketName": "<_YOUR_S3_BUCKET_>"
                    }
                },
                {
                    "SNSAction": {
                        "TopicArn": "arn:aws:sns:us-east-1:************:<_YOUR_SNS_TOPIC_>",
                        "Encoding": "UTF-8"
                    }
                }
            ],
            "TlsPolicy": "Optional"
        }
    ],
    "Metadata": {
        "CreatedTimestamp": "2016-12-28T03:13:53.180Z",
        "Name": "tf-rule-set"
    }
}

設定が完了したら動作を確認してみます。まず正常にメールが受信できた場合の動作を確認してみます。SNSトピックへのパブリッシュは今回メールを利用したのでメールが通知されたことをもって確認しました。

# ルール適用対象のドメインに対してテスト用メールの送信
$ aws ses send-email \
  --from aaa@$domain \
  --to bbb@$domain \
  --subject subject \
  --text body
{
    "MessageId": "0100015943cc6d48-28423e45-35c6-401f-a1d6-4dba77fb3308-000000"
}
# S3バケットにメールがputされていることを確認
$ aws s3 ls s3://<_YOUR_S3_BUCKET_> --recursive
2016-12-28 13:59:12        645 ses/AMAZON_SES_SETUP_NOTIFICATION
2016-12-28 14:03:18       3475 ses/j363mv5dj9c1bg22fpbgfi4ree9feepvmis2k501
# Lambda関数がInvokeされていることを確認
$ aws logs get-log-events \
  --log-group-name <_YOUR_LOG_GROUP_> \
  --log-stream-name <_YOUR_LOG_STREAM_>
{
    "nextForwardToken": "f/33069806205607788645082847192957270138853351933253713922",
    "events": [
        {
            "ingestionTime": 1482901205029,
            "timestamp": 1482901189833,
            "message": "START RequestId: 75eba679-ccba-11e6-a8e5-7fab800e61d8 Version: $LATEST\n"
        },
<snip>
# 指定したヘッダが追加されていることを確認
$ aws s3 cp s3://<_YOUR_S3_BUCKET_>/ses/j363mv5dj9c1bg22fpbgfi4ree9feepvmis2k501 - \
  | grep -F 'TestHeader'
TestHeader: Test

続いてバウンスメール発生時の動作を確認してみます。

# バウンスメールの送信
$ aws ses send-email \
  --from aaa@$domain \
  --to bounce@simulator.amazonses.com \
  --subject subject \
  --text body
{
    "MessageId": "0100015943d6bfad-cfb7cef1-1ab4-4214-bbef-5136730c6cc5-000000"
}

バウンスメール送信後、以下のような内容が記述されたバウンスメールがSNS経由で届いていれば成功です。

  "action": {
    "type": "Bounce",
    "topicArn": "arn:aws:sns:us-east-1:************:<_YOUR_SNS_TOPIC_>",
    "smtpReplyCode": "550",
    "statusCode": "5.1.1",
    "message": "Mailbox does not exist",
    "sender": "bounce@<_YOUR_VERIFICATION_DOMAIN_>"
  }

まとめ

いかがだったでしょうか。

Terraformを利用したSESのIPアドレスフィルタ及びアクションの設定方法についてご紹介しました。次回はメール送信関連のリソースについてご紹介したいと思います。

本エントリがみなさんの参考になれば幸いに思います。