nginx
インフラ

NginxのTCP Proxy機能とMail Proxy機能を使って常にSMTPをlocalhost:25で受けられるようにする

More than 1 year has passed since last update.

何の話?

WebApplication等と同居するNginxからみて常にlocalhost:25にSMTPサーバが見えるようにNginxでSMTP ServerのProxyを構築する方法の紹介です。

最終的にTCP Proxy機能を使ってSMTPサーバと会話するので、そのあたりで複数のSMTPサーバへの分散を記述することが出来ます。

Application内部の変数などでSMTPサーバを設定していたり、インフラ屋さんの都合でSMTPサーバが変更になったり、分散しているSMTPサーバが増減したり、テストサーバと本番サーバとかでSMTPサーバの設定が異なったりする環境の場合に便利です。

また、メールの送信ログをNginxが動作しているサーバ側に残すことが出来るので、SMTPサーバに乗り込んでの調査が出来ない/やり辛い場合にも便利です。

例えば以下のような環境であれば動きます。

$ nginx -V 2>&1 | sed -e 's/--/\n--/g' | grep -e '\(version\|stream\|mail\)'
nginx version: nginx/1.11.9
--with-stream=dynamic
--with-stream_ssl_module
--with-mail=dynamic
--with-mail_ssl_module

正確な必要になるVersionやPluginが気になる方は自分で調べてください。

設定

デフォルト設定に追記していく想定です。

自身のNginxの設定や環境に合わせて読み替えてください。

/etc/nginx/nginx.confの最下行に以下を追加します。

ngix.conf
stream {
    upstream smtp-server{
        server my.mail.server1.com:25 weight=3;
        server my.mail.server2.com:25;
        server my.mail.server3.com:25;
    }

    server {
        listen      2525;
        proxy_pass  smtp-server;
        error_log   /var/log/nginx/mail-tcp-proxy.log info;
    }
}

mail {
    # See /etc/nginx/sites-available/mail_auth
    auth_http         localhost:8025/mail_auth/;

    proxy             on;
    proxy_pass_error_message on;

    smtp_auth         none;
    smtp_capabilities "SIZE 10485760" 8BITMIME ENHANCEDSTATUSCODES DSN ;


    ssl        off;
    xclient    off;

    server {
        listen     25;
        protocol   smtp;
        error_log  /var/log/nginx/mail-smtp-proxy.log info;
    }
}

/etc/nginx/sites-available/mail_authを以下の内容で作成します。

mail_auth
log_format mail-auth-log '$host - $remote_user [$time_local] '
                          '$request_time '
                          '"$http_auth_smtp_helo" '
                          '"$http_auth_smtp_from" '
                          '"$http_auth_smtp_to"';

server {
    listen      8025;
    server_name localhost;
    charset     utf-8;
    access_log  /var/log/nginx/mail-send.log mail-auth-log;
    error_log   /var/log/nginx/mail-auth-error.log;

    location ~ /mail_auth/ {
        add_header Auth-Status OK;
        add_header Auth-Server 127.0.0.1;
        add_header Auth-Port   2525;
        empty_gif;
        break;
    }
}

SymbolicLinkを作っておきます。

$ sudo ln -sf /etc/nginx/sites-available/mail_auth /etc/nginx/sites-enabled/mail_auth
$ sudo ls -l /etc/nginx/sites-enabled/mail_auth
lrwxrwxrwx 1 root root ss MMM  d hh:mm /etc/nginx/sites-enabled/mail_auth -> /etc/nginx/sites-available/mail_auth

リロードしたら有効になるはずです。

$ sudo service nginx reload

送信に成功した場合のログはこんな感じになるはずです。

log
$ tail -n100 /var/log/nginx/mail-*.log
==> /var/log/nginx/mail-send.log <==
localhost - - [02/May/2017:18:31:31 +0900] 0.000 "app-srv.c.app-grp.internal" "MAIL FROM:<test@mail.com>" "RCPT TO:<someone@foo.org>"

==> /var/log/nginx/mail-smtp-proxy.log <==
2017/05/02 18:31:31 [info] 17222#17222: *3811 client 127.0.0.1:54096 connected to 0.0.0.0:25
2017/05/02 18:31:31 [info] 17222#17222: *3811 client logged in, client: 127.0.0.1, server: 0.0.0.0:25
2017/05/02 18:31:32 [info] 17222#17222: *3811 proxied session done, client: 127.0.0.1, server: 0.0.0.0:25

==> /var/log/nginx/mail-tcp-proxy.log <==
2017/05/02 18:31:31 [info] 17221#17221: *3815 client 127.0.0.1:38740 connected to 0.0.0.0:2525
2017/05/02 18:31:31 [info] 17221#17221: *3815 proxy nnn.nnn.nnn.nnn:33726 connected to xxx.xxx.xxx.xxx:2525
2017/05/02 18:31:32 [info] 17221#17221: *3815 upstream disconnected, bytes from/to client:832/267, bytes from/to upstream:267/832

mail-send.log以外のログは、動作確認が終わればinfoレベルじゃなくてもいいと思います。

以上です。

参考

モジュール・リファレンス(公式)
https://nginx.org/en/docs/http/load_balancing.html
https://nginx.org/en/docs/stream/ngx_stream_core_module.html
https://nginx.org/en/docs/mail/ngx_mail_core_module.html
https://nginx.org/en/docs/mail/ngx_mail_smtp_module.html

ラウンドロビンをmail_auth(http)で行う場合の例
http://qiita.com/ma3ki/items/44dab8d93423978dd5d9