web-dev-qa-db-ja.com

ホストのPostfixを使用してDockerコンテナーからメールを送信する

Ubuntu 14.04(Linux)サーバーを実行しています。サーバーに Postfix および OpenDKIM をインストールして構成しました。 echo hi | sendmail rootなどのコマンドを使用して自分にメールを送信できます。postfix/ opendkimはMessage-IdDateDKIM-Signatureなどのヘッダーを追加し、メールを自分に転送します個人のメールアドレス、そしてすべてがうまくいきます。

Docker コンテナで実行され、同じように簡単にメールを送信できるアプリケーションを作成したいと思います。特に、Message-Idのようなヘッダーを追加することについて心配したくありません。また、コンテナー自体の内部で構成やソフトウェアのインストールをあまり行いたくありません。

これを行う最良の方法は何ですか?

コンテナがホストでsendmail exectuableを実行できるようにする方法はありますか?

ポート25でSMTPプロトコルを使用してコンテナーからPostfixへの接続を試みましたが、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=<[email protected]>, 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=<[email protected]>, 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=<[email protected]>
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=<[email protected]>, 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
19
David Grayson

コンテナーがメールを送信する方法は、コンテナーとホストの両方からDocker "ボリューム"としてアクセスできる特定のディレクトリのファイルに書き込むことであると判断しました。

指定したディレクトリからメールを読み取り、sendmailに送信してから削除するmailsender.shというシェルスクリプトを作成しました。

#!/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ディレクトリを作成し、-v /var/mailsend:/var/mailsendコマンドに引数docker runを追加して、Dockerコンテナーからアクセスできるようにする必要があります。

4
David Grayson

有効な解決策があるので、ここでは、Telnet to Postfix(SMTP)とsendmail(非SMTP)を使用する場合のさまざまな動作について説明します。

ちなみに、OpenDKIMは Milterメカニズム を指定してpostfixによって呼び出されます。この 公式ドキュメント を介して、Postfixでどのようにmilterを実装するかについての情報を得ることができます。ここに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に挿入される前にクリーンアップによって処理されます。 クリーンアップデーモン は、欠落しているヘッダーを追加する責任がありました:(Resent-)From:、To:、Message-Id:、および日付:。したがって、元のメールに不完全なヘッダーがあったとしても、OpenDKIM milterに注入されると、メールには完全なヘッダーが含まれます。

  • SMTP電子メールは、クリーンアップ処理が行われる前にOpenDKIM milterに挿入されます。したがって、元のメールのヘッダーが不完全だった場合、opendkimはメールへの署名を拒否することがあります。 From:ヘッダーは必須でした( RFC 6376 を参照)。メールにヘッダーがない場合、OpenDKIMは拒否しますメールに署名して警告を出す

    can't determine message sender; accepting
    

私はdockerを使用したことがないので、コンテナー内のsendmail/pickupの制限がわかりません。 David Graysonの回避策は、OpenDKIMがメッセージに署名することを保証するのに十分安全だったと思います。

8
masegaloeh

セットinet_interfacesにあるPostfix構成でdocker0をドッカーブリッジ(/etc/postfix/main.cf)にポイントする必要があります

inet_interfaces = <docker0_ip>

sending-email-from-docker-through-postfix-installed-on-the-Host の内部作業の詳細

7
Satish Gandham

私は現在同じ問題に取り組んでいるので、これは半分の答え、または少なくとも半分テストされたものです。誰かが私が逃したものを肉付けするのを手伝ってくれることを望んでいます。

OP(David Grayson)からの回答は、ポストドロップメールスプールの再発明のように聞こえますが、そのメールスプールを使用することは有望なアプローチのように聞こえるので、ここで私がたどり着きました。

Postfixが提供する/ usr/bin/sendmail互換インターフェースは、メールをpostdrop(sgid postdrop)に渡し、メールを/ var/spool/postfix/maildropのメールドロップキューに格納できるようにします。これは、Dockerコンテナーで発生します。 postfixの残りの部分はコンテナで実行する必要がないはずです。

つまり、私は/ var/spool/postfix/maildropと/ var/spool/postfix/publicをホストマウントしています。メールドロップキューディレクトリをマウントしたので、ホスト環境で/ var/spool/postfix/maildropにメールを配信できます。マウントしたので/var/spool/postfix/publicmaildropは、キューからメールを収集するようにpickupに通知できます。残念ながら、私が対処しない限り、uidsとgidsが関係します。つまり、ホストディレクトリでのピックアップはスプールファイルを読み取ることができず、さらに悪いことに、Postfixのインストールでは、ホスト環境のメールドロップディレクトリの権限がめちゃくちゃになります。

それでも、これはうまくいくようです:

$ 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 [email protected]

$ 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を1回インストールするだけでよいと思います。これらのuidとgidは、すべてのコンテナが継承するベースイメージに設定します。

これは本当に良いアプローチなのかと思いますが。このような単純なメール構成で、配信を再試行するためにコンテナーでデーモンが使用されていない場合は、msmtpのような単純なローカルMTAの方が適切な場合があります。 TCPを介して、同じホスト上のリレーに配信され、そこでスプーリングが発生します。

Msmtpアプローチの懸念事項は次のとおりです。

  • 送信先のSMTPリレーが使用できない場合、メールを失う可能性が高くなります。それが同じホスト上のリレーである場合、ネットワークの問題の可能性は低いですが、リレーコンテナーを再起動する方法に注意する必要があります。
  • パフォーマンス?
  • 大量のメールが通過した場合、メールがドロップされ始めますか?

一般に、共有Postfixスプールアプローチは、設定が壊れやすい構成である可能性が高くなりますが、実行時に失敗する可能性は低いです(リレーが利用できないため、メールがドロップされます)。

5
mc0e