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環境上で再現した構成が以下です。
検証内容
- sendor-host(yamada) から sendor-smtp にメールを送信する
→この時、SMTP Authでメール送信者(yamada)に対して認証を要求する - sendor-smtp から receiver-smtp にメールをリレー(転送)する
- 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