LoginSignup
41

More than 5 years have passed since last update.

nginxでメールサービス(SMTP,POP,IMAP)をproxyする

Last updated at Posted at 2015-01-17

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. 認証エラー時のパスワードをログに出力し、ボットが入力するパスワードの調査などができる

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41