VPC内のLambdaからはSES向けのVPCエンドポイントを使用してもBoto3ではメールを送信できない

# 初めに

本記事はタイトルについて検証を行った詳細を記載します

書くきっかけですが、最近久しぶりにVPCと戯れることになりました。
VPCを使った構成ですんなり行った思い出はなく、実際に沼にハマりました。

ポイントをまとめましたので、どなたかのお役にたてば嬉しいです

結論だけ知りたい方は最後まで飛ばしてください

# 検証を行うことになった背景

**VPC内のLambdaからメールを送る**
ただこちらを行いたいだけでした

アーキテクチャとしては以下となります
RDS→Lambda(VPC内)→SES→ユーザー

あまり見ないアーキテクチャですね
RDS Proxyがリリースされてから、LambdaとRDSの組み合わせは使用する頻度が多くなりました
DynamoDBだけでは辛い部分もあるので、今後こちらのアーキテクチャは増えるようになるのではないでしょうか

実装するに当たって、どのようにVPCから外へ出るか調べていました
すると、2020年5月に[SES向けのVPCエンドポイント](https://aws.amazon.com/jp/blogs/news/new-amazon-simple-email-service-ses-for-vpc-endpoints/)がリリースされているじゃありませんか

これは簡単に実装が終わるなと試してみたところ、Lambdaでタイムアウトが発生しました
これが長い検証の始まりです...



## 検証内容

CloudWatchには以下のように表示されていました

```
Task timed out after 30.03 seconds
```

VPCの外に出れていないようです

#### 1. SESの設定を疑う
まずはSESの検証が正しく行われているかテスト送信を行い確認です
サンドボックス環境にありましたので、何か影響があるのかもしれないと考えていました

テスト送信はコンソールから簡単に行えます
[手順はこちら](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-an-email-from-console.html)

送信元から送信先へ想定通りにメールが送信されました


SESのコンソールからは正しく送信が行えることが確認できましたので、別の原因を探します

#### 2. 権限周り
ロールか、セキュリティーグループの設定の問題だろうと思い、まずはガバガバの設定を行いました
とりあえずVPCの外に出れるか確認です

1. セキュリティーグループのインバウンドとアウトバウンドで全ての通信を許可
2. ロールに以下ポリシーを追加

```
"ses:SendEmail","ses:SendRawEmail"を全てのリソースから許可
AWSLambdaVPCAccessExecutionRole
VPCFullAccess
SESFullAccess
```

それでもタイムアウトは改善されませんでした


#### 3. ロジックの問題
次に行ったのはSESへ送信するロジックの見直しです
python3.8でBoto3を使っていました
ソースは[こちら](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-using-sdk-python.html)を参考にしています

ロジックに問題があるのか、設定に問題があるのか切り分けるためにまずはVPCの外にLambdaを作成し、同じソースコードで送信を行いました

すると、VPCの外にあるLambdaからはメールを送信できました
ソースに問題はなく、おそらくVPC関連の問題だということがこれまでの検証でわかりました

#### 4. エンドポイントを指定する?

VPC内のLambdaからSQSを利用する場合ですがエンドポイントを指定する必要があります
手順には記載がありませんでしたが、これと同じでSESもエンドポントを指定する必要があるのではないかと疑いました

boto3ではclientを生成時に引数でエンドポイントを指定することができます
[公式](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html)

VPCエンドポイントのURLを追加してみました

```
import boto3
ses_client = boto3.client('ses', region_name='ap-northeast-1', endpoint_url='com.amazonaws.region.email-smtp')
```

こちらで送信を試してみたところ以下エラーが発生しました

```
[ERROR] ValueError: Invalid endpoint: com.amazonaws.ap-northeast-1.email-smtp
```

エンドポイントとして無効なようです

#### 5. Lambda(VPC内)→Lambda（VPC外）→SES

もうやけくそに近くなってきました
SES向けのVPCエンドポイントはどうやら使えないようだと思い別のアプローチをすることにしました

2020年10月に[LambdaがPrivateLinkに対応](https://aws.amazon.com/jp/about-aws/whats-new/2020/10/aws-lambda-now-supports-aws-privatelink/)していました

これを試したところ問題なくメールの送信まで行えました
でもスマートじゃない...

#### 6. SMTPインターフェース...?

海外の方が[Amazon SES SMTP インターフェイスを使用した E メールの送信](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-email-smtp.html)をすればいけるよ!
と教えてくれているのを見つけました

>リージョンごとにユーザー名とパスワードが必要になります。

うーん微妙...
boto3で送りたい...


#### 7. サポートへ問い合わせ

もう自らの知識ではどうにもならないと判断し、問い合わせを行いました
以下回答の抜粋です


>VPCエンドポイントがサポートされておりますのは「1.SES SMTP インターフェースを使う」方法となります。
一方、お客様が実施しようとしている Boto3のsend_emailメソッドを使う方法は「2.SES API(AWSCLI,AWS SDK) を使う」となり、VPCエンドポイントには対応しておりません。
「2.SES API(AWSCLI,AWS SDK) を使う」場合には以下のいずれかにて対応いただければと存じます。
・LambdaをVPC外で起動する
・LambdaをVPC内に起動し、NAT Gateway を配置して Lambda からインターネットへ接続できるようにする

まじか!!!
どこにもBoto3じゃあかんとは書いてなかったやんけ!!!
という気持ちでしたが、無事解決できてよかったです
サポートの方に感謝

このあとNAT Gatewayでの方法を試してメールが届くことを確認しました


## 結論

* VPC内LambdaからBoto3を使ってメールを送信する場合はNATゲートウェイを使用してインターネット接続を行う必要がある
* SES向けのVPCエンドポイントを使う場合はBoto3は使えない(タイムアウトになる)
* SES向けのVPCエンドポイントを使う場合は[Amazon SES SMTP インターフェイス](https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-email-smtp.html)を使用する必要がある

### 組み合わせ

||SES向け VPCエンドポイント|送信方法|
|:-------:|:-------:|:------:|
|方法1|使用|SMTPインターフェース|
|方法2|不使用|NATゲートウェイ + Boto3|


サポートに聞くのが強い