Goでメールサーバーを作る
やること
- メールの仕組みについて深ぼる
- メールサーバーを作る
電子メール(Email)
- 電子メールとはインターネット上で送る郵便のようなもの。文章や画像データなど、コンピュータで扱えるさまざまな情報を送ることができる。
電子メールの仕組み
- 初期の電子メールでは、電子メールの送信者が利用しているコンピュータと宛先のコンピュータ間で、直接TCPコネクションが貼られて電子メールが配送されていた。この時メールの送信にはSMTPプロトコルが使用されている。
- しかし、この方法では両方のホストの電源が入っていて、常にインターネットに接続されていなければメールを配信できない。仮に相手のコンピュータの電源が入っていなくて通信できない場合、しばらく時間を置いてから再度メールの配信を試みていた。
- メールを送信するコンピュータの電源を落とした場合、電源が入るまでメールを送信することはできない。
- メールを受信するコンピュータは電源が落ちているとメールを受信できない。そしてメールを受信するコンピュータのホストがインターネットに接続していないときにはメールを受信できない。
- この方法は電子メールの信頼性を高める上では非常に良い方法だったが、時差の影響をクリアすることができない。そのため、電子メールの送信者のコンピュータと受信者のコンピュータの間で直接TCP接続をするのではなく、電源を切らないメールサーバーを経由するようになった。そして、受信者がメールサーバーから電子メールを受け取るPOPというプロトコルが標準化された
メールアドレスの構造とMXレコードについて
- メールアドレスは、「ユーザー名@ドメイン名」という構造になっている。
- メールアドレス中のどこにもメールサーバを示す部分がない。そのため、まずはDNSサーバーにアクセスして、DNSサーバが管理しているMXレコードを見て、メールアドレスのドメイン名からサーバーのドメイン名を特定する。その後DNSサーバーにアクセスして、Aレコードを見て、メールサーバーのドメイン名に紐づくIPアドレスを取得する。
- DNSサーバーは、Aレコード(ドメインに紐づくIPアドレスの対応関係)を管理している。ホスト名の問い合わせに対してはこのAレコードが参照される。DNSサーバーが管理しているMX(Mail eXchanger)レコードは、メールアドレスのドメインに紐づくメールサーバーのドメインの対応関係を管理している。
DNSサーバには名前とアドレスを対応づけた“Aレコード”という情報が登録されており、ホスト名の問い合わせに対してこのAレコードが参照される。しかし「メールサーバはどちら?」とコンピュータの役割で問い合わせられても、Aレコードのどれが対応しているのか判断はできない。そのためDNSサーバには、そのドメインのメールサーバ名を定義する「MX(Mail eXchanger)レコード」が別に登録されている。
メールボックスは、メールサーバーのハードディスク上に存在する
メーラ(メールクライアント)は、電子メールを送受信するためのソフトウェアやアプリケーションのこと。
gmailは代表的なメールクライアント。
「何を」伝えるか に関するプロトコルは、提供したいサービスによって変わってきます。
なぜなら、何を相手に伝えなければいけないかは、サービスによって変わってくるからです。
例えば、 メールを送るサービス であれば、
- 自分のメールアドレス
- 宛先メールアドレス
- タイトル
- 本文
などを相手に伝える必要があるでしょう。
同様に、 Webサービス であれば、
- リクエストするWebページのURL
- リクエストの種類(Webページが見たいのか、入力したフォームのデータを送りたいのか、など)
- HTTPを使うのか、HTTPSを使うのか
- Cookieの値は何か(Cookieについては本書後半で詳説します)
などを相手に伝える必要があります。
当然、メールを送るサービスとWebサービスでは違うフォーマットで相手にメッセージを伝えることになりますし、それはプロトコルが変わってくるということを意味しています。
(ちなみにメールを送るときはSMTP、メールを受け取るときはPOPというプロトコルが使われます)
しかし、HTTPもSMTPもPOPも、全てメッセージのフォーマットに関する約束事であり、送る側も受け取る側も、メッセージは「漏れなく順序よく」届くことは大前提として作られています。
つまり、HTTPもSMTPもPOPも、 TCP通信を行うことを前提としたプロトコル ということになります。
「どうやって送るか」のプロトコルがまずあって、その上に「何を送るか」のプロトコルがあるという構造になっているわけです。
大事なこと
「何を」伝えるか に関するプロトコルは、提供したいサービスによって変わってくる。
HTTPもSMTP(simple transfer protool)もPOP(Post Office Protocol )も、全てメッセージのフォーマットに関する約束事であり、送る側も受け取る側も、メッセージは「漏れなく順序よく」届くことは大前提として作られている。つまり、HTTPもSMTPもPOPも、 TCP通信を行うことを前提としたプロトコル ということになる。
「どうやって送るか」のプロトコルがまずあって、その上に「何を送るか」のプロトコルがあるという構造になっている。
メールサーバを作ってみる
MailHogはGo製のツール。smtpサーバー(ポート1025)を実装しているサーバーを立ててくれる。
このサーバに対してポート1025でhttpリクエストすれば、レスポンスで管理画面が返されて、smtpサーバーに送信されたメールを確認できる。
そっか、開発環境でのテストはsmtpで送ったメールがちゃんと表示されているかがわかれば良いのか。なので、smtpサーバーに送られた内容が確認できれば良い。メールはメールサーバーに格納される。結局ユーザーはメールクライアントを使えば、メールクライアントが勝手にメールサーバーから、POP3プロトコルを使って、メールを取得することができる。
独自ドメインを差出人としてメールを送信させたいなら、SendGridやAWS SESを使った方が良さそう。
今回はMailHogのDockerイメージを使う。
version: "3"
services:
#メールサーバのコンテナ
# 本来の SMTPでは、port:1025 で受け付けている。
mail:
image: mailhog/mailhog
container_name: mailhog
ports:
- "8025:8025"
environment:
MH_STORAGE: maildir
MH_MAILDIR_PATH: /tmp
volumes:
- mail-volumes:/tmp
volumes:
# mailhog はメールをメモリ上に保存するため、Docker のコンテナを停止するとメールが消えてしまう。
# そのため、メールのデータをボリュームに保存しておく
mail-volumes:
make ps
docker compose ps -a
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
mailhog mailhog/mailhog "MailHog" mail 45 seconds ago Up 39 seconds 1025/tcp, 0.0.0.0:8025->8025/tcp
これで、簡易的なメールサーバーは用意できた。おそらくsmtpプロトコルを使ってこのサーバーに接続できるのは想定内だが、popプロトコルを使ってメールを取得できるのかが未知だな。
Telnet
- Telnetとは、遠隔地にあるサーバーやルータを端末から操作するための通信プロトコル(このプロトコルはアプリケーション層のプロトコル)。または、そのプロトコルを利用するソフトウェアである。
- RFC 854で規定されている。
- Telnetクライアントは、Telnetサーバとの間でソケットを開き、単純なテキストベースの通信を行う。基本的にはポート番号23番を使用する。
- 認証も含めすべての通信を暗号化せずに平文のまま送信するため、パスワードの窃取は比較的容易である。同様の機能を有する代替プロトコルとしては、情報を暗号化して送信するSSHが知られている。
- telnetは以下のフォーマットで実行する
telnet IPアドレス(またはドメイン) アプリケーションを特定するためのポート番号
SMTPプロトコル
- SMTPはメールサーバーにメールを送信するためのプロトコル
- SMTPには2つの使い方がある
- 一つはユーザーがメールクライアント(メーラともいう。gmailとかが代表例)を利用して、メールサーバにメールを送信するという使われ方
- メールを送信する際には、差出人のメールアドレスのドメインから、そのドメインに紐づくメールサーバーを特定して、SMTPプロトコルに従ってメールを送信する(おそらく)。
- この場合、メール受信者はPOP3プロトコルを利用して、メールサーバからメールを取得する。この場合、メール送信者とメール受信者が利用するメールサーバーは同一でなくてはならない。つまり、送信者と受信者のメールアドレスのドメイン名が同じでなくてはならない(MXレコードでドメイン名にメールサーバが紐づいているから)
- もう一つはメールサーバから別のメールサーバに対してメールデータを送信するという使われ方。
- 何故これでメールが相手に届くかというと、SMTPサーバがメールを転送してくれるためである。 SMTPによるメールの転送はバケツリレーに例えられる。SMTPサーバは、とりえあず受け取ったメールを最適と思われる次のSMTPサーバに送ります。 メールを受け取ったSMTPサーバは自分宛でなければさらに次のSMTPサーバに送ります。 このような処理が繰り返されてメールは相手に到達する。 これがメールサーバ間で利用されるSMTPである。
- 一つはユーザーがメールクライアント(メーラともいう。gmailとかが代表例)を利用して、メールサーバにメールを送信するという使われ方
SMTPはメールオブジェクトを転送する。メールオブジェクトはエンベロープとコンテンツを含む。
メール送信の処理をするとメール処理の時間が長いのでリクエストした時のレスポンスが遅くなってしまいます。これを防ぐためにメール処理は非同期に行うことがあります。
メール送信は非同期でやるのが鉄則。あんなsmtpプロトコルのやりとりでユーザーを待たせられない。