【AWS】エンタープライズレベル SESマルチテナント設計

## 1. はじめに
(株)電通総研 XI本部 羽根田と申します。インフラ構築運用サービス「DMAP」を運営しています。
Amazon Simple Email Service (SES) は、高いスケーラビリティと信頼性を持つクラウド型メール配信サービスで、さまざまなアプリケーションやサービスから簡単にメールを送信できます。本記事では、単一のAWSアカウント内でSESをマルチテナント環境として利用するための設計についてご紹介します。SESの基本的な使い方については、公式ドキュメントや他のリソースをご参照ください。

特に各テナントの送信品質を分離し、他のテナントに悪影響を与えない仕組みとして、**レピュテーション管理のテナント間分離**にポイントをおいています。

 SESにおけるマルチテナント利用のイメージ図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3336189/919012af-a772-f47c-dc1a-5445e9e2668c.png)


## 2. マルチテナントSESの要件
マルチテナント向けのSES環境を構築するにあたってはどんな要件が考えられるでしょうか。以下に、SESをマルチテナント環境で利用する際に必要となる具体的な要件をまとめました。
なお、メールセキュリティのように一般的に必要な要件については割愛します。

### 2.1 テナント分離を実現するための要件
SESをマルチテナント環境で利用する際には、以下のようなテナント分離を実現するための要件が考えられます。

- **設定の分離**: 各テナントのSPF、DKIM、DMARC、専用IPなどのメール設定を個別に管理
- **レピュテーションの分離**: テナントごとに送信品質を分離し、他のテナントへの影響を防止
- **送信制限の分離**: テナントごとにバウンス率や苦情率が閾値を超えた場合に、個別に送信制限の警告と適用を実施
- **メール分析**: テナント毎にメール送信のログを参照し、分析を行い、改善策を策定

### 2.2 AWSアカウント全体でのSES要件
マルチテナント環境でSESを利用する際には、AWSアカウント全体のSES管理に関する要件も考慮する必要があります。

- **サプレッションリストの管理**: メールアドレスへの送信を抑止するリストを正常に維持・管理
- **バウンス率と苦情率の監視**: CloudWatchアラームを利用して、メール配信の健全性を維持
- **クォータの監視と管理**: SES送信クォータの利用状況を監視し、必要に応じた調整を実施

本記事では、これらの要件を実現するための具体的な設計内容について詳しく解説していきます

## 3. SESにおけるテナント分離の設計と実装
SESをマルチテナント環境で利用する際には、テナントごとに設定を分離し、送信品質を管理する仕組みが必要です。以下では、SESにおけるテナント分離設計と実装についてご紹介します。

先に全体構成図を示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3336189/07735506-12f2-ff76-33e4-00b2402686d0.png)

ポイントは以下になります。

* 検証済みIDと設定セットでテナント毎の設定を行い、個別のメトリクス・ログ管理、バウンス・苦情の管理を実現します（図のNo1～No4）
* AWSアカウント単位でのサプレッションリスト管理、バウンス・苦情の管理、クォータ管理を実施します（図のNo5～No7）

以降では、「2. マルチテナントSESの要件」で記載した要件ごとに設計と実装をご紹介していきます。

### 3.1. 設定の分離
まずは、テナント毎の設定の分離が必要になります。SESでは、設定セットを利用することで、複数の設定を管理することができます。設定セットを利用することで、テナントごとにSPF、DKIM、DMARC、専用IP、評価メトリクスなどの設定を個別に管理することが可能です。
検証済みID（Header From）と設定セットの関係はN:1の関係になりますので、1つのテナントが複数のドメインを利用する場合も、1つの設定セットで管理することができます。

テナント毎に何が設定可能かを示した構成図として、ここではテナント1に2つの検証済みID（ドメイン）と1つの設定セットを割り当てた場合の構成図になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3336189/784eb504-4104-3625-65d9-43cc43359259.png)

テナント別の設定として、検証済みIDではメールドメインに関する設定を、設定セットではメール送信サーバに関する設定を行うイメージになります。その他は各テナント共通の設定となります。

### 3.2. レピュテーション管理の分離
#### 3.2.1. 送信元IPのレピュテーション分離
インターネットでは、メール送信元のIPアドレスによって、そのメールの信頼性が判断されますので、各テナント（設定セット）毎に専用のIPアドレスを割り当てることで、レピュテーションを分離します。

**設定セット毎に専用IPプールを用意し、専用IPアドレス（マネージド）を割り当てる**ことが運用面で効果的です。専用IPプールは、専用IPをプールにグループ化し、それを設定セットにマッピングする機能です。さらに、専用IPは標準とマネージドの2種類があり、マネージドは管理工数削減に効果的。

（参考）専用IPアドレス 標準とマネージドの違いについて
* 標準: 専用IPアドレスを購入し、自分で管理する。専用IPが自動ウォームアップされるまで45日程度かかり、IPアドレスを事前に取得したりオーバーブッキングしておく必要あり。宛先プロバイダー側にIPアドレスをホワイトリスト登録する場合に利用。
* マネージド: AWS が専用 IP アドレスを管理し、インテリジェントウォームアップと呼ばれる機能で送信トラフィックパターンによって有効なプロバイダーへ自動的にウォームアップする。

#### 3.2.2. レピュテーション評価メトリクスの分離
テナント毎にメール送信のバウンス率や苦情率を監視するために、設定セットで評価メトリクスを有効化が必須です。

評価メトリクスを有効化することで、SESが送信したメールのバウンスや苦情をCloudWatchで追跡することができます。評価メトリクスを有効化することで、テナント毎に送信品質を監視し、必要に応じて送信制限を適用することが可能です。

なお、このメトリクスを監視して、バウンス率や苦情率が閾値を超えるお行儀の悪いテナントのメール送信を制限することもできます。（詳しくは後述します。）

#### 3.2.3. レピュテーションリスク通知の分離
AWSアカウント全体の送信停止を回避するために、テナント毎にバウンス率と苦情率を監視し、テナント毎に通知を行う仕組みが必要です。

CloudWatchで設定セット単位のバウンス率、苦情率のメトリクスを監視します。
バウンス率10%、苦情率0.5%を超えるとAWSアカウントレベルでSESが送信停止が行われます。各テナントで閾値を超える前に対策がとれるように、通知が行われるように設定します。


なお、ここでいうバウンス率はハードバウンス率を指しています。
バウンス率の詳細については、以下のAWS公式ドキュメントをご参照ください。

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/faqs-enforcement.html

>Q3. どのような種類のバウンスがバウンス率に含まれますか?
バウンス率には、まだ確認していないドメインに対するハードバウンスのみが含まれます。ハードバウンスは、「アドレスは存在しません」などの永続的な配信障害です。「メールボックスがいっぱいです」 などの一時的かつ断続的な障害や、IP アドレスのブロックによるバウンスは、バウンス率にカウントされません。

例えば、以下の表のとおりバウンス、苦情の監視、通知を行います。

| 項目 | 監視対象メトリクス | 閾値 | アラームアクション |
| --- | --- | --- | --- |
| バウンス率 | SES>Configuration-set>Reputation.BounceRate | 5%(推奨上限値) | 該当テナントのアプリチーム、またはメール管理者へSNS通知 |
| バウンス率 | SES>Configuration-set>Reputation.BounceRate | 8%(送信停止の10%より低い値) | 同上 |
| 苦情率 | SES>Configuration-set>Reputation.ComplaintRate | 0.1%(推奨上限値)| 同上 |
| 苦情率 | SES>Configuration-set>Reputation.ComplaintRate | 0.3%(送信停止の0.5%より低い値)| 同上 |

なお、詳細は後述しますが、バウンス率10%、苦情率0.5%になったテナントのメール送信は、強制的に停止させるように実装します。前述の通知はそうなる前の警告としての通知の意味もあります。

#### 3.2.4. バウンス、苦情のフィードバック転送
検証済みIDにおいて、ハードバウンス、及び苦情のフィードバック転送が行えます。
各テナントのアプリチームのSNSトピックに通知を行うことで、日常的な運用として、バウンス、苦情のフィードバックに対する対応を行ってもらいます。

#### 3.2.5. メールログの分離
テナント毎にメール送信のログを分離して保存することで、メール送信のトラブルシューティングや分析を行うことができます。SESでは、Kinesis Data Firehose を利用して、送信ログをS3に保存することが可能です。テナント毎に送信ログを個別のS3等に保存することで、ログのアクセス権限を個別に管理することができます。

ロギング対象のイベントタイプは選択できますが、ハードバウンス率と苦情率を監視するためには、**ハードバウンスのイベント**と**苦情のイベント**を送信するように設定することが重要になります。

### 3.3. 送信制限の分離
特定のテナントのバウンス率、苦情率が高い場合、テナント単位で自動的に送信停止を行う仕組みを構築します。
特定のテナントのメール送信が問題を引き起こしている際に、AWSアカウントレベルでSESが送信停止が行われる前に停止させることで、他のテナントの送信に影響を与えることを防ぐことができます。

これは、AWSのソリューションが既にありますので、それを利用します。

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/monitoring-sender-reputation-pausing-configuration-set.html

ただし、通知メッセージがテナントのアプリチームやメール管理者がわかりやすくするため、Lambdaを利用してメール内容をリッチなものにして通知します。

CloudWatchの設定は以下の通りです。

| 項目 | 監視対象メトリクス | 閾値 | Lambda+SNS通知先 |
| --- | --- | --- | --- |
| バウンス率 | SES>Configuration-set>Reputation.BounceRate | 10%(上限値) | LambdaでテナントのアプリチームへSNS通知 |
| 苦情率 | SES>Configuration-set>Reputation.ComplaintRate | 0.5%(送信停止)| 同上 |

### 3.4. メール分析
VDM ダッシュボードを利用することで、以下の確認分析が可能です。

* 検証済みIDごとのメトリクスの確認
* 30日以内のメッセージログの確認、エクスポート
* メッセージログからバウンスになった理由の確認

ここでのポイントは、テナント毎のIAMユーザー・グループを作成し、該当テナント（検証済みID単位）でのみメトリクスやメッセージログを確認できるようにすることです。

IAMポリシーはコチラ

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ConsolidatedPermissions",
      "Effect": "Allow",
      "Action": [
        "ses:SendBulkEmail",
        "ses:SendEmail",
        "ses:BatchGetMetricData",
        "ses:ListDomainDeliverabilityCampaigns",
        "ses:GetDeliverabilityDashboardOptions",
        "ses:GetDomainStatisticsReport",
        "ses:GetAccount",
        "ses:GetBlacklistReports",
        "ses:GetDomainDeliverabilityCampaign",
        "ses:ListDeliverabilityTestReports",
        "ses:GetEmailIdentity",
        "ses:GetEmailIdentityPolicies",
        "ses:ListEmailIdentities",
        "ses:ListCustomVerificationEmailTemplates",
        "ses:ListRecommendations",
        "ses:GetMessageInsights",
        "ses:ListSuppressedDestinations",
        "ses:GetSuppressedDestination",
        "ses:GetCustomVerificationEmailTemplate",
        "ses:GetIdentityPolicies",
        "ses:GetSendStatistics",
        "ses:GetSendQuota",
        "ses:DescribeConfigurationSet",
        "ses:ListReceiptFilters",
        "ses:GetIdentityMailFromDomainAttributes",
        "ses:GetIdentityVerificationAttributes",
        "ses:GetIdentityNotificationAttributes",
        "ses:DescribeReceiptRule",
        "ses:DescribeActiveReceiptRuleSet",
        "ses:GetAccountSendingEnabled",
        "ses:GetIdentityDkimAttributes",
        "ses:DescribeReceiptRuleSet",
        "ses:ListReceiptRuleSets",
        "ses:ListVerifiedEmailAddresses",
        "ses:GetTemplate"
      ],
      "Resource": "arn:aws:ses:ap-northeast-1:${account_id}:identity/${mail_domain}"
    }
  ]
}
```

## 4. AWSアカウント全体のSES管理設計と実装
#### 4.1. サプレッションリストの管理
サプレッションリストとは、メール送信時にブロックする送信先のメールアドレスのリストです。AWSでは、バウンス（ハードバウンス）と苦情（迷惑メール通知）のメールアドレスがサプレッションリストに登録されます。

SES には 「グローバルサプレッションリスト」、「アカウントレベルサプレッションリスト」の2種類があります。さらにさらにアカウントレベルサプレッションリストへのメールアドレス登録を設定セット単位で変更することができます。

設定セットレベルの抑止はアカウントレベルの抑制を上書きできますが、これはかなり限定的な機能です。

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/sending-email-suppression-list-config-level.html

例えば、アカウントレベルでは「バウンス」と「苦情」を抑止しているときに、これを「バウンスのみ」、または「苦情のみ」、あるいは「全く抑止しない」に変更することができます。
マルチテナントの場合は、基本的にはアカウント全体のメール送信停止を避けるため、アカウントレベルのサプレッションリストを避けるため原則的にはアカウントレベルでのバウンスと苦情の抑止の設定を利用することが望ましいと考えています。
（テナントのアプリ担当者から「バウンスのみ」、または「苦情のみ」の要件が来たら考えよう。。。）

 「グローバルサプレッションリスト」、「アカウントレベルサプレッションリスト」の変更はテナントの担当者からの要望を受け、AWSアカウント管理者で一括して実施します。または、「アカウントレベルサプレッションリスト」の変更のみ、一時的にIAMで権限を割り当ててテナントの担当者にて実施します。

ここは、サプレッションリストがテナント毎に管理できないことにより、運用でのカバーが必要になってくる点です。

### 4.2. バウンス率と苦情率の監視
テナント毎にバウンス率と苦情率を監視することで理論的には送信品質を維持することができますが、ハードバウンス率10%、苦情率0.5%を超えるとAWSアカウントレベルでSESが送信停止が行われますので、AWSアカウントの管理者はバウンス率と苦情率を監視しておく必要があります。

バウンス率と苦情率を監視するために、CloudWatchアラームを利用します。以下の表のとおり、バウンス率と苦情率のメトリクスを監視し、閾値を超えた場合には、SNS通知を行います。

| 項目 | 監視対象メトリクス | 閾値 | アラームアクション |
| --- | --- | --- | --- |
| バウンス率 | SES>Account>Reputation.BounceRate | 8%(送信停止の10%より低い値) | AWSアカウントの管理者 |
| 苦情率 | SES>Account>Reputation.ComplaintRate | 0.3%(送信停止の0.5%より低い値)| AWSアカウントの管理者 |

### 4.3. クォータの監視と管理
Amazon SESは、メール送信に対して一定のクォータ（制限）を設定しています。
残念ながらテナント何でのクォータ割り当てはできませんので、AWSアカウント単位でクォータ監視とタイムリーな引き上げリクエストが必要となります。

* **日次送信クォータ**: 1日に送信できるメールの最大数。SESアカウントごとに設定されており、通常はサンドボックスモードで制限が厳しく設定されています
* **秒単位の送信レート**: 1秒間に送信できる最大メール数。この制限は、大量のメールを送信する際に送信が均等に処理されるよう設定されています

クォータで送信不能になることを防ぐためには、日次送信クォータの方を監視します。
以下のブログを参考にして、AWS SESの送信クォータを監視することが可能です。

https://oc-technote.com/aws/aws-ses%E3%81%AE%E9%80%81%E4%BF%A1%E3%82%AF%E3%82%A9%E3%83%BC%E3%82%BF%E3%82%92%E7%9B%A3%E8%A6%96%E3%81%99%E3%82%8B/

1. 送信クォータのステータスを取得
2. 直近24時間の送信数 / 24時間での最大送信数 * 100 [%]を算出
3. 60%以上消費していた場合、CloudWatch Alarmを使用してSNSでAWSアカウント管理者へ通知（60%未満なら何もしない）

## 5. さいごに
SESをマルチテナント環境で利用する際のテナント分離設計とAWSアカウント全体のSES管理設計について、具体的な設計内容についてご紹介しました。

本来は、テナント毎にAWSアカウントを分離したり、SendGridのサブアカウントを利用することで本記事のような考慮をせずに設計・実装するが楽ではあります。ただ、同一のAWSアカウントで迅速にマルチテナント環境を構成したい場合やマルチアカウントの管理負荷を回避したい場合など、実際にはシステム全体での要求があるかと思います。そのような場合に本記事が参考になれば幸いです。