nginxにはメールサービス(SMTP,POP,IMAP)をproxyする機能があるので試してみた。
##1. テスト構成
サービス | サービス用IPとPort | Proxy先IPとPort | Proxy先ソフトウェア |
---|---|---|---|
SMTP | 192.168.1.1:587 | 127.0.0.1:587 | Postfix(OUT-BOUND用) |
SMTPS | 192.168.1.1:465 | 127.0.0.1:587 | Postfix(OUT-BOUND用) |
POP | 192.168.1.1:110 | 127.0.0.1:110 | dovecot |
POPS | 192.168.1.1:995 | 127.0.0.1:110 | dovecot |
IMAP | 192.168.1.1:143 | 127.0.0.1:143 | dovecot |
IMAPS | 192.168.1.1:993 | 127.0.0.1:143 | dovecot |
##2. nginx.confにproxy設定の追加
nginx.conf
mail {
auth_http 127.0.0.1/php/proxy.php ;
proxy on;
proxy_pass_error_message on;
ssl_certificate /etc/pki/tls/certs/startssl.crt;
ssl_certificate_key /etc/pki/tls/certs/startssl.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache shared:MAIL:10m;
smtp_capabilities PIPELINING 8BITMIME "SIZE 20480000";
pop3_capabilities TOP USER UIDL;
imap_capabilities IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=LOGIN;
smtp_auth login plain;
server {
listen 192.168.1.1:587;
protocol smtp;
starttls on;
xclient on;
resolver 127.0.0.1 valid=30s;
resolver_timeout 10s;
auth_http_header PORT 587;
}
server {
listen 192.168.1.1:465;
protocol smtp;
ssl on;
xclient on;
resolver 127.0.0.1 valid=30s;
resolver_timeout 10s;
auth_http_header PORT 465;
}
server {
listen 192.168.1.1:110;
protocol pop3;
starttls off;
auth_http_header PORT 110;
}
server {
listen 192.168.1.1:995;
protocol pop3;
ssl on;
auth_http_header PORT 995;
}
server {
listen 192.168.1.1:143;
protocol imap;
starttls off;
auth_http_header PORT 143;
}
server {
listen 192.168.1.1:993;
protocol imap;
ssl on;
auth_http_header PORT 993;
}
}
##3. Proxy先応答スクリプトの用意
nginxで指定したauth_httpのURLにproxy先を応答するスクリプトを設置する。
Auth-Status, Auth-Server, Auth-Port を応答すればいい。
下記のProxy先応答スクリプトは
- proxy先を 127.0.0.1 固定
- パスワードを test で固定
しています。
proxy.php
<?php
// set $env from nginx
$env['user'] = getenv('HTTP_AUTH_USER');
$env['passwd'] = getenv('HTTP_AUTH_PASS');
$env['proto'] = getenv('HTTP_AUTH_PROTOCOL');
$env['client'] = getenv('HTTP_CLIENT_IP');
$env['port'] = getenv('HTTP_PORT');
// proxy port map
$portmap = array(
"smtp" => 587,
"pop3" => 110,
"imap" => 143,
);
// protocol map
$protomap = array(
"995" => "pops",
"993" => "imaps",
"110" => "pop",
"143" => "imap",
"587" => "smtp",
"465" => "smtps",
);
$passwd = urldecode($env['passwd']) ;
$proxyhost = "127.0.0.1" ;
$proxyport = $portmap[$env['proto']];
// maillog
$log = sprintf('user=%s, client=%s, proto=%s', $env['user'], $env['client'], $protomap[$env['port']]);
if ( $passwd === 'test' ) {
$log = sprintf('proxy=successful, %s, connect=%s:%s', $log, $proxyhost, $proxyport);
header('Content-type: text/html');
header('Auth-Status: OK') ;
header("Auth-Server: 127.0.0.1") ;
header("Auth-Port: $proxyport") ;
} else {
$log = sprintf('proxy=failure, %s, passwd=%s', $log, $passwd);
header('Content-type: text/html');
header('Auth-Status: Invalid login') ;
}
// write syslog
openlog("ngproxy", LOG_PID , LOG_MAIL);
syslog(LOG_INFO,"$log");
closelog();
?>
##4. テスト時の出力
POP Proxy成功時
maillog
Jan 17 21:14:09 ma3ki ngproxy[2729]: proxy=successful, user=test@example.com, client=192.168.1.2, proto=pop, connect=127.0.0.1:110
Jan 17 21:14:09 ma3ki dovecot: pop3-login: Login: user=<test@example.com>, method=PLAIN, rip=127.0.0.1:41530, lip=127.0.0.1:110, mpid=18457, secured
POP Proxy失敗時
maillog
Jan 17 21:17:24 ma3ki ngproxy[2730]: proxy=failure, user=test@example.com, client=192.168.1.2, proto=pop, passwd=hoge
SMTPS Proxy成功時
maillog
Jan 17 21:22:57 ma3ki ngproxy[2727]: proxy=successful, user=test@example.com, client=192.168.1.2, proto=smtps, connect=127.0.0.1:587
Jan 17 21:22:57 ma3ki postfix/smtpd[7847]: connect from localhost[127.0.0.1]
Jan 17 21:22:57 ma3ki postfix/smtpd[7847]: E1B091044307E: client=[192.168.1.2], sasl_method=XCLIENT, sasl_username=test@example.com
##5. 利点
認証スクリプトを自作することで、柔軟なバックエンドの構成がとれる
例えば
a. Proxy先をldapやmysql等のdatabaseで管理できる
b. nginxはXCLIENTに対応している為、proxyをしてもpostfixの制限機能が使用できる
c. rbldnsやmemcachedなどのKVSと組み合わせれば、ratelimitが実装できる
d. 認証エラー時のパスワードをログに出力し、ボットが入力するパスワードの調査などができる