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

Тематические термины: 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, проверяем с какими модулями он работает:

nginx -V

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

Настройка NGINX

Открываем конфигурационный файл nginx.

vi /etc/nginx/nginx.conf

После раздела http добавляем опцию mail :

http {

}

mail {
auth_http localhost/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.

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

nginx -t && nginx -s reload

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

Для выполнения аутентификации с помощью PHP, необходимо установить в систему php и php-fpm, а также запустить последний. В зависимости от типа Linux, наши действия будут отличаться.

а) Если RPM (Rocky Linux / CentOS):

yum install php php-fpm

systemctl enable php-fpm —now

б) Если DEB (Debian, Ubuntu):

apt update

apt install php php-fpm

Открываем конфигурационный файл nginx с доменом по умолчанию. В зависимости от системы это будут разные действия.

а) Для RPM:

vi /etc/nginx/nginx.conf

б) Для DEB:

vi /etc/nginx/sites-enabled/default

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

http {

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

location ~ .php$ {
set $root_path /usr/share/nginx/html;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
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 .

Обратите особое внимание на опцию fastcgi_pass , отмеченную зеленым. Ее значение будет отличаться в зависимости от системы.

а) в системах RPM ее можно посмотреть командой:

cat /etc/php-fpm.d/www.conf | egrep -v ^; | grep ‘listen =’

б) в системах на базе DEB:

cat /etc/php/8.1/fpm/pool.d/www.conf | egrep -v ^; | grep ‘listen =’

* где 8.1 — версия PHP, которую можно посмотреть командой php -v.

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

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

Создаем файл auth.php. Его путь зависит от опции root в nginx. Как правило, это зависит от типа Linux.

а) Для RPM:

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

б) Для DEB:

vi /var/www/html/auth.php

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

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

* данный скрипт принимает любые логин и пароль и перенаправляет запросы на серверы 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 = yes

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

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

systemctl restart dovecot

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

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

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

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

Шифрование

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

vi /etc/nginx/nginx.conf

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

proxy_pass_error_message on;

ssl_certificate /etc/ssl/nginx/public.crt;
ssl_certificate_key /etc/ssl/nginx/private.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

server {
listen 995 ssl;
protocol pop3;
}

server {
listen 143;
listen 993 ssl;
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.remontka.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.

EnglishRussianUkrainian