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

18

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

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

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

Есть ли способ позволить контейнеру запустить sendmailexecuable на хосте?

Я попытался установить соединение с 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 6
Sep 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 nobody
Sep 28 23:35:52 dantooine spamd[3175]: spamd: processing message (unknown) for someone:65534
Sep 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=no
Sep 28 23:35:52 dantooine opendkim[3179]: DD457889B: can't determine message sender; accepting
Sep 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: 254E688A0
Sep 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: removed
Sep 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
Дэвид Грейсон
источник
Пожалуйста, опубликуйте заголовок вашей электронной почты (тот, который GMAIL ошибочно определил как спам)
masegaloeh,
Письмо, которое я пытался отправить, содержало Toзаголовок, Subjectзаголовок и однострочное тело. Я не знаю, как определить, какие заголовки у него были после того, как Postfix пропустил его через миллиметры, знаете как? Вот вывод в / var / log / syslog, показывающий, как он был обработан Postfix и отклонен Gmail: gist.github.com/DavidEGrayson/fbf65c8290c049a1f262
Дэвид Грейсон,

Ответы:

8

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

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

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

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

  • Электронная почта, не относящаяся к SMTP, будет обработана после очистки, прежде чем будет введена в milter. Cleanup демон был ответственен за добавление недостающих заголовков: (Resent-) From :, To :, Message-Id :, и Дата: . Поэтому при отправке в OpenDKIM milter ваше письмо будет иметь полный заголовок, даже если исходное письмо имело неполный заголовок.

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

    can't determine message sender; accepting
    

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

masegaloeh
источник
Это было поучительно; Спасибо. К сожалению, я все еще не вижу решения лучше моего текущего решения (описано в моем ответе).
Дэвид Грейсон
Очевидной причиной было исправление приложения для добавления From:заголовка в вашу электронную почту :)
masegaloeh
Но я также должен был бы добавить такие вещи, о Message-Idкоторых я не очень разбираюсь, и, вероятно, я ошибаюсь ... кажется, проще, чтобы демон очистки позаботился об этом.
Дэвид Грейсон
На самом деле, Message-ID не был обязательным, как сказано в RFC 6376 . По умолчанию обязательным заголовком был только Fromзаголовок. Но, если вы хотите сгенерировать свой собственный идентификатор сообщения, вы можете использовать рекомендации, подобные этому черновику IETF
masegaloeh
6

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

inet_interfaces = <docker0_ip>

Дополнительные внутренние рабочие детали при отправке электронной почты от docker-through-postfix -установлено-на-хосте

Сатиш Гандхэм
источник
Спасибо за ссылку! Соответствующая часть для меня , чтобы добавить что - то вроде , 172.17.0.0/16чтобы mynetworksв /etc/postfix/main.cfи service postfix restart.
Ширрмахер
5

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

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

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

Итак, я хост монтирую / var / spool / postfix / maildrop и / var / spool / postfix / public. Я могу получить почту, доставленную в / var / spool / postfix / maildrop в хост-среде, так как я смонтировал каталог очереди maildrop. Потому что я смонтировал /var/spool/postfix/public, maildropможет сигнализировать pickupо получении почты из очереди. К сожалению, используются uids и gids, если я об этом не позабочусь, это означает, что раскладка в каталоге хоста не может читать файлы спула, и что еще хуже, установка 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

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

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

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

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

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

mc0e
источник
4

Я решил, что способ, которым контейнер будет отправлять почту, заключается в том, чтобы записать ее в файл в определенном каталоге, который будет доступен как из контейнера, так и с хоста как «том» 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=$1

if [ \! \( -d "$DIR" -a -w "$DIR" \) ]
then
  echo "Invalid directory given: $DIR"
  exit 1
fi

echo "`date`: Starting mailsender on directory $DIR"

cd $DIR

while :
do
  for file in `find . -maxdepth 1 -type f`
  do
    echo "`date`: Sending $file"
    sendmail -t < $file
    rm $file
  done
  sleep 1
done

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

description "sends mails from directory"
start on stopped rc RUNLEVEL=[2345]
stop on runlevel[!2345]
respawn
exec 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команду.

Дэвид Грейсон
источник
Может быть, что-то вроде mini_sendmail будет полезным? Он используется в контейнерах, как мост между изолированным приложением контейнеров и демоном сервера sendmail в хост-системе контейнеров. cyberciti.biz/tips/... acme.com/software/mini_sendmail
Mikl
Если он отправляет письмо в Postfix через SMTP, я не думаю, что Postfix очистит письмо. Возможно, если бы у вас был MTA, который был бы более настраиваемым (или мы выяснили, как лучше настроить Postfix), он бы работал.
Дэвид Грейсон