2016年7月19日火曜日

AWS SESでのメール送信(メルマガ・通常)

SESを使ったメール配信を作ったというより、作っていたWebアプリのメール配信機能でSMTPサーバーとしてSESを使った。

SESではバウンスメールが10%くらいになると出入り禁止になるという鉄の掟があるらしい(実際にはいきなり止まるわけではなくメール等で警告があるという噂もある)ので、バウンスを処理して、ダメなアドレスに何度も送らないようにしないといけない。

それは、↓こんなで何とかなった。

http://venzol.blogspot.jp/2015/05/amazon-ses-e-httpdocs.html


その後の話をまとめておく。

SES + SNS + SQSでどの程度のバウンスレートになったか?

もとのメールアドレスリストの品質にもよるだろうが、数%以内にはとどまっている。通常時は2%程度。

新しいアドレスが大量に投下された時は単純にそのアドレスリストのクオリティになるだろうから、どのくらいの品質があるかわからないリストであれば、一気に大量に送らないで小分けにしていった方がいいのかも知れない。(小分けにして、ちゃんと送れてる他のメールと混ざれば薄まる)


バウンスで多いもの

docomoとezwebの受信拒否機能によるバウンスが圧倒的に多い。これは盲点(抜け過ぎか?)だった。いや、そういうことがあるというのは予見していたが、問題はこれらの場合、(少なくとも俺がやっている業務では)受信拒否を解除してもらってメールを送る必要があるということ。

受信拒否の解除の連絡は、業務運用の方々の守備範囲なのでエンジニアたる俺には関係ない・・・ならよかったが、一度バウンスしてSESのSuppression List(ブラックリスト)に載ってしまったものは、手動で解除しなければならない。そして、この解除は、バッチやAPIでは出来ないようになっていて(キャプチャを入れなければいけない)、業務運用の方々がAWSのコンソールに抵抗ない場合でなければ、これがこちらの仕事になってしまう。


配信速度

現状、1万件/時間程度。SESの応答時間を早くすることは出来ないと思うが、スレッドを増やしてもあちらは遅くならないので、こちらがスレッド数を増やせばもっと早くできるはず。
SESの応答時間以外の処理時間もあり、それはどんなアプリでどんな機器なのかによるので一般論とは言えないが、自分のケースでは上記の性能のために16スレッドで動かしている。

反省というか、SESの使い方

メルマガのようなバルクでの配信をSESでやるとしても、通常の単発のメールは他のSMTPサーバーを使った方がいい。

たとえば、メルマガだとしてもその登録確認メールはSESでないので送っておき、それがバウンスしていない場合だけSESの方でも送るようにすれば、完全ではなくても、ある程度はSESで無駄にバウンス→Suppression List解除とやる手間は減らせるのではないか。


まあ、とりあえず根幹にある問題はスパム業者の跋扈と日本のケータイ会社のショボいフィルター機能(元で対処できないから端末レベルでの拒否設定に頼る)なのではないか、と苛立ちも募るが、そこは言っても詮無いことなのでどうでもいいや。くそ。

SSLについてまとめる

全体的なところがよくあやふやになるので。細かいところは書かない。
また、俺の理解によるので、他人が読んだ場合に間違った結果につながっても責任はもちろんとれない。

利用目的

  1. 暗号化通信を可能にする。暗号化の強度は鍵の長さに依存する。
  2. 通信相手のサーバーを特定・認証する。
    • ただし、これは秘密鍵を使っていることに依存するので、認証局が胡散臭い場合やオレオレ証明書では成り立たない。
    • クライアントは認証されない。暗号化通信開始時点のクライアントであることは保証できるが、HTTPレベルの話なのでセッション単位で認証されるわけではない。

各種ファイル

  • 秘密鍵ファイル xxxx.key
    • 最初に秘密鍵あり。
    • サーバーで作ってサーバーで使う。秘密鍵なだけに。
    • パスフレーズの有無はここで決定
      • 有にした場合、この後、CSRの作成やサーバーの起動で入力が必要
        • サーバーの起動は、コマンドの設定で自動入力することは可能
  • CSR (Certificate Signing Request) xxxx.csr
    • 秘密鍵を使ってサーバーで作る。
    • 公開鍵情報とディスティンギッシュネーム(組織名などの登録情報)
    • 公開鍵として、認証局に登録する(オレオレ証明書でなければ、XXXサインなどのサーバー証明書の発行をする企業に提出する)
  • サーバー証明書 xxx.crt
    • 提出したCSRを使って、認証局(XXXサイン、などの企業と思っていい)が作ってくれる。
      • 間接的に秘密鍵を使っていることになる。ペアとなる公開鍵がCSR→サーバー証明書と形を変えている状態なので。
      • 通常は、認証局がCSRと申込み情報をもとに発行(電子署名をつける)する。自己証明書ではサーバーで作る。

設定の流れ

  1. 登録情報(ディスティンギッシュネームに入れるもの)を確認
    • 鍵の長さ指定も確認
  2. 秘密鍵の作成
  3. CSRの作成
  4. サーバー証明書の申込
    1. CSRの送付
    2. 証明を受ける組織が、XXXサインなどに対して申し込む。実際の手順はサービスにより多少違う。
  5. サーバー証明書を受け取り、サーバーに設置
    1. Apacheならば、ssl.confに秘密鍵とサーバー証明書のファイルを指定し再起動(再起動時にパスフレーズが必要)

更新時の流れ

  1. 原則的に登録時と同じ
    • ssl.confを書き換える
      • SSLPassPhraseDialog
        http://venzol.blogspot.jp/2016/05/apachessl.html の、起動時にパスフレーズを入れない方法のためのshファイル
      • SSLCertificateFile
        xxx.crt
      • SSLCertificateKeyFile
        xxx.key
      • SSLCertificateChainFile ※Apache 2.4では、これがなくてSSLCertificateFileに指定する.crtファイルに、そのまま続けて.cerの内容を貼り付けておくらしい(俺が実際作業したのは2.2だから試してはいない)
        xxx.cer ← 中間CAがあるタイプだったので、crtと一緒に証明書業者から送付されてきた
    • 証明書がメール本文に書きこまれて送付されてきたりしたのでテキストエディタでファイルにしてからサーバーにアップロードしたが、最後の改行はあってもなくても大丈夫ということがわかった。
    • というか、証明書をファイルにした状態でダブルクリック(Windows10)したら、証明書の情報が表示された。保存に失敗していればこれが出ないので、この方法でもひとまずの確認をしておくとよい。
  2. 入れ損ねた場合に既存の証明書で稼働させるため、上記の各ファイルはすべて既存のものと別の名前で配置する。そうすれば、ssl.confを書き換える際に、現状のものをコピーしておけば、もし再起動でエラーが出た場合、ssl.confだけ戻せば既存の証明書で起動できるはず。

具体的な手順の補足

フォルダなども指定しない最低限の手順の例。実際にはもうちょっとファイル名などは管理しやすくする方がよい(更新などで戸惑わないように)。

Apache 2.2 + OpenSSL 1.0の環境。

    秘密鍵の作り方

    1. sudo su -
    2. cd /etc/httpd/conf
    3. openssl genrsa -des3 -out ./myprivate.key 2048
      →パスフレーズを求められる。ここでは"mypass"とした
    4. confディレクトリにmyprivate.keyができてる

    CSRの作り方

    1. openssl req -new -key ./myprivate.key -out ./myssl.csr
      → "req"が「CSRを作る」コマンド。
      → 指定した秘密鍵のパスフレーズが必要。ここでは"mypass"
      → その他設問は冒頭の国コードのみ"JP"、あとはすべて空
      ※ 秘密鍵作成時にパスフレーズのリタイプに失敗してたりすると、秘密鍵ファイルはあるくせにここで妙なエラーが出たりするので注意。その場合秘密鍵を作り直す
    2. myprivate.csrができてる。この中身のテキストを証明書発行サービスに申込時、貼り付けるなどすることになる。

    サーバー証明書の作り方(オレオレ用。通常はいらない)

    1. openssl x509 -req -days 3650 -sha256 -in myssl.csr -signkey myprivate.key -out myserver.crt
      → 秘密鍵は使いまわし
    2. myserver.crtができてる。本来であれば、サーバー証明書の発行会社がくれるもの。
    ついでに以下を作っておく。

    # vi mypass.sh
    # chmod 500 mypass.sh
    中身↓
    #!/bin/sh
    echo "mypass"


    サーバーへの組み込み

    1. vi /etc/httpd/conf.d/ssl.conf
      ここでそんなものないじゃん、という場合:
      # apachectl -t -D DUMP_MODULES | grep ssl
      としてSSLモジュール有無を確認。なければ、
      # yum install mod_ssl
      で入る(少なくとも、AWS EC2 AmazonLinuxでは。)
    2. SSLCertificateFile、SSLCertificateKeyFileをさきほどのファイルで指定
    3. SSLPassPhraseDialog  exec:/etc/httpd/conf.d/mypass.sh
      → これは起動時にパスフレーズ入れるのを省略した場合のみ

    ついでに全ページSSL

    /etc/httpd/conf.dに以下を作成。
    sslall.conf

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
    Apache再起動で、強制SSL。

    ※ 最初は以下のように書いていたのだが、ホスト名直下の"/"が抜けることがあった。転送先の最後の"/"を足せばいい?と思ったが、どうもRewriteRuleの方が世の多数派のようなので、それに倣うことにした。
    NameVirtualHost *:80
    <VirtualHost *:80>
      ServerName myhostname
      Redirect permanent / https://myhostname
    </VirtualHost>

    2016年6月29日水曜日

    AWS EC2が勝手に再起動するとは聞いていたけど本当にしやがった

    サーバーを動かしていて、httpでの応答を監視していたのだが、しばらくタイムアウトが続いた後、Connection Refusedになった。

    CloudWatchの方では、タイムアウトした頃からモニタリング測定値がなくなっていた。

    異常になってから15分ほどで回復したが、自分がsshで接続してみた頃には何事もなかったのようにサーバーは動いていて、最初は何が起きたかわからなかった。

     last reboot

    というコマンドで、直前のOS起動時刻がわかるそうで、それを実行してみてなるほど再起動かと。


    なお、Elastic IPは使っていないし、特に何かの設定もしていないが、EC2のIPアドレスは変わっていなかった。


    なお、せっかくきれいに再起動してくれたのに、Apacheの自動起動を設定していなかったために、サービス自体は自動復旧しなかった。

    スクリプト自体は勝手にあったので(Amazon Linuxのインスタンス)、

     chkconfig httpd on


    で、たぶん次からは自動起動するだろう(テストしてないけど)。

    2016年6月28日火曜日

    RDS(PostgreSQL)で接続が足りないエラーが出たりする状況

    本日の教訓。

    PostgreSQLで、最大接続数の設定値確認は、PSQLで

    show max_connections;

    AWS RDS(PostgreSQL)での最大接続数の原則は、

    インスタンスのメモリ(GiB)*1024*1024*1024/31457280 
    より少し少ない程度。
    ただしこれは、パラメータグループの内容により変わるかもしれない。


    2016年6月18日土曜日

    Files.listにやられた→ディレクトリが消えない

    Java7から追加されているjava.nio.file.Filesクラスは、Pathと並んで便利。

    だが、ちょっと気持ち悪いAPIのせいで気持ち悪い現象に嵌った。

    Tomcatからの操作でディレクトリをFiles.delete(path)で消すと、成功する。成功するのにディレクトリは消えていない。もう一度消すと失敗する。そりゃそうだ、さっき削除に成功しているのだから。

    でも、ディレクトリは依然として存在している・・・なぜかタイムスタンプがなくなった状態で。

    そして、Tomcatをシャットダウンするとディレクトリは消える。


    ・・・という現象。


    何かをcloseし忘れてるのだろうとは思ったが、ファイルじゃなくてディレクトリだし、空だし・・・と悩んでしまった。

    結局、Files.listで一覧を取得した際に帰ってくるStreamをcloseしていなかったから、ファイルのハンドルが閉じていなかったようだ。で、プロセスが消える時にそれが閉じるのでようやく本当に消えると。

    でもさあ。

    Files.list(path).filter(f->Files.isDirectory(f)).forEach(f->Files.delete(f));// これはダメ

    とか書きたくなるじゃん。Streamで帰ってくるんだから。

    JavaDocをよく見たら、

    try(Stream<Path> = Files.list(path)){
      //do something
    }catch(Exception e){

    みたいにやれと書いてあったけど、なんかそんなら無理にStreamとかにしなくても良かったんじゃねえの、という気がする・・・。


    なお、問題が起きてたのはWindows環境。Linuxだとどうなのかは確認してない。

    2016年6月3日金曜日

    豚の餌

    自分にはしばしばあることなのだが、外食というか飲み屋なんかで、出てきた料理を食いながら「さすがにしょっぱ過ぎる」とか「化学調味料バリバリっつうのも・・・」とか「肉ってやっぱ値段相応じゃん?」とか「豚の餌みたいな・・・」とか、そういう台詞を言った瞬間に店の人が「お待たせしました」って料理持ってきたりする。しかも、そういうときに限ってなぜかオーダー取りの姉ちゃんじゃなくて調理担当の人が持ってきてたりする。

    ・・・今夜もそうであった。

    言っておくが、上記の台詞は、けして店の料理に対して言っているのではない。店の料理もイマイチのこともあるが、だからと言ってそれを店内でケチつけるほど俺も不届きな人間ではない。

    むしろ、食いものが美味しい時に、触発されて料理の話になり、他所のひどい店で食ったものとか、自分で作った料理の失敗談とかを話しているのだ。

    でも、料理を持ってきて断片的に言葉を聞いた店の人は、きっと自分の料理が貶されたと思うのではと思い、いつも無駄にビビっており、かつ申し訳なく思う。やれやれ。

    まあ、普段からそういう汚いことを言わなきゃいいと言われたらそれもそうなんだが、でもねえ・・・・それも詰まらないしねえ・・・。

    みなさんはそういうこと、ないんですかね?

    2016年5月28日土曜日

    Apacheで、パスフレーズつきのSSLを使っていてパスフレーズなしで起動する方法

    まあ、安全のためのパスフレーズだし、そんなに頻繁に再起動しないし毎回入力すればいいかと思っていたが、運用環境で入力を一度ミスったら、「あと9回」とか出て、焦ってまた操作をミスり「あと8回」「あと7回」・・・ってカウントダウンされて心臓止まるかと思ったので。

    ↓こちらの「2」の方法
    http://www.linux-tech.info/web/entry-42.html


    要は
    #!/bin/sh
    echo 'pass-phrase'
    というようなshスクリプトのファイルを作り、実行権限を与えて、実行してパスフレーズを表示できるようにしておき、


    こんな感じでsslの設定を書き替え
    #diff ssl.conf ssl.conf.org
    < SSLPassPhraseDialog  exec:/etc/httpd/conf.d/passphrase.sh
    ---
    > SSLPassPhraseDialog  builtin

    ればOK。