Как вы знаете, на территории Украины в последнее время почта от Mail.Ru перестала открываться. С доступом к веб-версии все легко — там проблемы решаются в несколько кликов, а вот с IMAP-клиентом веселее. У меня в телефоне, например, работает штатная почтовая программа от Apple, но уже неделю я не могу получать почту в то время, когда я подключен к Интернету от сотовой сети. А я, надо сказать, мобильным Интернетом от «Vodafone» пользуюсь исключительно для получения почты на мобильный, в остальном он мне без надобности. Решил исправить проблему оригинально, но из этого так ничего и не вышло. Пишу здесь на случай, если кто-нибудь, кто лучше меня понимает исходный код nginx, придумает патч, который будет в силах ситуацию исправить.
Итак, в nginx, как вы, наверное, знаете, есть модуль из базовой комплектации, который позволяет использовать веб-сервер как прокси для почты по протоколам IMAP, POP3 и SMTP с местной авторизацией. В принципе, функционал весьма интересный, поэтому мне эта идея понравилась. Для начала, собираем nginx с указанием того, что мы желаем пользоваться электронной почтой:
./configure --with-mail --with-mail_ssl_module --with-openssl=/path/to/openssl
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;
}
}
Теперь немного о том самом 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);
}
?>
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;
}
}
Ну все, вроде бы готово. Пробуем подключиться с клиента. Фигушки! Процесс подключения начинается и вполне себе успешно виснет. В чем же дело? А дело, как выяснилось, очень простое! «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 производит авторизацию в неправильной очередности (тогда почему с другими серверами все работает отлично?).