Настройка NGINX для проксирования почты
Тематические термины: NGINX, SMTP, IMAP, POP3.
NGINX можно использовать не только в качестве веб-сервера или http-proxy, но и для проксирования почты по протоколам SMTP, IMAP, POP3. Это позволит настроить:
- Единую точку входа для масштабируемой почтовой системы.
- Балансировку нагрузки между всеми почтовыми серверами.
В данной статье установка выполняется на операционной системе Linux. В качестве почтового сервиса, на который передаются запросы можно использовать postfix, exim, dovecot, exchange, сборку iredmail и другое.
Описание принципа работы
Подготовка системы
Установка NGINX
Настройка NGINX
PHP — установка и настройка
Скрипт аутентификации
Настройка почтового сервера
Пример настройки клиента
Включение шифрования
Логирование ошибок
Устранение возможных проблем
Принцип работы
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
Пример официальной заготовки для скрипта проверки логина и пароля:
- <?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;
- }
- ?>
* данный скрипт принимает любые логин и пароль и перенаправляет запросы на серверы 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, например:
* в данном примере почтовый клиент настраивается для подключения к серверу 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.dmosk.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.