
Как вы знаете, на территории Украины в последнее время почта от Mail.Ru перестала открываться. С доступом к веб-версии все легко — там проблемы решаются в несколько кликов, а вот с IMAP-клиентом веселее. У меня в телефоне, например, работает штатная почтовая программа от Apple, но уже неделю я не могу получать почту в то время, когда я подключен к Интернету от сотовой сети. А я, надо сказать, мобильным Интернетом от «Vodafone» пользуюсь исключительно для получения почты на мобильный, в остальном он мне без надобности. Решил исправить проблему оригинально, но из этого так ничего и не вышло. Пишу здесь на случай, если кто-нибудь, кто лучше меня понимает исходный код nginx, придумает патч, который будет в силах ситуацию исправить.
Итак, в nginx, как вы, наверное, знаете, есть модуль из базовой комплектации, который позволяет использовать веб-сервер как прокси для почты по протоколам IMAP, POP3 и SMTP с местной авторизацией. В принципе, функционал весьма интересный, поэтому мне эта идея понравилась. Для начала, собираем nginx с указанием того, что мы желаем пользоваться электронной почтой:
./configure --with-mail --with-mail_ssl_module --with-openssl=/path/to/opensslmail {
	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'] == "my@perfect.mail"){
		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'] == "my@perfect.mail"){
		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 my@perfect.mail someSecretPasswordНа этом этапе меня ожидало жестокое разочарование: соединение успешно поднимается, авторизация проходит но тут же разрывается. В журнал ошибок nginx в это время выводится сообщение о неожиданном ответе upstream-сервера, в котором перед «OK» выводится список поддерживаемых расширений «CAPABILITIES». Что с этим беспокойным хозяйством делать — я не знаю, поэтому делаю откат. Возможно, кого-то тема заинтересует. Если вы, вдруг, найдете решение этого вопроса, буду рад, если расскажете мне об этом на alik [at] ibice.ru (не смотрите на дату публикации, если не убрал — значит решение еще не найдено).
Кто делает неправильно — не знаю. То ли это mail.ru дает ответ не в том порядке, в котором это предусмотрено протоколом (но тогда почему ни один клиент на это не ругается, а все работает стабильно?), то ли nginx производит авторизацию в неправильной очередности (тогда почему с другими серверами все работает отлично?).


  , , , , .