Отправка почты из контейнера Docker с постфиксом хоста

Я использую сервер Ubuntu 14.04 (Linux). Я установил и настроил Постфикс и OpenDKIM очень хорошо работает на сервере; Я могу отправлять электронные письма самому себе с помощью таких команд, как echo hi | sendmail root, а postfix / opendkim добавит такие заголовки, как Message-Id, Date, и DKIM-Signature, перешлите электронное письмо на мой личный адрес электронной почты, и все работает отлично.

Теперь я хотел бы создать приложение, которое работает в Докер контейнер и может отправлять электронные письма с такой же легкостью. В частности, я не хочу беспокоиться о добавлении таких заголовков, как Message-Id, и я не хочу делать очень много настроек или установки программного обеспечения внутри самого контейнера.

Каков наилучший способ сделать это?

Есть ли какой-нибудь способ позволить контейнеру запускать sendmail выполнимо на хосте?

Я попытался подключиться к Postfix из контейнера, используя протокол SMTP на порту 25, но Postfix, похоже, обрабатывает сообщения, полученные таким образом, по-другому; Я думаю, что он не добавил никаких заголовков, поэтому сообщение было сразу отклонено как спам gmail (оно даже не было достаточно хорошим, чтобы быть помещенным в мой Папку со спамом).

Здесь содержимое почтового журнала

Sep 28 23:35:52 dantooine postfix/smtpd[4306]: connect from unknown[172.17.0.95]Sep 28 23:35:52 dantooine postfix/smtpd[4306]: DD457889B: client=unknown[172.17.0.95]Sep 28 23:35:52 dantooine postfix/cleanup[4309]: DD457889B: message-id=<>Sep 28 23:35:52 dantooine spamd[3175]: spamd: connection from localhost [::1]:59471 to port 783, fd 6Sep 28 23:35:52 dantooine spamd[3175]: spamd: handle_user (getpwnam) unable to find user: 'someone'Sep 28 23:35:52 dantooine spamd[3175]: spamd: still running as root: user not specified with -u, not found, or set to root, falling back to nobodySep 28 23:35:52 dantooine spamd[3175]: spamd: processing message (unknown) for someone:65534Sep 28 23:35:52 dantooine spamd[3175]: spamd: clean message (2.5/5.0) for someone:65534 in 0.0 seconds, 331 bytes.Sep 28 23:35:52 dantooine spamd[3175]: spamd: result: . 2 - MISSING_DATE,MISSING_FROM,MISSING_MID,UNPARSEABLE_RELAY scantime=0.0,size=331,user=someone,uid=65534,required_score=5.0,rhost=localhost,raddr=::1,rport=59471,mid=(unknown),autolearn=no autolearn_force=noSep 28 23:35:52 dantooine opendkim[3179]: DD457889B: can't determine message sender; acceptingSep 28 23:35:53 dantooine postfix/qmgr[3664]: DD457889B: from=<whoever@example.com>, size=275, nrcpt=1 (queue active)Sep 28 23:35:53 dantooine postfix/smtpd[4306]: disconnect from unknown[172.17.0.95]Sep 28 23:35:53 dantooine postfix/smtp[4311]: DD457889B: to=<someone@gmail.com>, relay=gmail-smtp-in.l.google.com[2607:f8b0:4003:c05::1b]:25, delay=0.25, delays=0.12/0.01/0.03/0.09, dsn=5.7.1, status=bounced (host gmail-smtp-in.l.google.com[2607:f8b0:4003:c05::1b] said: 550-5.7.1 [fd17:8b70:893a:44bf:fe73:6c21] Our system has detected that 550-5.7.1 this message is likely unsolicited mail. To reduce the amount of spam 550-5.7.1 sent to Gmail, this message has been blocked. Please visit 550-5.7.1 http://support.google.com/mail/bin/answer.py?hl=en&answer=188131 for 550 5.7.1 more information. su20si7357528oeb.94 - gsmtp (in reply to end of DATA command))Sep 28 23:35:53 dantooine postfix/cleanup[4309]: 254E688A0: message-id=<20140928233553.254E688A0@myserver.example.com>Sep 28 23:35:53 dantooine postfix/bounce[4330]: DD457889B: sender non-delivery notification: 254E688A0Sep 28 23:35:53 dantooine postfix/qmgr[3664]: 254E688A0: from=<>, size=3374, nrcpt=1 (queue active)Sep 28 23:35:53 dantooine postfix/qmgr[3664]: DD457889B: removedSep 28 23:35:53 dantooine postfix/virtual[4331]: 254E688A0: to=<whoever@example.com>, relay=virtual, delay=0.01, delays=0/0/0/0, dsn=2.0.0, status=sent (delivered to maildir)Sep 28 23:35:53 dantooine postfix/qmgr[3664]: 254E688A0: removed

Вы должны указать inet_interfaces к мосту докера (docker0) в конфигурации postfix, расположенной в set /etc/postfix/main.cf

inet_interfaces = <docker0_ip>

Более подробная внутренняя рабочая информация на отправка электронной почты из docker через postfix, установленный на хосте

Поскольку у вас есть рабочее решение, здесь я попытаюсь объяснить различное поведение при подключении telnet к postfix (SMTP) и при использовании sendmail (не-SMTP).

К вашему сведению, OpenDKIM будет вызываться postfix с помощью Доильный механизм. Вы можете получить некоторую информацию о том, как улучшить реализацию в postfix, с помощью этого официальная документация. Вот схема крючка милтера в постфиксе.

             SMTP-only       non-SMTP             filters         filters                ^ |            ^ |                | v            | |Network ->  smtpd(8)           | |                       \       | VNetwork ->  qmqpd(8)    ->  cleanup(8)  ->  incoming                       /            pickup(8)               :Local   ->  sendmail(1)

Вы можете видеть, что sendmail-way (non-SMTP) и telnet-way (SMTP) имеют разный порядок обработки.

  • Электронное письмо, не относящееся к SMTP, будет обработано очисткой перед отправкой в milter. Демон очистки отвечал за добавление отсутствующих заголовков: (Возмущенно) От:, Кому:, Идентификатор сообщения:, и Дата:. Таким образом, ваше электронное письмо будет иметь полный заголовок при вводе в OpenDKIM milter, даже если исходное электронное письмо имело неполный заголовок.

  • Электронное письмо SMTP будет отправлено в OpenDKIM milter до того, как будет выполнена какая-либо обработка очистки. Поэтому, если в вашем исходном электронном письме был неполный заголовок, opendkim может отказаться подписывать электронное письмо. То От: заголовок был обязательным (см. RFC 6376) и если в электронном письме его нет, OpenDKIM откажется подписывать электронное письмо и выдаст вам предупреждение

      can't determine message sender; accepting

Поскольку я никогда не использую docker, я не знаю, какие ограничения на отправку почты / отправку внутри контейнера. Я думаю, что обходной путь Дэвида Грейсона был достаточно безопасным, чтобы гарантировать, что OpenDKIM подпишет сообщение.

Я решил, что способ, которым контейнер будет отправлять почту, - это записать ее в файл в определенном каталоге, который будет доступен как из контейнера, так и с хоста в качестве "тома" Docker.

Я создал сценарий оболочки под названием mailsender.sh который считывает письма из указанного каталога, отправляет их в sendmail, а затем удаляет их:

#!/bin/bash# Runs on the host system, reading mails files from a directory# and piping them to sendmail -t and then deleting them.DIR=$1if [ \! \( -d "$DIR" -a -w "$DIR" \) ]then  echo "Invalid directory given: $DIR"  exit 1fiecho "`date`: Starting mailsender on directory $DIR"cd $DIRwhile :do  for file in `find . -maxdepth 1 -type f`  do    echo "`date`: Sending $file"    sendmail -t < $file    rm $file  done  sleep 1done

Ubuntu использует upstart, поэтому я создал файл с именем /etc/init/mailsender.conf чтобы превратить этот скрипт в демона:

description "sends mails from directory"start on stopped rc RUNLEVEL=[2345]stop on runlevel[!2345]respawnexec start-stop-daemon --start --make-pidfile --pidfile /var/run/mailsender.pid --exec/path/to/mailsender.sh /var/mailsend

Я могу начать службу с start mailsender и остановить это с помощью stop mailsender. Я могу посмотреть его логи в /var/log/upstart/mailsender.log, и, конечно, я могу отслеживать это с помощью файла PID.

Вам нужно создать /var/mailsend каталог, а затем сделайте его доступным из контейнера Docker, добавив аргумент -v /var/mailsend:/var/mailsend к вашему docker run команда.

Это наполовину ответ или, по крайней мере, наполовину проверенный, поскольку в настоящее время я работаю над той же проблемой. Я надеюсь, что кто-нибудь сможет помочь конкретизировать то, что я пропустил.

Ответ от OP (David Grayson) звучит для меня как повторное изобретение почтовой катушки postdrop, но использование этой почтовой катушки звучит как многообещающий подход, так что вот к чему я пришел.

Интерфейс совместимости /usr/bin/sendmail, предоставляемый postfix, передает почту в postdrop, который является sgid postdrop, позволяя ему сохранять почту в очереди отправки почты в /var/spool/postfix/maildrop. Это должно произойти в контейнере docker. Остальная часть postfix, надеюсь, не должна запускаться в контейнере.

Итак, я устанавливаю на хост /var/spool/postfix/maildrop и /var/spool/postfix/public. Я могу получать почту, доставляемую в /var /spool/postfix/maildrop в среде хоста, поскольку я смонтировал каталог очереди отправки почты. Потому что я смонтировал /var/spool/postfix/public, maildrop может сигнализировать pickup чтобы забрать почту из очереди. К сожалению, uid и gid задействованы, если я не позабочусь об этом, а это означает, что pickup в каталоге хоста не может прочитать файлы spool, и, что еще хуже, установка postfix приводит к путанице в разрешениях для каталога maildrop в среде хоста.

Тем не менее, это, кажется, работает:

$ cat Dockerfile FROM debian:jessie# Ids from parent environment    RUN groupadd -g 124 postfix && \        groupadd -g 125 postdrop && \    useradd -u 116 -g 124 postfix    RUN apt-get update && \      DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \        postfix \        bsd-mailx    CMD echo test mail | mail myemail@example.com$ sudo docker build   ....Successfully built 16316fcd44b6$ sudo docker run   -v /var/spool/postfix/maildrop:/var/spool/postfix/maildrop \  -v /var/spool/postfix/public:/var/spool/postfix/public 16316fcd44b6

Хотя это работает, я не очень доволен жестким кодированием uid и gid. Это означает, что нельзя считать, что один и тот же контейнер работает одинаково везде. Однако я полагаю, что если вместо монтирования тома с хоста я смонтирую его из контейнера, который запускает postfix, то он никогда не будет конфликтовать, и мне нужна только одна установка postfix для получения почты из многих контейнеров. Я бы установил эти uid и gid в базовом образе, от которого наследуются все мои контейнеры.

Однако я действительно задаюсь вопросом, действительно ли это хороший подход. При такой простой конфигурации почты и отсутствии демона, используемого в контейнере для повторной попытки доставки, более подходящим может быть более простой локальный MTA, такой как msmtp. Он будет доставляться по протоколу TCP на ретранслятор на том же хосте, где будет происходить буферизация.

Проблемы, связанные с подходом msmtp, включают:

  • больше шансов потерять почту, если smtp-ретранслятор, на который она отправляется, недоступен. Если это ретранслятор на том же хосте, то вероятность сетевых проблем невелика, но я должен быть осторожен с тем, как я перезапускал контейнер ретрансляции.
  • производительность?
  • Если проходит большой поток почты, начинает ли почта отбрасываться?

В целом, подход с общим постфиксным спулем, скорее всего, представляет собой хрупкую конфигурацию для настройки, но с меньшей вероятностью приведет к сбою во время выполнения (ретрансляция недоступна, поэтому почта отброшена).

Пожалуйста, отправьте заголовок вашего электронного письма (того, которое GMAIL ошибочно идентифицировал как спам).

В электронном письме, которое я пытался отправить, были только заголовок “Кому”, заголовок “Тема” и текст из одной строки. Я не уверен, как определить, какие заголовки у него были после того, как Postfix пропустил его через фильтры, вы знаете, как? Вот вывод в /var/log/syslog, показывающий, как он был обработан Postfix и отклонен Gmail: log.txt · GitHub