PostfixとDovecotで学ぶメール配送技術の基礎

記事タイトルとURLをコピーする

3月よりカスタマーサクセス部に異動しました山﨑です。

弊社内ではSlackがメインのコミュニケーションツールであるため、メールを頻繁に利用することはありません。

ただ、私たちが普段なにげなく利用しているメールの仕組みが気になったので少し調べて検証してみました。

メール配送の仕組み

メール配送の仕組み

Linuxにおけるメール送信ではMTA(Mail Transfer Agent)、MDA(Mail Delivery Agent)、MUA(Mail User Agent)の3つのコンポーネントが登場します

  • MUA:いわゆるメールソフト(Outlook, Thunderbird)
  • MTA:メールの転送を担い、プロトコルはSMTPを使用(postfix, exim)
  • MDA:メールの配送(仕分け)を担い、プロトコルはPOPやIMAPを使用(dovecot)

まず、クライアントAはMUAを使ってメールを作成し、サーバーA(MTA)にメールを送信します。この時、必要に応じてメール本文や通信経路を暗号化し、SMTPサーバーからの認証要求に応えます。

次に、サーバーA(MTA)はDNSサーバーに対してサーバーB(MTA)のドメインの名前解決を行い、メールを転送します。

メールを受信したサーバーB(MTA)は、エンベロープFromのドメインのDNSサーバーに対してSPF/DKIMレコードを問い合わせることで、メール送信元のSMTPサーバーの適当性や改ざんの有無を検証します。そしてDMARCによって定義されたポリシーに従ってメールを受信したり、破棄するなどの処理を行います。受信したメールはサーバーB(MDA)に渡し、MDAはディレクトリごとにメールを仕分けします。

クライアントBは、POPまたはIMAPを利用してサーバーBにアクセスし、暗号化したメールを復号化した後に閲覧します。

サーバーBへのアクセスにはPOPとIMAPの両方が利用可能ですが、POPはクライアントBにメールをダウンロードするためクライアントBのディスク容量を逼迫する可能性がありますがオフラインでも閲覧可能です。一方でIMAPはサーバーBに直接アクセスして閲覧するためディスク容量が逼迫する心配はありませんが、オフラインではメールを閲覧できません。

検証の概要

「メール配送の仕組み」について理論的には理解できても動作イメージが湧かなかったので実際に検証してみました。

構成

メール配送の仕組み(検証用)

今回検証するメール配送のプロセスは以下のように一部簡略化したものです。

メール配送の仕組み(検証用)

①まず、クライアントAはMUAを使ってメールを作成してサーバーA(MTA)にメールを送信します。この時、SMTPサーバーからの認証要求に応えます。

②メールを受信したサーバーB(MTA)は、受信したメールをサーバーB(MDA)に転送します。

③サーバーB(MTA)はサーバーB(MDA)にメールを渡し、ディレクトリごとにメールを仕分けします。

④クライアントBは、POPまたはIMAPを利用してサーバーBにアクセスしてメールを閲覧します。

AWS環境

「メール配送の仕組み」で整理した内容をAWS環境上で再現した構成が以下です。

AWS環境

検証内容

  1. sendor-host(yamada) から sendor-smtp にメールを送信する
    →この時、SMTP Authでメール送信者(yamada)に対して認証を要求する
  2. sendor-smtp から receiver-smtp にメールをリレー(転送)する
  3. receiver-host(mugi) から receiver-smtp にIMAPでアクセスしてメールを閲覧する

環境

  • OS:Red Hat Enterprise Linux release 8.9 (Ootpa)
  • ミドルウェア:Postfix、dovecot
  • 送信側のドメイン:swx.example
  • 受信側のドメイン:kinako.example
  • DNSサーバー:なし(インスタンス内の「/etc/hosts」で宛先メールサーバーの名前解決を実施)
  • メールソフト:なし(今回はすべてコマンド操作で実施)

事前準備

sendor-smtp(サーバーA)

postfix

「/etc/postfix/main.conf」でpostfixの各種設定を行ないます。以下、デフォルト設定から変更したものの一部抜粋です。

/etc/postfix/main.conf
home_mailbox = Maildir/
inet_interfaces = all
inet_protocols = all
masquerade_domains = swx.example
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
mydomain = swx.example
myhostname = sendor.swx.example
mynetworks_style = subnet

smtp_host_lookup = native

smtpd_recipient_restrictions = permit_sasl_authenticated,reject_unauth_destination
smtpd_sasl_auth_enable = yes
smtpd_sasl_path = private/auth
smtpd_sasl_type = dovecot

「mynetworks_style = subnet」とすることで、同一サブネットから転送されたメールのリレーを許可しています。これを設定しておかないとsendor-host(クライアントA)からメールを送信する際に失敗します。

今回は Dovecot-SASL を用いてメール送信者の認証を行うため、「smtpd」に関する設定をいくつか追加しています。また、認証用のユーザー(yamada) を予め作成しておきます。

[ssm-user@ip-10-0-128-36 ~]$ sudo useradd yamada
[ssm-user@ip-10-0-128-36 ~]$ sudo passwd yamada
/etc/hosts

今回はテスト用ドメイン(.example)を利用しているためDNSサーバーを用意しておらず、名前解決は「/etc/hosts」で制御しています。

$ cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.0.149.51 kinako.example
10.0.128.36 swx.example

そのため、postfixが「/etc/hosts」を見るように「/etc/postfix/main.conf」では「smtp_host_lookup = native」を設定しています。

dovecot

「/etc/dovecot」配下の各種設定ファイルでdovecotの設定を行ないます。以下、デフォルト設定から変更したものの一部抜粋です。

/etc/dovecot/dovecot.conf
protocols = imap pop3 submission
listen = *, ::
/etc/dovecot/conf.d/10-master.conf

この設定でdovecotのSASLを有効にします。

service auth {

  # Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
}
/etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = no
auth_mechanisms = plain login
/etc/dovecot/conf.d/10-ssl.conf
ssl = no
/etc/dovecot/conf.d/20-submission.conf

この設定でdovecotによるSASL認証後にメールをリレーするメールサーバーを指定します。

submission_relay_host = kinako.example

receiver-smtp(サーバーB)

postfix

「/etc/postfix/main.conf」については sendor-smtp(サーバーA)と同様に各種設定を行ないます。

以下、デフォルト設定から変更したものの一部抜粋です。

/etc/postfix/main.conf
home_mailbox = Maildir/
inet_interfaces = all
inet_protocols = all
masquerade_domains = kinako.example
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
mydomain = kinako.example
myhostname = receiver.kinako.example
mynetworks_style = subnet
smtp_host_lookup = native

dovecot

クライアントBがPOPまたはIMAPを利用して、サーバーBからメールを閲覧できるようにdovecotの設定を変更します。以下、デフォルト設定から変更したものです。

/etc/dovecot/dovecot.conf
protocols = imap pop3 submission
listen = *, ::
/etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = no
/etc/dovecot/conf.d/10-ssl.conf
ssl = no

メールを送信してみる

sendor-host(クライアントA)

インターネット向けのSMTP通信(25ポート)は、スパムメール防止のために通常はブロックされているため(OP25B - Outbound Port 25 Blocking)通常はサブミッションポート(587)を使用する必要があります。

今回はインターネットに出ずにVPCネットワーク内部でのSMTP通信のため25ポートでも通信は可能ですが、一般的にSMTP Authで利用される587ポートを使ってメールを送信します。

サーバーAにSMTP接続して、メールを作成後に送信します。

[yamada@ip-10-0-143-246 ~]$ curl -v telnet://10.0.128.36:587
* Rebuilt URL to: telnet://10.0.128.36:587/
*   Trying 10.0.128.36...
* TCP_NODELAY set
* Connected to 10.0.128.36 (10.0.128.36) port 587 (#0)
220 ip-10-0-128-36.ap-northeast-1.compute.internal Dovecot ready.
EHLO swx.example
250-ip-10-0-128-36.ap-northeast-1.compute.internal
250-8BITMIME
250-AUTH PLAIN LOGIN
250-BURL imap
250-CHUNKING
250-ENHANCEDSTATUSCODES
250-SIZE
250 PIPELINING

ここまできたら、「AUTH LOGIN」を入力して、base64でエンコードしたユーザー名とパスワードを入力して認証を行います。認証に成功したら「235 2.7.0 Logged in.」と表示されます。

echo -n 'ユーザー名' | base64
echo -n 'パスワード' | base64
AUTH LOGIN
334 VXNlcm5hbWU6
{base64でエンコードしたユーザー名}
334 UGFzc3dvcmQ6
{base64でエンコードしたパスワード}
235 2.7.0 Logged in.

あとはメールを作成し、送信するだけです。

MAIL FROM:<yamada@swx.example>
250 2.1.0 Ok
RCPT TO:<mugi@kinako.example>
250 2.1.5 Ok
DATA
354 OK
FROM: yamada@swx.example
TO: mugi@kinako.example
Subject: This is a test mail
Hello this is sample mail using port 587.
.
250 2.0.0 Ok: queued as B2B85300C36B
QUIT
221 2.0.0 Bye

sendor-smtp(サーバーA)

ログを見ると、sendor-host(クライアントA)から送信されたメールが、サブミッションポートで認証され、receiver-smtp(サーバーB)へリレーされていることが確認できました。

[ssm-user@ip-10-0-128-36 ~]$ sudo tail /var/log/maillog
Apr  7 02:24:51 ip-10-0-128-36 dovecot[8241]: submission-login: Login: user=<yamada>, method=LOGIN, rip=10.0.143.246, lip=10.0.128.36, mpid=8829, session=<YiZoZHgVNKEKAI/2>
Apr  7 02:25:38 ip-10-0-128-36 dovecot[8241]: submission(yamada)<8829><YiZoZHgVNKEKAI/2>: Successfully relayed message: from=<yamada@swx.example>, size=120, id=+HPxLgAEEmZ9IgAA8pw9+g, nrcpt=1, reply=`250 2.0.0 Ok: queued as E2573300C36C'
Apr  7 02:25:40 ip-10-0-128-36 dovecot[8241]: submission(yamada)<8829><YiZoZHgVNKEKAI/2>: Disconnected: Logged out (state=READY) in=193 out=111

receiver-host(クライアントB)

IMAPを使ってメールを閲覧してみると、きちんとメールを閲覧することができました。

[ssm-user@ip-10-0-147-166 ~]$ curl -v telnet://10.0.149.51:143
* Rebuilt URL to: telnet://10.0.149.51:143/
*   Trying 10.0.149.51...
* TCP_NODELAY set
* Connected to 10.0.149.51 (10.0.149.51) port 143 (#0)
* OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ AUTH=PLAIN] Dovecot ready.
a01 LOGIN mugi xxxx
...
a03 FETCH 11 BODY[]
* 11 FETCH (FLAGS (\Seen \Recent) BODY[] {693}
Return-Path: <yamada@swx.example>
X-Original-To: mugi@kinako.example
Delivered-To: mugi@kinako.example
Received: from ip-10-0-128-36.ap-northeast-1.compute.internal (swx.example [10.0.128.36])
        by receiver.kinako.example (Postfix) with ESMTP id E2573300C36C
        for <mugi@kinako.example>; Sun,  7 Apr 2024 02:25:04 +0000 (UTC)
Received: from swx.example ([10.0.143.246])
        by ip-10-0-128-36.ap-northeast-1.compute.internal with ESMTPA
        id +HPxLgAEEmZ9IgAA8pw9+g
        (envelope-from <yamada@swx.example>)
        for <mugi@kinako.example>; Sun, 07 Apr 2024 02:25:04 +0000
FROM: yamada@swx.example
TO: mugi@kinako.example
Subject: This is a test mail

Hello this is sample mail using port 587.
)
a03 OK Fetch completed (0.002 + 0.000 + 0.001 secs).

まとめ

今回の検証でメール配送のイメージが少し湧きました。実際はSPF/DKIM/DMARCを使って送信ドメイン認証をしたり、メール本文の暗号化、通信の暗号化など、様々な実装が必要となりますが、それは追々やっていきたいと思います。

山﨑 翔平 (Shohei Yamasaki) 記事一覧はコチラ

カスタマーサクセス部所属。2019年12月にインフラ未経験で入社し、AWSエンジニアとしてのキャリアを始める。2023 Japan AWS Ambassadors/2023-2024 Japan AWS Top Engineers