Kaigi on Railsに主にSMTPの話で登壇した

タイトルの通りです。CFPの審査のある登壇は2回目、LT以外では初ということで実績解除です。

kaigionrails.org

speakerdeck.com

経緯

"Dark Depths of SMTP" の執筆後からPostfixのもうちょっと深い部分について書けた可能性について考えていて、例えばPostfixは受け取ったメールをもとにコマンドを実行するということができて、それを使えば例えば電子書籍のソーシャルDRM*1が作れるのではないか、ということを考えていました。そうしたらRails 6.0でAction Mailboxという機能が降ってきて、簡単な紹介とメール技術の基礎みたいな話は昨年のFukuoka.rb #150で発表しました。個人的にその発表をした時点では実際Action Mailboxをちゃんと使うところまで行っていなかったのであまり満足でなく、そもそもRubyの話ほとんどしてないし、間に合わなかったしこんなものか…と思っていたところかなり好評でなんとその回のLT賞を頂いてしまいました。なんだ、需要あるんじゃないか…!また、前回記事にも書いてあるようにSendGridを使うサービスの運用をしたことある方からSendGrid利用の実情*2について聞けたこともあって、やっぱり情報は自分から出していかないと降ってこない、ということを思い知らされました。

speakerdeck.com

で松本で開かれる予定だったRubyKaigi合わせでSMTPの闇を語る会みたいなのを開こうとしていたのですが、RubyKaigiのオフライン開催自体が流れてしまったのは皆様御存知の通り。

そうしたらある日Kaigi on Railsというイベントの開催と、そのトークの募集が行われていることを知ります。CFPの文章と運営陣によるCFPの説明によると、「Railsに関係するようなネットワークプロトコルに関する話とかもOK」という話だったので、これはこの前のトークを完成させるしかない、そしてサンプルアプリとしてちょうどいいネタになるのは冒頭に書いた電子書籍のソーシャルDRMあたりがよいのではないか、ということで今回のトークに至りました。

出した時点での勝算は6割くらいで、ぶっちゃけRubyコミュニティでメールの強い人は他にいるし、ほとんどRuby書いてない人がこれをやるのにふさわしいとみなされるかという点が自信がありませんでした。

やったこと

github.com

  • Rails Guidesに書いてあるとおりにPostfixと接続してメールを受信できるようにする
    • Rails Guidesに書いてあるとおりには行かなかったので試行錯誤と調査をする
  • Postfixの動作と設定パラメータを調べ、説明できるようにする
  • 登録済みのUUIDが与えられた場合、予め作っておいたPDFに対してリンクを返すようなAction Mailboxのサンプルを作る

やらなかったこと

  • 管理画面、というよりあらゆるWeb UIの作成
  • production環境での動作。本番動いてたのは全部development環境です
    • 時間と心の余裕が十分にできてから取り掛かったので作り込むには遅かったし、Rails自体ちゃんとモノを作ったのQtoolfが最後だからRails 5?
    • なお、development環境の場合Conductor UIがマウントされているが、そこはApacheBasic認証で塞いだ
  • コードの整理、レポジトリの整理
    • コミットメッセージの書き方、コミットの分割の話聞いてて耳が痛かった。し、仕事ではちゃんとやってるんだってば!!(実はそんなことはないので大反省
    • さらにひどいことに、INGRESS_PASSWORD がコミットされている。本来ならばこれは回避したい。実際動かすときは別の値を使っていますが…

内容について

SMTPをやめろ」

今回のトークでも触れた「迷惑メールフィルタが非常に強力なのでメールが届かないことがある」「IMAPサーバーの管理はストレージの管理が非常にしんどい」ということの他に、「SMTPには認証がなく、ほかの認証メカニズムと適切に組み合わせて正しく設定しないと迷惑メール送信の踏み台にされてしまう」ということがあるため、SMTPサーバー、というよりMTAを立てることは強くおすすめしません。SMTP自体が最初に出たRFC 821からもう40年が経過しようとしているプロトコルであること、End-to-Endの暗号化がプロトコルレベルでは存在しないこと、また当時のインターネット(というよりもインターネットになる前のネットワーク)が性善説をもとに運用されていたものであり悪意の攻撃者の存在を十分に織り込んで設計されていないプロトコルであることから、私はTwitterでよくSMTPをやめろと言っています。そろそろSMTPに代わるメッセージングの新しい時代が来てもいいのでは…?と思っていますが、それについてはまた別の話。

Rails Guidesに書いてある以外にPostfixでAction Mailboxやるにあたって必要なこと

1. master.cf にメールのUNIXドメインソケットでのルーティングを指定する

forward_to_rails unix - n n - - pipe
  flags=Xhq user=sylph01:sylph01 argv=/home/sylph01/keine/forward.sh ${nexthop} ${user}

${nexthop} ${user} はもしかしたら不要かもしれない。シェルスクリプトに対して標準出力で流し込んでその標準出力しか使っていないので、今回は引数は要らない説がある。

master.cfの書き方についての公式ドキュメントはこちらpipeのオプションについての公式ドキュメントはこちら

2. virtual_alias_maps, transport_maps の設定

重要なポイントは、Action Mailboxにメールアドレスをもとにルーティングをしてほしいので、任意のローカルパート*3を受け取れるようにしておきたい、が通常はサーバーに存在するユーザー名以外のローカルパートを受け取ってしまうとbounceしてしまうので、そうしないように適当な存在するユーザー名に配送するよう virtual_alias_maps を設定し、その上で自分のサーバー宛のメールは全部 forward_to_rails に流してくれ、ということを transport_maps で指定する、という点。

3. RubyRailsが見つかるようにした上で bin/rails action_mailbox:ingress:postfix .... を実行する

このへんPostfixのpipeの動作機構がちゃんとわかってたらもうちょっとスマートに書けるのかもしれませんが、PATHの追加*4を行い、Railsのルートディレクトリにcd、action_mailbox:ingress:postfix を実行しています。

受信する側でのSPF/DKIM/DMARCの扱いについて

CFPを書いた段階では「Railsの側でも迷惑メール判定できたら要らないメール処理しなくて済むよね」と思って書いていたのですが、本来はこれはMTAがRailsに渡す前にフィルタにかける必要があるものです。Envelope-Fromとか接続してきたサーバーのIPアドレスとかの情報はRailsに渡る時点では削られてしまっています。今回は postfix-policyd-spf-python とOpenDKIMを紹介しましたが、 postfix-policyd-spf-python の設定は地味にめんどくさく、DNS関係のライブラリを落としてきて入れる必要がある点がRubyではもしかしたら組み込みのresolvライブラリでなんとかできるのではないかと思ったので、RubySPF/DKIMの判定/フィルタを行うライブラリを書いてみようかという機運があります。

カンファレンスそのものについて

やっぱりオフラインで話したかったのはありますがこればかりは仕方がない。今回はDNSの話を発表している方がいて、オフラインで開催されてたら発表後かAfter Partyで間違いなく突撃していたと思います。「浸透」の話混ぜてきたのには机バシバシ叩いて笑い散らかしてました。まさか本当にこの話ぶっこむとは…!!ところでISOC-JPのIETF報告会っていうものがあるんですけど(以下略

懇親会のSpatialChatは慣れてみると非常によい体験で、今までのオンライン飲み会では5人くらいが限度でその5人でも一つのトークのトピックの奪い合いをしていたところがもっと大きい人数でも分かれてトークが成立するというのが目的に非常にあっていました。IIW #30(リンクは筆者のwriteup、英語)で用いられていたQiqoChatは複数部屋でunconferenceをするには向いていたように思うのですが、それよりもカジュアルにトピックやグループの間の行き来が可能である点がSpatialChatの強みに思いました。もっとも、CPUスペックをめちゃくちゃ要求するので、大規模利用すればするほど体験がつらくなりそうな気もしますが…。

出せるのかなあ…?基調講演にあった、「オープンソースをやるハードルを下げるためにソースを常に読めるようにしておくとよい」っていう部分はそのとおりだと思ってて、私の場合RFCやInternet-Draftを常に読めるようにすることがそれに相当するのかな、と思ったりしました。が、次までに皆さんに有用な成果が出るのか、特にあんまりRuby書かなくなってしまった身で出せるのか、というのはちょっと未知数。

*1:電子書籍に固有のコードあるいはメールアドレスを埋め込むことでコピーしてもバレるようにすること

*2:メモしてなかったので忘れてしまったぐぬぬ…Cryptic CommandのKeybaseチャット掘り返しても書いてない…orz

*3:メールアドレスの@の前の部分

*4:動かしたサーバーはパッケージマネージャやrbenv経由でRubyを利用するのではなくRubyをソースビルドしてmake installしたものを使っています。その理由として、基本的に使い捨てるつもりであったこと、パッケージマネージャのRubyが古いこと、rbenvがログインシェル以外から動作するかどうかとその動かし方について強い確証を持てなかったことがあります