📧

aws-ses-v2-localを使ってローカルでSESのAPIを使ってメール送信

2023/10/19に公開

最初に

SESのメール送信には、SMTP経由とAPI経由の2種類の方法があります。

SMTP経由でもAPI経由でも、AWS_ACCESS_KEY_ID と AWS_SECRET_ACCESS_KEY が必要となります。

ローカル環境での開発時、AWSのAWS_ACCESS_KEY_ID や AWS_SECRET_ACCESS_KEY をダウンロードして使用するのは推奨されません。

代わりに、ローカルでSMTPサーバーとしてmailpitやmailcatcherのようなツールを使うことができます。

しかし、SESのメール送信機能を模倣するローカル環境のシステムは多くありません。

そのため、aws-ses-v2-localを使用して、SESのメール送信機能をモックしてメール送信の動作を確認することにしました。

環境の構築やったこと

まずはdocker環境でローカルでメールが送信できる状態かつ、メールを取得できる環境を作成する

こちらのわかりやすい記事と、こちらのリポジトリをもとに参考に作成します。

docker-compose.yml
local_ses:
      build:
          context: .
          dockerfile: .docker/local_ses/Dockerfile
      ports:
            - "8005:8005"
.docker/local_ses/Dockerfile
FROM node:18
WORKDIR /app
RUN npm install -g aws-ses-v2-local
ENTRYPOINT ["aws-ses-v2-local", "--host=0.0.0.0"]

ここまでやってdockerを再度buildし、下記のportを叩くと

http://localhost:8005/

添付のような画面が表示される。これがメールを受信BoxのUI

SES APIを使った実装

Laravelでは、メール送信の際にドライバーをSESに設定することで、Amazon SESのAPIを利用してメールを送信することができます。一方、SMTPをドライバーとして設定する場合、SMTP経由でメールが送信されます。

ローカル環境での検証の際に、ドライバーをSESに設定すると、問題が発生します。具体的には、エンドポイントがAmazon SESの本番環境のエンドポイントに向かってしまいます。

ローカル環境での検証のためには、aws-ses-localのエンドポイントを指定する必要があります。これにより、外部への接続をせずに、ローカルでSESの動作を模倣できます。

.env
MAIL_MAILER=ses
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="Laravel"
MAIL_TO="admin1@example.com"

AWS_ACCESS_KEY_ID="dummy"
AWS_SECRET_ACCESS_KEY="dummy"
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
AWS_SES_ENDPOINT='http://local_ses:8005/'
services.php
    'ses' => [
        'version' => '2010-12-01',
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
        'endpoint' => env('AWS_SES_ENDPOINT', null),  //ここにendpointが設定できる
    ],
controller.php
Mail::to('test@example.jp')->send(new OrderShipped($order));

重要な点は、AWS_SES_ENDPOINTがnullの場合、SESは設定されたregionに基づいて自動的にエンドポイントを決定します。そのため、ローカルでの検証を行いたい場合は、AWS_SES_ENDPOINTの環境変数にローカルのエンドポイントを指定するだけで対応可能です。

もし、ドライバーをSESに設定した状態で、特にエンドポイントを指定せずにメールを送信しようとすると、以下のようなエラーが表示される可能性があります。

Request to AWS SES API failed. Reason: The security token included in the request is invalid..

<ErrorResponse xmlns=\"http://ses.amazonaws.com/doc/2010-12-01/\">
  <Error>
    <Type>Sender</Type>
    <Code>InvalidClie (truncated...)
 InvalidClientTokenId (client): The security token included in the request is invalid. - <ErrorResponse xmlns=\"http://ses.amazonaws.com/doc/2010-12-01/\">
  <Error>
    <Type>Sender</Type>
    <Code>InvalidClientTokenId</Code>
    <Message>The security token included in the request is invalid.</Message>
  </Error>
  <RequestId>afa048ee-232c-4f0b-9d45-308c6d8489dc</RequestId>
</ErrorResponse>

中身を見るとAWSアクセスキーIDが無効であるか、または不正な値のエラーとなる。これはやはり、
外向きでエンドポイントを叩いてる可能性が高そう??

SES SMTPを使った実装

SESを使うがMAIL_MAILERはsmtpのまま。

.env
MAIL_MAILER=smtp
MAIL_HOST=email-smtp.us-east-1.amazonaws.com
MAIL_PORT=587
MAIL_USERNAME=****************
MAIL_PASSWORD=***************************
MAIL_ENCRYPTION=TLS
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="Laravel"
MAIL_TO="admin1@example.com"

AWS_ACCESS_KEY_ID="dummy"
AWS_SECRET_ACCESS_KEY="dummy"
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

最後に

実際今まで自分が携わってた案件を見ると。localはsmtpでメール送信しており、dev環境以上は、APIでメールを送信してることがわかった。

Discussion