Настройка проксирования почты с NGINX для IMAP, POP3 и SMTP

Обновлено и опубликовано Опубликовано:

Тематические термины: NGINX, SMTP, IMAP, POP3.

NGINX можно использовать не только в качестве веб-сервера или http-proxy, но и для проксирования почты по протоколам SMTP, IMAP, POP3. Это позволит настроить:

  • Единую точку входа для масштабируемой почтовой системы.
  • Балансировку нагрузки между всеми почтовыми серверами.

В данной статье установка выполняется на операционной системе Linux. В качестве почтового сервиса, на который передаются запросы можно использовать postfix, exim, dovecot, exchange, сборку iredmail и другое.

Принцип работы

NGINX принимает запросы и выполняет аутентификацию на веб-сервере. В зависимости от результата проверки логина и пароля, прокси вернет ответ с несколькими заголовками.

В случае успеха:

Заголовок Ответ Описание
HTTP/1.0 200 OK Заголовок от HTTP-сервера. Обычно, 200. Если заголовок другой, то проблема может быть с самим веб-сервером.
Auth-Status OK Результат проверки логина и пароля.
Auth-Server <host> Почтовый сервер (имя сервера или его IP-адрес), на который перенаправляем запросы.
Auth-Port <port> Порт почтового сервера, на который перенаправляем запросы.

Таким образом, сервер и порт почтового сервера мы определяем на основе аутентификации. Это дает много возможностей при соответствующих знаниях языков программирования.

В случае неудачи:

Заголовок Ответ Описание
HTTP/1.0 200 OK Заголовок от HTTP-сервера. Обычно, 200. Если заголовок другой, то проблема может быть с самим веб-сервером.
Auth-Status <message> Сообщение об ошибке при проверки пользователя.
Auth-Wait <number> Число оставшихся попыток аутентификации до закрытия сессии.

В зависимости от результата аутентификации и заголовком, клиент перенаправляется на нужный нам почтовый сервер.

Подготовка сервера

Внесем некоторые правки в настройки безопасности сервера.

SELinux

Отключаем SELinux, если используем CentOS или если используем данную систему безопасности на Ubuntu:

vi /etc/selinux/config

SELINUX=disabled

setenforce 0

Брандмауэр

Если используем firewalld (по умолчанию в CentOS):

firewall-cmd –permanent –add-port=25/tcp –add-port=110/tcp –add-port=143/tcp

firewall-cmd –reload

Если используем iptables (по умолчанию в Ubuntu):

iptables -A INPUT -p tcp –dport 25 -j ACCEPT

iptables -A INPUT -p tcp –dport 110 -j ACCEPT

iptables -A INPUT -p tcp –dport 143 -j ACCEPT

apt-get install iptables-persistent

iptables-save > /etc/iptables/rules.v4

* в данном примере мы разрешили SMTP (25), POP3 (110), IMAP (143).

Установка NGINX

В зависимости от операционной системы, установка NGINX немного отличается.

или Linux Centos:

yum install nginx

или Linux Ubuntu:

apt install nginx

Разрешаем автозапуск сервиса и запускаем его:

systemctl enable nginx

systemctl start nginx

Если в системе уже установлен NGINX, проверяем с какими модулями он работает:

Мы получим список опций, с которыми собран веб-сервер — среди них мы должны увидеть –with-mail. Если нужного модуля нет, нужно обновить nginx

Настройка NGINX

Открываем конфигурационный файл nginx и добавляем опцию mail:

vi /etc/nginx/nginx.conf

mail {
    server_name mail.domain.local;
    auth_http   localhost:80/auth.php;

    proxy_pass_error_message on;

    server {
        listen    25;
        protocol  smtp;
        smtp_auth login plain cram-md5;
    } 

    server {
        listen    110;
        protocol  pop3;
        pop3_auth plain apop cram-md5;
    }

    server {
        listen   143;
        protocol imap;
    }
}

* где:

  • server_name — имя почтового сервера, которое будет отображаться при SMTP-приветствии.
  • auth_http — веб-сервер и URL для запроса аутентификации.
  • proxy_pass_error_message — разрешает или запрещает показ сообщения при неудачной аутентификации.
  • listen — порт, на котором прослушиваются запросы.
  • protocol — протокол приложения, для которого прослушивается соответствующий порт.
  • smtp_auth — доступные методы аутентификации для SMTP.
  • pop3_auth — доступные методы аутентификации для POP3.

В секции http – server дописываем:

http {
    …

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        …

        location ~ \.php$ {
            set $root_path /usr/share/nginx/html;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $root_path$fastcgi_script_name;
            include fastcgi_params;
            fastcgi_param DOCUMENT_ROOT $root_path;
        }
    …

* в данном примере мы добавили правило обработки файлов php через FastCGI, который будет работать на локальном сервере, порту 9000. Домашняя директория для хранения скриптов — /usr/share/nginx/html.

Перезапускаем сервер nginx:

systemctl restart nginx

Установка и настройка PHP

Для выполнения аутентификации с помощью PHP, необходимо установить в систему следующие пакеты.

Если CentOS:

yum install php php-fpm

Если Ubuntu:

apt-get install php php-fpm

Запускаем PHP-FPM:

systemctl enable php-fpm

systemctl start php-fpm

Аутентификация

Проверка логина и пароля выполняется скриптом, путь до которого задается опцией auth_http. В нашем примере, это скрипт на PHP.

Пример официальной заготовки для скрипта проверки логина и пароля:

vi /usr/share/nginx/html/auth.php

  1. <?php
  2.  
  3. /*
  4. NGINX sends headers as
  5. Auth-User: somuser
  6. Auth-Pass: somepass
  7. On my php app server these are seen as
  8. HTTP_AUTH_USER and HTTP_AUTH_PASS
  9. */
  10. if (!isset($_SERVER[“HTTP_AUTH_USER”] ) || !isset($_SERVER[“HTTP_AUTH_PASS”] )){
  11.   fail();
  12. }
  13.  
  14. $username=$_SERVER[“HTTP_AUTH_USER”] ;
  15. $userpass=$_SERVER[“HTTP_AUTH_PASS”] ;
  16. $protocol=$_SERVER[“HTTP_AUTH_PROTOCOL”] ;
  17.  
  18. // default backend port
  19. $backend_port=110;
  20.  
  21. if ($protocol==”imap”) {
  22.   $backend_port=143;
  23. }
  24.  
  25. if ($protocol==”smtp”) {
  26.   $backend_port=25;
  27. }
  28.  
  29. // NGINX likes ip address so if your
  30. // application gives back hostname, convert it to ip address here
  31. $backend_ip[“mailhost01″] =”192.168.1.22”;
  32. $backend_ip[“mailhost02″] =”192.168.1.33”;
  33.  
  34. // Authenticate the user or fail
  35. if (!authuser($username,$userpass)){
  36.   fail();
  37.   exit;
  38. }
  39.  
  40. // Get the server for this user if we have reached so far
  41. $userserver=getmailserver($username);
  42.  
  43. // Get the ip address of the server
  44. // We are assuming that you backend returns hostname
  45. // We try to get the ip else return what we got back
  46. $server_ip=(isset($backend_ip[$userserver]))?$backend_ip[$userserver] :$userserver;
  47.  
  48. // Pass!
  49. pass($server_ip, $backend_port);
  50.  
  51. //END
  52.  
  53. function authuser($user,$pass){
  54.   // password characters encoded by nginx:
  55.   // ” ” 0x20h (SPACE)
  56.   // “%” 0x25h
  57.   // see nginx source: src/core/ngx_string.c:ngx_escape_uri(…)
  58.   $pass = str_replace(‘%20′,’ ‘, $pass);
  59.   $pass = str_replace(‘%25′,’%’, $pass);
  60.  
  61.   // put your logic here to authen the user to any backend
  62.   // you want (datbase, ldap, etc)
  63.   // for example, we will just return true;
  64.   return true;
  65. }
  66.  
  67. function getmailserver($user){
  68.   // put the logic here to get the mailserver
  69.   // backend for the user. You can get this from
  70.   // some database or ldap etc
  71.   // dummy logic, all users that start with a,c,f and g get mailhost01
  72.   // the others get mailhost02
  73.   if (in_array(substr($user,0,1), array(“a”, “c”, “f”, “g”))) {
  74.     return “mailhost01”;
  75.   } else {
  76.     return “mailhost02”;
  77.   }
  78. }
  79.  
  80. function fail(){
  81.   header(“Auth-Status: Invalid login or password”);
  82.   exit;
  83. }
  84.  
  85. function pass($server,$port){
  86.   header(“Auth-Status: OK”);
  87.   header(“Auth-Server: $server”);
  88.   header(“Auth-Port: $port”);
  89.   exit;
  90. }
  91.  
  92. ?>

* данный скрипт принимает любые логин и пароль и перенаправляет запросы на серверы 192.168.1.22 и 192.168.1.33. Чтобы задать алгоритм аутентификации, редактируем строки 61 – 64. За возврат серверов, на которые идет перенаправление отвечают строки 73 – 77 — в данном примере если логин начинается на символы “a”, “c”, “f”, “g”, то перенаправление будет на сервер mailhost01, иначе, на mailhost02. Сопоставление имен серверов с IP-адресами можно задать на строках 31, 32, в противном случае, обращение будет идти по доменному имени.

Настройка почтового сервера

Обмен данными между NGINX прокси и почтовым сервером идут в открытом виде. Необходимо добавить в исключение возможность аутентификации по механизму PLAIN. Например, для настройки dovecot, делаем следующее:

vi /etc/dovecot/conf.d/10-auth.conf

Добавляем строки:

remote 192.168.1.11 {
   disable_plaintext_auth = no
}

* в данном примере мы разрешили PLAIN-запросы на аутентификацию с сервера 192.168.1.11.

Также проверяем:

* если ssl будет иметь значение required, проверка не будет работать, так как получится, что с одной стороны сервер разрешает запросы в открытом виде, но требует шифрование ssl.

Перезапускаем Dovecot сервис:

systemctl restart dovecot

Настройка клиента

Можно перейти к проверки настройки нашего прокси. Для этого в настройках клиента в качестве IMAP/POP2/SMTP указываем адрес или имя сервера nginx, например:

Пример настройки почтового клиента через NGINX mail proxy

* в данном примере почтовый клиент настраивается для подключения к серверу 192.168.1.11 по открытым портам 143 (IMAP) и 25 (SMTP).

Шифрование

Теперь настроим SSL-подключение. Nginx должен быть собран с модулем mail_ssl_module — проверяем командой:

При отсутствии необходимого модуля, пересобираем nginx.

После редактируем наш конфигурационный файл:

vi /etc/nginx/nginx.conf

mail {
    server_name mail.domain.local;
    auth_http   localhost/auth.php;

    proxy_pass_error_message on;

    ssl                 on;
    ssl_certificate     /etc/ssl/nginx/public.crt;
    ssl_certificate_key /etc/ssl/nginx/private.key;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    server {
        listen    110;
        protocol  pop3;
        pop3_auth plain apop cram-md5;
    }

    server {
        listen   143;
        protocol imap;
    }
}

Генерируем сертификат:

mkdir -p /etc/ssl/nginx

openssl req -new -x509 -days 1461 -nodes -out /etc/ssl/nginx/public.crt -keyout /etc/ssl/nginx/private.key -subj “/C=RU/ST=SPb/L=SPb/O=Global Security/OU=IT Department/CN=test.admins24.local/CN=test”

* где /etc/ssl/nginx/ — каталог хранения сертификатов, subj — индивидуальные настройки для сертификата.

Перезапускаем nginx:

systemctl restart nginx

Логирование

Для анализа ошибок включаем сохранение лога в файл:

vi /etc/nginx/nginx.conf

mail {
    …
    error_log /var/log/nginx/mail_proxy_error;
   …
}

Перезапускаем nginx:

systemctl restart nginx

Просмотр лога запускаем командой:

tail -f /var/log/nginx/mail_proxy_error

Возможные проблемы

bind() to x.x.x.x:XXX failed (13: Permission denied)

Ошибка возникаем при попытке перезапустить службу nginx. При этом, проверка конфигурационного файла командой nginx -t проходит корректно.

Причина: срабатывает система безопасности SELinux.

Решение: отключить или настроить SELinux.