Thank you for reading this post, don't forget to subscribe!
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
firewall-cmd --permanent --add-port=25/tcp --add-port=110/tcp --add-port=143/tcp
firewall-cmd --reload
Если используем iptables
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 и добавляем опцию mail:
vi /etc/nginx/nginx.conf
[codesyntax lang="php" blockstate="collapsed"]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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; } } |
[/codesyntax]
* где:
- server_name — имя почтового сервера, которое будет отображаться при SMTP-приветствии.
- auth_http — веб-сервер и URL для запроса аутентификации.
- proxy_pass_error_message — разрешает или запрещает показ сообщения при неудачной аутентификации.
- listen — порт, на котором прослушиваются запросы.
- protocol — протокол приложения, для которого прослушивается соответствующий порт.
- smtp_auth — доступные методы аутентификации для SMTP.
- pop3_auth — доступные методы аутентификации для POP3.
В секции http - server дописываем:
[codesyntax lang="php" blockstate="collapsed"]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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; } ... |
[/codesyntax]
* в данном примере мы добавили правило обработки файлов php через FastCGI, который будет работать на локальном сервере, порту 9000. Домашняя директория для хранения скриптов — /usr/share/nginx/html.
Перезапускаем сервер nginx:
systemctl restart nginx
Установка и настройка PHP
Для выполнения аутентификации с помощью PHP, необходимо установить в систему следующие пакеты.
yum install php php-fpm
Запускаем PHP-FPM:
systemctl enable php-fpm
systemctl start php-fpm
Аутентификация
Проверка логина и пароля выполняется скриптом, путь до которого задается опцией auth_http. В нашем примере, это скрипт на PHP.
Пример официальной заготовки для скрипта проверки логина и пароля:
vi /usr/share/nginx/html/auth.php
[codesyntax lang="php" blockstate="collapsed"]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
<?php /* NGINX sends headers as Auth-User: somuser Auth-Pass: somepass On my php app server these are seen as HTTP_AUTH_USER and HTTP_AUTH_PASS */ if (!isset($_SERVER["HTTP_AUTH_USER"] ) || !isset($_SERVER["HTTP_AUTH_PASS"] )){ fail(); } $username=$_SERVER["HTTP_AUTH_USER"] ; $userpass=$_SERVER["HTTP_AUTH_PASS"] ; $protocol=$_SERVER["HTTP_AUTH_PROTOCOL"] ; // default backend port $backend_port=110; if ($protocol=="imap") { $backend_port=143; } if ($protocol=="smtp") { $backend_port=25; } // NGINX likes ip address so if your // application gives back hostname, convert it to ip address here $backend_ip["mailhost01"] ="192.168.1.22"; $backend_ip["mailhost02"] ="192.168.1.33"; // Authenticate the user or fail if (!authuser($username,$userpass)){ fail(); exit; } // Get the server for this user if we have reached so far $userserver=getmailserver($username); // Get the ip address of the server // We are assuming that you backend returns hostname // We try to get the ip else return what we got back $server_ip=(isset($backend_ip[$userserver]))?$backend_ip[$userserver] :$userserver; // Pass! pass($server_ip, $backend_port); //END function authuser($user,$pass){ // password characters encoded by nginx: // " " 0x20h (SPACE) // "%" 0x25h // see nginx source: src/core/ngx_string.c:ngx_escape_uri(…) $pass = str_replace('%20',' ', $pass); $pass = str_replace('%25','%', $pass); // put your logic here to authen the user to any backend // you want (datbase, ldap, etc) // for example, we will just return true; return true; } function getmailserver($user){ // put the logic here to get the mailserver // backend for the user. You can get this from // some database or ldap etc // dummy logic, all users that start with a,c,f and g get mailhost01 // the others get mailhost02 if (in_array(substr($user,0,1), array("a", "c", "f", "g"))) { return "mailhost01"; } else { return "mailhost02"; } } function fail(){ header("Auth-Status: Invalid login or password"); exit; } function pass($server,$port){ header("Auth-Status: OK"); header("Auth-Server: $server"); header("Auth-Port: $port"); exit; } ?> |
[/codesyntax]
* данный скрипт принимает любые логин и пароль и перенаправляет запросы на серверы 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
Добавляем строки:
[codesyntax lang="php" blockstate="collapsed"]
1 2 3 |
remote 192.168.1.11 { disable_plaintext_auth = no } |
[/codesyntax]
* в данном примере мы разрешили PLAIN-запросы на аутентификацию с сервера 192.168.1.11.
Также проверяем:
ssl = yes
* если ssl будет иметь значение required, проверка не будет работать, так как получится, что с одной стороны сервер разрешает запросы в открытом виде, но требует шифрование ssl.
Перезапускаем Dovecot сервис:
systemctl restart dovecot
Настройка клиента
Можно перейти к проверки настройки нашего прокси. Для этого в настройках клиента в качестве IMAP/POP2/SMTP указываем адрес или имя сервера nginx, например:
* в данном примере почтовый клиент настраивается для подключения к серверу 192.168.1.11 по открытым портам 143 (IMAP) и 25 (SMTP).
Шифрование
Теперь настроим SSL-подключение. Nginx должен быть собран с модулем mail_ssl_module — проверяем командой:
nginx -V
При отсутствии необходимого модуля, пересобираем nginx. После редактируем наш конфигурационный файл:
vi /etc/nginx/nginx.conf
[codesyntax lang="php" blockstate="collapsed"]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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; } } |
[/codesyntax]
Генерируем сертификат:
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.test.ru/CN=test"
* где /etc/ssl/nginx/ — каталог хранения сертификатов, subj — индивидуальные настройки для сертификата.
Перезапускаем nginx:
systemctl restart nginx
Логирование
Для анализа ошибок включаем сохранение лога в файл:
vi /etc/nginx/nginx.conf
[codesyntax lang="php" blockstate="collapsed"]
1 2 3 4 5 |
mail { ... error_log /var/log/nginx/mail_proxy_error; ... } |
[/codesyntax]
Перезапускаем 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.