Open25

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)レコード」が別に登録されている。

https://ascii.jp/elem/000/000/584/584417/

ハガユウキハガユウキ

メーラ(メールクライアント)は、電子メールを送受信するためのソフトウェアやアプリケーションのこと。

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通信を行うことを前提としたプロトコル ということになる。
「どうやって送るか」のプロトコルがまずあって、その上に「何を送るか」のプロトコルがあるという構造になっている。

https://wa3.i-3-i.info/word1135.html

ハガユウキハガユウキ

メールサーバを作ってみる

MailHogはGo製のツール。smtpサーバー(ポート1025)を実装しているサーバーを立ててくれる。
このサーバに対してポート1025でhttpリクエストすれば、レスポンスで管理画面が返されて、smtpサーバーに送信されたメールを確認できる。

https://github.com/mailhog/MailHog

そっか、開発環境でのテストはsmtpで送ったメールがちゃんと表示されているかがわかれば良いのか。なので、smtpサーバーに送られた内容が確認できれば良い。メールはメールサーバーに格納される。結局ユーザーはメールクライアントを使えば、メールクライアントが勝手にメールサーバーから、POP3プロトコルを使って、メールを取得することができる。

独自ドメインを差出人としてメールを送信させたいなら、SendGridやAWS SESを使った方が良さそう。

今回はMailHogのDockerイメージを使う。
https://hub.docker.com/r/mailhog/mailhog/

ハガユウキハガユウキ
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アドレス(またはドメイン) アプリケーションを特定するためのポート番号

http://srgia.com/docs/rfc854j.html
https://ja.wikipedia.org/wiki/Telnet

ハガユウキハガユウキ

SMTPプロトコル

  • SMTPはメールサーバーにメールを送信するためのプロトコル
  • SMTPには2つの使い方がある
    • 一つはユーザーがメールクライアント(メーラともいう。gmailとかが代表例)を利用して、メールサーバにメールを送信するという使われ方
      • メールを送信する際には、差出人のメールアドレスのドメインから、そのドメインに紐づくメールサーバーを特定して、SMTPプロトコルに従ってメールを送信する(おそらく)。
      • この場合、メール受信者はPOP3プロトコルを利用して、メールサーバからメールを取得する。この場合、メール送信者とメール受信者が利用するメールサーバーは同一でなくてはならない。つまり、送信者と受信者のメールアドレスのドメイン名が同じでなくてはならない(MXレコードでドメイン名にメールサーバが紐づいているから)
    • もう一つはメールサーバから別のメールサーバに対してメールデータを送信するという使われ方。
    • 何故これでメールが相手に届くかというと、SMTPサーバがメールを転送してくれるためである。 SMTPによるメールの転送はバケツリレーに例えられる。SMTPサーバは、とりえあず受け取ったメールを最適と思われる次のSMTPサーバに送ります。 メールを受け取ったSMTPサーバは自分宛でなければさらに次のSMTPサーバに送ります。 このような処理が繰り返されてメールは相手に到達する。 これがメールサーバ間で利用されるSMTPである。

https://japan.zdnet.com/article/20086914/

https://www.geekpage.jp/technology/ip-base/mail-2.php

ハガユウキハガユウキ

SMTPはメールオブジェクトを転送する。メールオブジェクトはエンベロープとコンテンツを含む。

ハガユウキハガユウキ

エンベロープは一連のSMTPプロトコルユニットとして送信される
エンベロープは、発信者アドレス、一つまたは複数の受信者アドレス、オプションのプロトコル拡張の要素の3つで構成されている。

コンテンツは、DATAプロトコルユニットの中で送信される
コンテンツは、ヘッダ部とボディの2つの部分を持つ
ヘッダ部はヘッダフィールドの集合から構成されている。ボディはMIMEに従って定義される

ハガユウキハガユウキ

メール送信の処理をするとメール処理の時間が長いのでリクエストした時のレスポンスが遅くなってしまいます。これを防ぐためにメール処理は非同期に行うことがあります。

メール送信は非同期でやるのが鉄則。あんなsmtpプロトコルのやりとりでユーザーを待たせられない。
https://wapa5pow.com/posts/2023-03-12--asyn-jon-on-gke