AWS SESへコマンドラインからメール送信する方法


認証なしのSMTPサーバーへは`telnet`を使って簡単にメール送信できますが、最近は認証&暗号化が必要なケースが多くなっているかと思います。
AWSのSimple Email Service(SES)ではSMTPサーバーとして各種クライアントからメール送信させることができますが、やはり認証&暗号化が必要となります。

サーバー内でメール送信できない場合などに調査するためにコマンドラインからSES経由でメール送信する手順を下記に示します。

## ログイントークンの生成

AWSマネージメントコンソール上で作成したSMTP送信用のIAMユーザーのアクセスキーとSMTP用パスワードを用いてログイントークンを生成します。(※SMTP用パスワードは、IAMユーザーのシークレットキーとは別物です。シークレットキーからパスワードへの変換は [こちら](http://d.hatena.ne.jp/asakawajunya/20140307/1394201842) を参照ください。)

下記の例では以下のように仮定しています。

* アクセスキー: AKIXXXXXXX6VQ
* パスワード: AjXXXXXXXXXXXXXXXXXXXXXXXXXXXGr

```bash
$ perl -MMIME::Base64 -e 'print encode_base64("AKIXXXXXXX6VQ\0AKIXXXXXXX6VQ\0AjXXXXXXXXXXXXXXXXXXXXXXXXXXXGr");'
QUtJWFhYWFhYWDZWUQBBS0lYWFhYWFhYNlZRAEFqWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhY
R3I=
```

生成された`QUt..中略..R3I=`(改行は削除)というのがログイントークンになります。
このログイントークンを後にSMTPの`AUTH PLAIN`コマンドの引数として利用します。

参考: http://fedorasrv.com/memo/log/18.shtml

## 送信実行

`openssl`でセキュアな通信経路を生成してSMTPコマンドを送信します。

```bash
$ openssl s_client -starttls smtp -crlf -connect email-smtp.us-east-1.amazonaws.com:587
CONNECTED(00000003)
depth=2 /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/C=US/ST=Washington/L=Seattle/O=Amazon.com Inc./CN=email-smtp.us-east-1.amazonaws.com
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
 1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
 2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
   i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFODCCBCCgAwIBAgIQbFgliPzkb078VTtEuv67EDANBgkqhkiG9w0BAQUFADCB
tTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug
YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMm
VmVyaVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwHhcNMTQwNjE3
MDAwMDAwWhcNMTUwNjEyMjM1OTU5WjB7MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
V2FzaGluZ3RvbjEQMA4GA1UEBxQHU2VhdHRsZTEYMBYGA1UEChQPQW1hem9uLmNv
bSBJbmMuMSswKQYDVQQDFCJlbWFpbC1zbXRwLnVzLWVhc3QtMS5hbWF6b25hd3Mu
Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt+3VyA97sPiOkE5L
nzR6GLrUk3A8K2QgJ91gu8IFIaQUjFzstCW5ZtIy9TNdWRwci5oG7evEm+Hev5OU
pIZ406f78oTav+lCMeyCgrDotEmA5F0k4HCE5PW5X59tcvPIVml2j+Pwz2joD45D
wDuP/YTU6FdET2cdKT954PVxAbHaTqXipd3dZXFuMlzLa5wpOs5n1OaDkoOMnwW5
RMk83Dz364hZrBzMWYYvnNuxo83+AxhXqhL1tNmU5AVrCUTakyffcIt8qFpCQzHI
iHwIEcIA7TgPjJv7HaXzEOokArU/W27uZoVoHc3oPYNjGOjFdFNFX5rxTktnvPN4
vGcJRQIDAQABo4IBezCCAXcwLQYDVR0RBCYwJIIiZW1haWwtc210cC51cy1lYXN0
LTEuYW1hem9uYXdzLmNvbTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIFoDAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwZQYDVR0gBF4wXDBaBgpghkgBhvhF
AQc2MEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsG
AQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBhMB8GA1UdIwQYMBaAFA1E
XBZTRMGCfh0gqyX0AWPYvnmlMCsGA1UdHwQkMCIwIKAeoByGGmh0dHA6Ly9zZC5z
eW1jYi5jb20vc2QuY3JsMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0
cDovL3NkLnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL3NkLnN5bWNiLmNv
bS9zZC5jcnQwDQYJKoZIhvcNAQEFBQADggEBACCj4Svx87rvh0/0ZDn1LP0MsVgB
r04/Hd5QmsC3OcGfipIRB09+qlThDatqvo04/vLrn1jpLQ1EZgCIZGTeH4DhLr7y
CVK/LXTgn/E+o2NO6Eg+4z73dGF+XV5HrzZL2d3e8DjI3sxO2FyeYk0nLSEb4vXl
R/6COV3OWQDDW+QExY4kuucQkQrXNynCJriPf9QICD213sHbPCQlHe5z8kdyPbZ7
py74NEvT8x29s6yogf6HJdg7pkwdwzEH4sqfl5uuaGyBvBFKKcq1PC6h15SPztss
yGDP1R4RCohFAqryqilQE8bTTVzSzrWfiwBH2gGC1tD4xnYNh93RTU5LNfI=
-----END CERTIFICATE-----
subject=/C=US/ST=Washington/L=Seattle/O=Amazon.com Inc./CN=email-smtp.us-east-1.amazonaws.com
issuer=/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
---
No client certificate CA names sent
---
SSL handshake has read 4468 bytes and written 491 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : AES256-SHA
    Session-ID: 54B5E0F4ED38A22EE44042886FF5BFBC393636499AA000FC3519DAF935DE1991
    Session-ID-ctx:
    Master-Key: 2BD01F7B78763D6E485805A91C5D993A6E7A712698B5DAA6C676FDB5848AF72340D79BBBD771885CE0C86E525E51FE24
    Key-Arg   : None
    Start Time: 1421205761
    Timeout   : 300 (sec)
    Verify return code: 0 (ok)
---
250 Ok
EHLO example.com                   # ← 入力
250-email-smtp.amazonaws.com
250-8BITMIME
250-SIZE 10485760
250-STARTTLS
250-AUTH PLAIN LOGIN
250 Ok
AUTH PLAIN QUt..中略..R3I=         # ← 入力 (先の手順で生成したログイントークン)
235 Authentication successful.
MAIL FROM: test@example.com       # ← 入力
250 Ok
rcpt TO: tasaki@example.com       # ← 入力 (※先頭は小文字のrです)
250 Ok
DATA                              # ← 入力
354 End data with <CR><LF>.<CR><LF>
From: test@example.com            # ← 入力
Subjet: test                      # ← 入力
Hello world.                      # ← 入力
.                                 # ← 入力
250 Ok 0000014ae676f26f-9cc226da-c045-4218-8256-2bb42dde127f-000000
QUIT                              # ← 入力
DONE
```


## 失敗その1

`telnet`を使って直接接続してみる。ポートは587番。

```bash
$ telnet email-smtp.us-east-1.amazonaws.com 587
Trying 23.21.91.54...
Connected to ses-smtp-prod-335357831.us-east-1.elb.amazonaws.com.
Escape character is '^]'.
220 email-smtp.amazonaws.com ESMTP SimpleEmailService-908880847 y4rp3XewXjJukNFXnBHZ
AUTH PLAIN QUt..中略..R3I=
530 Must issue a STARTTLS command first
EHLO lo421 Timeout waiting for data from client.
Connection closed by foreign host.
```

`AUTH`コマンドを使う前に、`STARTTLS`コマンドというのを利用して通信を暗号化したものにしないといけないっぽい。

参考: http://yukimura1227.blog.fc2.com/blog-entry-43.html

## 失敗その2

送信宛先を指定する`RCPT TO`コマンドをそのまま大文字で入力すると、`openssl`コマンドの方が`RENEGOTIATING `コマンドのショートカットキーと認識してしまいSMTPサーバー側にコマンドを送信できませんでした。

```bash
$ openssl s_client -starttls smtp -crlf -connect email-smtp.us-east-1.amazonaws.com:587
CONNECTED(00000003)
  中略
---
250 Ok
AUTH PLAIN QUt..中略..R3I=         # ← 入力
235 Authentication successful.
MAIL FROM: test@example.com       # ← 入力
250 Ok
RCPT TO: tasaki@exapmle.com       # ← 入力
RENEGOTIATING                     # ← なんで勝手にネゴシエーションしよるん!?
depth=2 /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
verify error:num=20:unable to get local issuer certificate
verify return:0
DATA                              # ← 入力
503 Error: need RCPT command      # ← 本文データ送る前に宛先を指定しなさいって怒られた.. くっそー。
421 Timeout waiting for data from client.
closed
```

参考: https://forums.zoho.com/topic/renegotiating-when-providing-the-rcpt-to

> The "renegotiating" happens as you are typing RCPT TO with "R" in Caps. That is how s_client is behaving. You may try entering "rcpt to" instead of "RCPT TO". Please refer this public link for details.