Nginx-прокси для корпоративной почты от Mail.Ru


Как вы знаете, на территории Украины в последнее время почта от Mail.Ru перестала открываться. С доступом к веб-версии все легко — там проблемы решаются в несколько кликов, а вот с IMAP-клиентом веселее. У меня в телефоне, например, работает штатная почтовая программа от Apple, но уже неделю я не могу получать почту в то время, когда я подключен к Интернету от сотовой сети. А я, надо сказать, мобильным Интернетом от «Vodafone» пользуюсь исключительно для получения почты на мобильный, в остальном он мне без надобности. Решил исправить проблему оригинально, но из этого так ничего и не вышло. Пишу здесь на случай, если кто-нибудь, кто лучше меня понимает исходный код nginx, придумает патч, который будет в силах ситуацию исправить.

Итак, в nginx, как вы, наверное, знаете, есть модуль из базовой комплектации, который позволяет использовать веб-сервер как прокси для почты по протоколам IMAP, POP3 и SMTP с местной авторизацией. В принципе, функционал весьма интересный, поэтому мне эта идея понравилась. Для начала, собираем nginx с указанием того, что мы желаем пользоваться электронной почтой:

./configure --with-mail --with-mail_ssl_module --with-openssl=/path/to/openssl
После сборки nginx, создаем конфигурационный документ примерно следующего вида:
mail { auth_http 127.0.0.1:9990/auth.php; proxy_pass_error_message on; server_name my.perfect.mail; xclient off; proxy on; smtp_auth "plain"; smtp_capabilities "8BITMIME" "SIZE 42991616" "ENHANCEDSTATUSCODES"; ssl on; ssl_certificate /some/ssl/cert.crt; ssl_certificate_key /some/ssl/cert.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; pop3_capabilities "TOP" "USER"; imap_capabilities "IMAP4rev1" "UIDPLUS" "ID" "XLIST" "UNSELECT" "MOVE" "LIST-STATUS"; server { listen 465; listen [::]:465; protocol smtp; smtp_auth login plain cram-md5; } server { listen 995; listen [::]:995; protocol pop3; pop3_auth plain apop cram-md5; } server { listen 993; listen [::]:993; protocol imap; } }
Размещается вся эта красота ниже секции http в файле /etc/nginx/nginx.conf. Немного поясню о том, что это все значит. Итак, в самой первой строке мы объявили, что обращаться за авторизацией перед соединением нужно по адресу 127.0.0.1:9990/auth.php (пока этого у нас нет, далее расскажу подробнее). В поле «server_name» указано доменное имя сервера, к которому мы будем обращаться с клиента. «ssl_certificate» и «ssl_certificate_key» — это сертификат и ключ, соответствующие тому домену, который указан в «server_name». Нужно для того, чтобы трафик между клиентом и прокси-сервером шифровался, а не передавался в открытом виде. В секциях «server» объявлено то, что сервер будет слушать порты IMAP, POP3 и SMTP как по IPv4, так и по IPv6 (если у вас, конечно, нет поддержки IPv6, то строки с двоеточиями в квадратных скобках можно смело ликвидировать).

Теперь немного о том самом 127.0.0.1:9990/auth.php. Это адрес, на который прокси-сервер будет передавать логин и пароль от ящика при авторизации. В ответ на эти данные мы должны дать прокси разрешение на соединение, а вместе с тем указать, какой именно сервер и порт необходимо проксировать для данного ящика. По сути дела, ничего сложного. Многие коллеги писали тяжелые скрипты на Perl с получением данных из MySQL. Я решил пока ограничиться небольшим тестовым скриптом на PHP, суть которого заключается в том, что он принимает логин и если он соответствует моей почте, отдает IMAP-сервер mail.ru для дальнейшей работы.

<?php if(@$_SERVER['HTTP_AUTH_USER'] == "[email protected]"){ if(@$_SERVER['HTTP_AUTH_PROTOCOL'] == "imap"){ header("Auth-Status: OK", true); header("Auth-Server: imap.mail.ru", true); header("Auth-Port: 993", true); }elseif(@$_SERVER['HTTP_AUTH_PROTOCOL'] == "pop3"){ header("Auth-Status: OK", true); header("Auth-Server: pop.mail.ru", true); header("Auth-Port: 995", true); }elseif(@$_SERVER['HTTP_AUTH_PROTOCOL'] == "smtp"){ header("Auth-Status: OK", true); header("Auth-Server: smtp.mail.ru", true); header("Auth-Port: 465", true); }else{ header("Auth-Status: Internal Error", true); } }else{ header("Auth-Status: Invalid Mailbox", true); } ?>
Скрипт из семейства простейших, но свою функцию исполняет. Разместить его можно где угодно, но только на HTTP, так как с шифрованием эта штука работать не умеет. Лучше всего создать отдельный локальный виртуальный хост на выделенном порту и привязать авторизацию туда. В nginx это делается достаточно просто:
server { listen 127.0.0.1:9990; server_name 127.0.0.1; access_log off; error_log /var/log/nginx/127.0.0.1-error.log; location @apache { proxy_pass http://127.0.0.1:8400$uri$is_args$args; proxy_set_header Host $host; proxy_set_header X-Real-IP $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $remote_addr; proxy_connect_timeout 120; proxy_send_timeout 120; proxy_read_timeout 180; } location / { try_files /BYCezwf54s @apache; } location ~* \.(jpg|jpeg|gif|png|ico|css|bmp|swf|js|html|txt)$ { root /var/www/127.0.0.1/; try_files $uri @apache; } }
Я взял типовую конфигурацию, в которой все скрипты обрабатывает Apache, а статику отдает nginx. Можно было, конечно, и переписать, но мне было лень. «/var/www/127.0.0.1/» — это директория, в которой находится скрипт авторизации. Все остальное, я думаю, будет понятно. На случай, если у вас вдруг нет Apache, то ставить его не нужно — просто возьмите аналогичную конфигурацию на другом сайте — я много такого сегодня встречал в Интернете, пока искал решение проблемы.

Ну все, вроде бы готово. Пробуем подключиться с клиента. Фигушки! Процесс подключения начинается и вполне себе успешно виснет. В чем же дело? А дело, как выяснилось, очень простое! «mail_ssl_module» обеспечивает только подключение клиентов к прокси-серверу по SSL, но не подключение самого прокси-сервера к конечному узлу. Таким образом, nginx пытается подключиться к серверу mail.ru на SSL-порту не используя при этом SSL. Исправить это средствами nginx, кстати, никак нельзя (или же мне за сутки поисков просто не попалась информация об этом). Зато есть stunnel — хитрый прокси-сервер, который подключается к целевому узлу по SSL, а нам разрешает подключиться к себе по обычному прозрачному протоколу. Ok, устанавливаем. /etc/stunnel/stunnel.conf для mail.ru имеет примерно такой вид:

client = yes [pop3s] accept = 127.0.0.1:1995 connect = pop.mail.ru:995 [imaps] accept = 127.0.0.1:1993 connect = imap.mail.ru:993 [ssmtp] accept = 127.0.0.1:1465 connect = smtp.mail.ru:465

Все остальное в файле можно не трогать. Обратите внимание: «accept» — это наша внутренняя точка, к которой мы будем подключаться без SSL; «connect» — это сервер mail.ru с SSL-шифрованием. Соответственно, теперь нам придется править наш скрипт авторизации, который отвечает по адресу 127.0.0.1:9990/auth.php:

<?php if(@$_SERVER['HTTP_AUTH_USER'] == "[email protected]"){ if(@$_SERVER['HTTP_AUTH_PROTOCOL'] == "imap"){ header("Auth-Status: OK", true); header("Auth-Server: 127.0.0.1", true); header("Auth-Port: 1993", true); }elseif(@$_SERVER['HTTP_AUTH_PROTOCOL'] == "pop3"){ header("Auth-Status: OK", true); header("Auth-Server: 127.0.0.1", true); header("Auth-Port: 1995", true); }elseif(@$_SERVER['HTTP_AUTH_PROTOCOL'] == "smtp"){ header("Auth-Status: OK", true); header("Auth-Server: 127.0.0.1", true); header("Auth-Port: 1465", true); }else{ header("Auth-Status: Internal Error", true); } }else{ header("Auth-Status: Invalid Mailbox", true); } ?>

Запускаем stunnel и пробуем подключиться из консоли:

openssl s_client -connect my.perfect.mail:993

Когда соединение успешно проходит, отправляем команду авторизации:
1 LOGIN [email protected] someSecretPassword

На этом этапе меня ожидало жестокое разочарование: соединение успешно поднимается, авторизация проходит но тут же разрывается. В журнал ошибок nginx в это время выводится сообщение о неожиданном ответе upstream-сервера, в котором перед «OK» выводится список поддерживаемых расширений «CAPABILITIES». Что с этим беспокойным хозяйством делать — я не знаю, поэтому делаю откат. Возможно, кого-то тема заинтересует. Если вы, вдруг, найдете решение этого вопроса, буду рад, если расскажете мне об этом на alik [at] ibice.ru (не смотрите на дату публикации, если не убрал — значит решение еще не найдено).

Кто делает неправильно — не знаю. То ли это mail.ru дает ответ не в том порядке, в котором это предусмотрено протоколом (но тогда почему ни один клиент на это не ругается, а все работает стабильно?), то ли nginx производит авторизацию в неправильной очередности (тогда почему с другими серверами все работает отлично?).


28.05.2017, 21:03
  nginx, IMAP, SMTP, Mail.Ru, прокси.
Просмотров: 74503.
2