Данный пост прежде всего будет интересен разработчикам, системные администраторы врятле вынесут из него что-то полезное.
Зачем нужен proxy для SMTP и почему именно Nginx
Если в вашем приложении нужно реализовать кастомный SMTP-сервер, (например, наш Debug Mail или сервисы вроде Mailjet, Mailgun и тд.), то для вас SMTP proxy-сервер будет полезен для следующих целей:
- отсев несанкционированных попыток отправки писем до попадания писем в кастомный бекенд,
- фильтрация флуда,
- балансировка - можно выбирать на какой сервер целесообразнее отправить обработку письма.
Nginx из коробки предоставляет полнофункциональный smtp proxy с свойственной для него отличной производительностью. В моем случае в проекте уже использовался Nginx для проксирования запросов к Node.js приложению и для отдачи статики, по-этому выбор в его пользу был очевиден.
Далее мы рассмотрим процесс настройки proxy-сервера и способы его взаимодействия с приложением.
Настройка
Пример конфигурации SMTP proxy-сервера (должен быть определен или подключен в корне nginx.conf):
mail {
auth_http 127.0.0.1:8000/auth;
starttls on;
ssl_certificate /etc/nginx/cert.pem;
ssl_certificate_key /etc/nginx/key.pem;
auth_http_header X-Auth-Key wow_so_secret;
# Generic port
server {
server_name debugmail;
listen 25;
protocol smtp;
proxy on;
proxy_pass_error_message on;
smtp_auth login plain;
xclient on;
}
}
Рассмотрим некоторые параметры:
server_name
- имя сервера, которое используется в приветствии SMTP-сервера,listen
- задает адрес и порт который будет слушать proxy,protocol
- smtp, также поддерживается imap и pop3, но их описание выходит за рамки данного поста,proxy_pass_error_message
- задает необходимость передачи сообщений об ошибке клиенту от проксируемого SMTP-сервера.
Остальные параметры рассмотрим в следующих разделах.
Вы наверное заметили, что нигде не задается адрес проксируемого сервера. Чтобы узнать откуда Nginx узнает его адрес см. следующий раздел.
Аутентификация
Nginx поддерживает 3 метода аутентификации SMTP: login
, plain
и cram-md5
. Для задания активных методов необходимо использовать параметр smtp_auth
значением которого является список методов через пробел. Также можно полностью отключить аутентификацию задав значение none
.
HTTP Auth сервер
Для проверки аутентификации необходимо реализовать сервер, который будет принимать по HTTP запросы от proxy сервера и возвращать результат проверки. Адрес такого сервера задается при помощи параметра auth_http
.
Логин и пароль для проверки отправляются на сервер аутентификации через заголовки Auth-User
и Auth-Pass
соответственно. В заголовке Auth-Login-Attempt
proxy сервер передает номер попытки аутентификации. После проверки в зависимости от результата сервер должен вернуть в ответе следующие заголовки:
- Если проверка выполнена успешно:
Auth-Status: OK
,Auth-Server: <host>
- хост проксируемого сервера, на который нужно отправить письмо,Auth-Port: <port>
- порт проксируемого сервера,
- Если возникла ошибка:
Auth-Status: <error-message>
- сообщение об ошибке, которое будет возвращено пользователю,Auth-Wait: <count>
- количество оставшихся попыток аутентифкации, если данный заголовок не возвращать, то соединение будет закрыто.
Независимо от результата proxy сервер ждет ответ со статусом HTTP/1.0 200 OK
.
Как вы видите адрес проксируемого сервера возвращает сервер аутентификации, что дает широкие возможности в плане балансировки нагрузки/логгирования/фильтрации и тд. Например, в зависимости от ip-адреса клиента (который передается в заголовке Client-IP
), можно направить письмо на сервер, который географически ближе к данному клиенту (GeoIP). Или можно использовать другой способ балансировки, например, в зависимости от домена пользователя, даты его регистрации или любых других данных которые предаставляет proxy сервер или которые хранятся в БД.
auth_http_header
Я не рекомендую делать ваш сервер аутентификации доступным из вне, но если у вас есть веские причины для этого, то Nginx предоставляет возможность простой способ проверки запроса при помощи shared secret
. Для настройки используется параметр auth_http_header <header-name> <shared-secret-key>
. При отправки запроса на аутентификацию proxy также будет отправлять заголовок с именем <header-name>
и значением <shared-secret-key>
. На сервере аутентификации соответственно нужно выполнять проверку данного значения.
Как альтернативный или дополнительный вариант, можно выполнять проверку по IP-адресу proxy сервера, который вероятно является фиксированным.
XCLIENT
Nginx поддерживает расширение XCLIENT. Для активации данной возможности используется параметр xclient: on
.
После активации xclient на бекенд (SMTP-сервер) будет приходить команда примерно следующего вида: XCLIENT <attr1=value1> <attr2=value2>
. Среди атрибутов команды можно выделить LOGIN
, который (как вы наверное догадались) передает логин при помощи которого пользователь аутентифицировался.
В Debug Mail у нас пароль является идентификатором проекта, к которому нужно в итоге прикрепить письмо. Соответственно одного логина нам было недостаточно, для того, чтобы понять куда сохранять email. К счастью Nginx позволяет переопределить значение атрибута LOGIN
передаваемого на бекенд. Для этого необходимо (в случае успешной проверки) возвращать дополнительный заголовок Auth-User
от сервера аутентификации, например так:
res.set "Auth-Status", "OK"
res.set "Auth-Server", settings.smtp_host
res.set "Auth-Port", settings.smtp_port
res.set "Auth-User", "#{login}##{password}"
res.send 200
SSL / TLS
Если вам нужно добавить поддержку ssl или tls, то в Nginx есть всё для этого. Для включения ssl необходимо задать параметр ssl: on
. Если же вы предпочитаете TLS, то необходимо использовать параметр starttls: on
, при этом использование TLS будет опциональным и будет зависеть от клиента. Чтобы сделать использование TLS обязательным нужно вместо on
задать значение only
.
Для дальнейшей настройки нам потребуется сертификат и секретный ключ в формате PEM. Для примера сгенерируем self-signed сертификат (для реального проекта вам вероятно потребуется купить сертификат выданный сертификационным центром):
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 50 -nodes
Далее задаем пути к сертификату и ключу в настройках Nginx (параметры ssl_certificate
и ssl_certificate_key
соответственно).
P.S.
Полная документация по всем параметрам на официальном сайте Nginx.