В принципе, мне этого было достаточно, т.к. приложение теоретически должно было отправлять почту на ровно один корпоративный почтовый сервер. Но такое решение меня не очень устраивало, потому что задача может поменяться в любой момент. Поэтому, я попытался настроить sasl-авторизацию, которая позволяет отправлять почту авторизованным пользователям на произвольный домен.
Первым решением, которым я попробовал воспользоваться — это установить postfix вне контейнера Docker и оправлять на него прямо на порт 25 сообщения с клиента из контейнера Docker. Конечно, меня ожидало разочарование, т.к. localhhost внутри контейнера не имеет ничего общего с localhost вне контейнера.
Следовательно пришлось бы обращаться к postfix не через localhost. Да и изначально цель была такая, чтобы все нужное установить в контейнерах, и не зависеть от окружения на сервере. То есть postfix желательно было установить в контейнере.
Дополнительным плюсом установки postfix внутри контейнера является его полная закрытость от внешнего мира, если только явно не опубликовать его порт в docker-compose, что естественно легче контролировать, чем экзотические параметры в конфигурационных файлах самого postfix.
Для того чтобы можно было обращаться к postfix серверу, который работает внутри контейнера, нужно настроить авторизацию. Самым популярным решением является авторизация sasl. Я ее и решил настроить с использованием sasldb2. Как оказалось не так уж много подробных инструкций об установке sasl для postfix с использованием sasldb2. Некоторые инструкции содержали устаревшую информацию и не содержали нужных сведений. В результате, с первого раза авторизация упорно отказывалась работать. Как оказалось, две основные причины были: использование postfix сервером файловой системе в «песочнице» /var/spool/postfix/, о которой ничего не знает sasl, а также необходимость раздачи прав на файл базы данных /etc/sasldb2, который создает пользователь sasl — пользователю postfix.
Результирующий файл Dockerfile достаточно прост, хотя при поиске решения пришлось повозиться, в основном с правами пользователей, о чем я расскажу немного подробнее.
FROM ubuntu:xenial
ARG UID
RUN useradd -u $UID www-arc && apt-get update && apt-get install tzdata && echo Europe/Kiev | tee /etc/timezone && dpkg-reconfigure --frontend noninteractive tzdata && apt-get install -y postfix rsyslog sasl2-bin && postconf -e "mydestination = localhost" && postconf -e "myhostname = example.com" && postconf -e "always_bcc = www-arc@localhost" && postconf -e "smtpd_sasl_auth_enable=yes" && postconf -e "broken_sasl_auth_clients=yes" && postconf -e "smtpd_recipient_restrictions=permit_sasl_authenticated,reject_unauth_destination" && postconf -e "smtpd_client_restrictions = permit_sasl_authenticated,reject_unauth_destination" && postconf -e "smtpd_sasl_security_options = noanonymous" && echo 123456 | saslpasswd2 -c -p -u example.com postfix && ln /etc/sasldb2 /var/spool/postfix/etc/sasldb2 && adduser postfix sasl && touch /var/log/mail.log
COPY ./smtpd.conf /etc/postfix/sasl/smtpd.conf
CMD service rsyslog start && service postfix start && tail -f /var/log/mail.log
ln /etc/sasldb2 /var/spool/postfix/etc/sasldb2
По умолчанию postfix работет с файловой системой в «песочнице» /var/spool/postfix/. Там он будет искать файл с логинами /etc/sasldb2. Поэтому, задаем ссылку на файл /etc/sasldb2 из песочницы на реальный файл. Ссылка должны быть только жесткой (без параметра -s).adduser postfix sasl
вполне понятное и очень важно действие позволяет работать postfix с файлом /var/spool/postfix/etc/sasldb2.Правило
postconf -e "smtpd_recipient_restrictions=permit_sasl_authenticated,reject_unauth_destination"
как раз и позволяет отправлять почту на произвольный домен всем прошедшим авторизацию sasl клиентам.Конфигурация авторизации sasl в файле ./smtpd.conf:
log_level: 3
pwcheck_method: auxprop
auxprop_plugin: sasldb
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5 NTLM
Параметр
postconf -e "mydestination = localhost"
говорит о том, что почту для этого домена не отправлять дальше а принимать на этом хосте. Как показывает опыт, почтовые сервера, которые должны принимать почту, очень часто настроены неверно, теряют почту. Иногда это критично т.к. может вредить бизнесу, если почтовое сообщение содержит заявку клиента на приобретение товара или услуги. Поэтому данная конфигурация настроена на отправку копии локальному получателю postconf -e "always_bcc = www-arc@localhost"
. Это получатель создается с идентификатором текущего пользователя useradd -u $UID www-arc
.Текущий пользователь определяется в конфигурации docker-compose:
postfix:
build:
context: ./docker/postfix
args:
- UID
volumes:
- ./docker/postfix/mail:/var/mail
При билде текущий пользователь передается из окружения
env UID=$UID docker-compose build
.Для подключения из другого контейнера docker к этому сервису, необходимо в качестве хоста указать postfix (название сервиса из docker-compose.yml), а имя с учетом домена (postfix@example.com) и пароль. Во всех случаях имя postfix не является обязательным инастраивается в конфигурации.
Дополнение.
Конечно при решении задачи я «подглядывал» как это делают делают другие разработчики в открытых репозитариях и очень много почерпнул из этих источников. Поэтому разберу, как некоторые из конфигураций решают аналогичные задачи.
Для обзора я воспользовался ключевой фразой в поиске Google «docker sasl postfix». Поэтому, если кто-нибудь посоветует еще какие-то репозитарии, я из включу в этот список.
1) github.com/MarvAmBass/docker-versatile-postfix
Автор воспользовался не плагином postfix
auxprop_plugin: sasldb
, а стандартным средством sasl pwcheck_method: saslauthd
. Это еще один дополнительный процесс который запускается в контейнере, и который не очень удобно конфигурировать, т.к. по умолчанию он выключен. Поэтому автор применяет в своей конфигурации такие вот конструкции:RUN sed -i 's/^START=.*/START=yes/g' /etc/default/saslauthd; sed -i 's/^MECHANISMS=.*/MECHANISMS="shadow"/g' /etc/default/saslauthd
RUN sed -i 's/^OPTIONS=/#OPTIONS=/g' /etc/default/saslauthd; echo 'OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd"' >> /etc/default/saslauthd
2) github.com/cloudposse/postfix
Аналогично предыдущему используется
pwcheck_method: saslauthd
3) github.com/floriandejonckheere/docker-postfix
Автор использовал sasldb2 с плагином postfix, но отключил механизм «песочницы»:
smtp inet n - n - - smtpd
, и для запуска процессов использовал supervisor, таким образом включив в контейнер несколько процессов.4) github.com/catatnight/docker-postfix
Подобно предыдущему, автор откючил «песочницу»:
postconf -F '*/*/chroot = n'
и использовал supervisor.5) github.com/juanluisbaptiste/docker-postfix
Использует плоский файл с паролями:
echo "[$SMTP_SERVER]:587 $SMTP_USERNAME:$SMTP_PASSWORD" >> /etc/postfix/sasl_passwd
6) github.com/container-images/postfix
Отключает песочницу
sed -i 's/^smtp\(\s*\)inet\(.*\)/docker_smtp\1inet\tn\ty\tn\t-\t-\tsmtpd -v/g' "$MASTER"
Вцелом, я не утверждаю, что, например, отключение песочницы или компоновка нескольких процессов в одном контейнере является решением более неудачным, просто обращаю внимание, каким обрназом решались эти проблемы другими авторами.
Буду благодарен за дополнение этого списка репозитариев, и обещаю оперативно дополнять этот список по Вашим комментариям.
apapacy@gmail.com
8 апреля 2018 года
Комментарии (12)
akamensky
09.04.2018 12:26Мне одно непонятно — зачем?
Если надо отправить почту из приложения которое крутится внутри контейнера, можно:
а) подключиться к локальному MTA на хосте из докера (локально оно слушает всегда на 127.0.0.1:25)
или
б) отправлять по API через предназначенные для этого сервисы (а не заморачиваться с настройкой своего mail-сервера, а то адрес отправителя и IP очень быстро улетит в замечательный spam-blacklist)apapacy Автор
09.04.2018 15:29Дополнил в статье проблемы которые возникают при подключении к postfix из докера на localhost. За 25-й порт спасибо за дополнение в дальнейшем учту. По пункту б) сервер улетает в spam-лист если он действительно рассылает спам или если он не нестроен до конца. В частности, докеризация предполагает такую комплексную настойку, в частности совместно с dkim сервисом, который в этой статье не описан, но в тех репозитариях на которые даны ссылки в дополнении к статье этот вопрос решен комплексно.
Другая часть этих настроек конечно должна быть проведена в настройках домена. Что к сожалению не всегда выполняется правильно. И поэтому почта часто попадает в серые и черные списки или отвергается некоторыми серверами.akamensky
11.04.2018 08:54Другая часть этих настроек конечно должна быть проведена в настройках домена. Что к сожалению не всегда выполняется правильно. И поэтому почта часто попадает в серые и черные списки или отвергается некоторыми серверами.
Так я вот именно поэтому и спрашиваю — зачем? Настройка полноценного мейл-сервера, которому другие сервера будут доверять, это дело очень сложное (DNS A/AAAA, DNS PTR, DNS SPF это как минимум, еще добавим TLS, DKMS и прочее), что все достоинства использования контейнеров сходят на ноль, совсем.
При этом в интернете тысячи (ну или хотя бы сотни уж точно) сервисов, которые делают отправку почты через API банальным делом (например — AWS SES, не знаю какие сервисы для этого популярны в России).apapacy Автор
11.04.2018 09:14Хотя бы для этого docs.aws.amazon.com/ses/latest/DeveloperGuide/postfix.html
powernic
09.04.2018 15:29Может для бесплатной массовой рассылки в 100тыс клиентов?
apapacy Автор
09.04.2018 15:36Технически любой сервер может отправить спам и 100 тыс. и 1 млн. клиентов. Сложнее (практически невозможно) сделать так чтобы эта почта была принята принимающим сервером. Увы это так. У меня более простая задача отправить почту на корпоративный сервер админами которого я всегда могу договориться чтобы они включили мой адрес в белый список. Однако и там не без проблем это все. Т.к. всяческих фильтров иногда совершенно неожиданных для отброса спама больше чем можно себе вообразить. Например я отправляю письмо менеджеру продаж и копию на общий почтовый ящик. Почта не пропускается правилом которое гласит то два письма с одним контентом это уже спам.
Darigaaz
10.04.2018 23:24+1По-моему
postconf -e "smtpd_recipient_restrictions = permit_sasl_authenticated,reject_unauth_destination" postconf -e "smtpd_client_restrictions = permit_sasl_authenticated,reject_unauth_destination"
лучше заменить на
postconf -e "smtpd_relay_restrictions = permit_sasl_authenticated,reject_unauth_destination" postconf -e "smtpd_recipient_restrictions = reject" # (reject_unauth_destination - если хотите локальный bcc)
smtpd_recipient_restrictions
после версии 2.10 (я уверен, у вас старше) предназначен для ограничений на "локальную" доставку, вы же хотите настроить relay, поэтому лучше использовать соответствующий параметр.
smtpd_client_restrictions
— это generic ограничения
Причем, правилоreject_unauth_destination
— это правило из разделаsmtpd_recipient_restrictions
, и вsmtpd_client_restrictions
оно будет иметь силу только если стоитsmtpd_delay_reject = yes
(значение по умолчанию).
И получается что вы одно и то же написали 2 раза.
Тут написано подробнее
И последний момент
myhostname = example.com
советую все таки вписывать именно имя хоста
docker-mx.mycompany.com
(а не просто доменную часть) ведь именно так он будет представляться в HELO/EHLO при отправке почты. И на это имя вы будете заводить dkim/spf/dmark записи, а так же PTR запись в днс, а то рискуете попасть в спам.
Если представляться надо как-то иначе, то можно воспользоваться параметром
smtp_helo_name
.apapacy Автор
10.04.2018 23:38Спасибо, очень ценное дополнение. Когда проверю дополню статью и свои конфигурации. К сожалению параметров у postfix довольно много и не все сразу доходит до понимания.
antirek
докерфайл с примерами конфигов надо на гитхаб выложить, а в статье описать проблему, ключевые моменты решения, сомнения и т.д.
с гитхаба легко стянуть и попробовать, а выковыривать код с хабра — сомнительное удовольствие ))
aol-nnov
я больше скажу: в докерхабе образов, решающих данную задачу
как за банейдостаточно. :)apapacy Автор
Привел наиболее популярные ссылки на репозитарии в дополнении к статье. Если Вы знаете еще буду рад дополнить список Вашими дополнениями.
apapacy Автор
Проблему и ключевые моменты дополнил. Опасений пока не имею когда начну пользоваться наверное появятся. Сделать репозитарий на githab и выложить контейнер в общий доступ думаю. Но нужно еще провести дополнительную работу для этого. Подумать немного чтобы было более универсально. Боюсь что например bcc нужно из общедоступного репозитария выбросить т.к. кто-нибудь включит эту фичу для дублирования почты там где это совсем не нужно или даже неуместно.