ククログ

株式会社クリアコード > ククログ > hbstudy#15発表資料: milter managerで簡単迷惑メール対策

hbstudy#15発表資料: milter managerで簡単迷惑メール対策

hbstudy#15でmilterについて発表しました。

milter managerで簡単迷惑メール対策

公開しているスライドの内容は実際に使ったものと異なっています1。実際に使ったものや当日の雰囲気などが気になる人はUstreamの録画を観てください。

スライドのPDFやソース、当日使ったmilterなどはスライドページからダウンロードできます。milterはスライドのソースと同じアーカイブに含まれています。

スライドはRabbitというRubyで書かれたフリーソフトウェアで作成しています。Ruby界隈ではとても有名なプレゼンツールなのですが、インフラ界隈ではあまり有名ではないので、当日使ったRabbitの機能を簡単に説明しておきます。

スライドの下にでていたうさぎとかめは、うさぎがページ数を、かめが経過時間を示しています。うさぎが前を走っていればペースが速い、かめが前を走っていれば間に合わない、というようにプレゼンテーションの進み具合が視覚的に一瞬でわかるのがよいところです。「残り7:35」と出てもいい感じで進んでいるのかどうかがわからないですよね。また、タイマー用のテキストがスライドの中にあると不自然ですが、うさぎとかめがスライドの中にいても不自然ではないので、発表者用のビューではなく、表示用のビューに表示することができるのもよいところです。PCの画面とディスプレイの内容を同じ内容にできるので、ディスプレイ側の表示だけおかしくなっている、という状態を防ぐことができます。ただし、聞いている人が発表よりもうさぎとかめの方が気になってしまうという問題があります。ここは発表者の腕でなんとかする必要があります。

また、携帯電話でRabbitを遠隔操作していました。仕組みを図示したのものがとちぎRuby会議02の資料公開にあります。今回も大体これと同じ構成です。処理の流れは以下の通りです。

  1. 携帯電話のi-modeブラウザで「次のページへ遷移」リンクを辿る ーHTTP→

  2. サーバ上で動いているHTTPサーバ(Rabrick: Rubyで書かれている)がノートPC上のRabbitプロセスの「次のページへ遷移メソッド」をdRubyを使って呼び出す ーdRuby→

  3. ーSSHトンネル→

  4. ーdRuby→ ノートPC上のRabbitプロセスの「次のページへ遷移メソッド」が呼び出されて、次のページへ遷移

システムとしてはカッコイイのですが、目の前のノートPC上で動いているRabbitを操作するためにインターネットを経由しているので多少タイムラグがあります。

それでは、以下に発表内容を簡単にまとめておきます。省略している部分が気になる場合は上述のUstreamの録画を観てください。

概要

話すこと

タイトルは「milter managerで簡単迷惑メール対策」でしたが、参加者がそれほどメール環境になじみの深い人ばかりではなさそうだったので、milter managerそのものの話は最後に少しする程度にしました。内容のほとんどはmilter managerがベースとしている技術であるmilterについてです。

milterを言葉や図だけで理解するのは少し大変なので、今回は実際にありうる例を動かしながら理解していきます。

milterについて

milterとは

milterとはSendmailが作ったメールフィルターの仕組みです。実際には、この仕組みの中で使われるネットワークプロトコルや、メールフィルターを開発するためのAPI、メールフィルターそのもののことも「milter」と呼ぶことがあります。ただ、多くの場合は文脈からどれを指しているかがわかるので、それほど混乱することはありません。

milterは2001年9月にリリースされたSendmail 8.12.0から正式サポートされています。9年の歴史がある枯れた技術といえます。また、SendmailだけではなくPostfixでもサポートされています。Postfixでは2006年6月にリリースされた2.3.0からサポートされ、現時点の最新版2.7.1ではmilterのほとんどの機能がサポートされています。こちらも4年の歴史があり、もう十分に実践に投入できるほど使われています。

つまり、現時点ではmilter2を用いてメールフィルター機能を実現することは「実験的な試み」ではなく「いくつかある有力な選択肢の1つ」といえます。

しかし、日本語でのmilterの情報が少ないことは事実です。milterに関する英語の情報はmilter.orgに集まっています。milter.orgにはmilter3を検索する機能や開発者向けの情報なども載っています。

たとえば、Technical Overview - Control Flowに載っている以下の擬似コードを見れば、複数のmilter4を同時に用いたときにどのような動作になるかはわかります5

For each of N connections {
  For each filter
    process connection (xxfi_connect)
  For each filter
    process helo (xxfi_helo)
  MESSAGE:For each message in this connection (sequentially) {
    For each filter
      process sender (xxfi_envfrom)
    For each recipient {
      For each filter
        process recipient (xxfi_envrcpt)
    }
    For each filter {
      process DATA (xxfi_data)
      For each header
        process header (xxfi_header)
      process end of headers (xxfi_eoh)
      For each body block
        process this body block (xxfi_body)
      process end of message (xxfi_eom)
    }
  }
  For each filter
    process end of connection (xxfi_close)
}

日本語でもこれと同じような情報を読むことができれば、milter利用の敷居が低くなるかもしれません。ここでは、実際に動かしながらmilter6がどのように動くのかを確認していきます。

milterの挙動

milterの挙動: 3行で

上記のmilterの動作フローをざっくりとまとめると、以下のようになります。

  • 本文は直列(前のmilterの影響を受ける)
  • 本文以外は並列(前のmilterの影響を受けない)
  • 詳細な結果は最後に返す

milterは迷惑メール対策に使われることがほとんどです。迷惑メールの手法は複雑化しているので、1つの迷惑メール対策方法で完璧ということは不可能です。複数のmilter(迷惑メール対策方法)を組み合わせて効果的な迷惑メール対策システムを構築する必要があります。

しかし、milterは上記のような動作のため、milter1の結果を使ってmilter2の挙動を変えるということが難しくなっています。milter1の詳細な結果を使いたければ、メール全体の処理が終わるのを待たなくてはいけません。milter2がエンベロープ情報(送信元や宛先)を取得した段階で実行する迷惑メール対策方法7を採用している場合は、メール全体の処理が終わるまで待つのは効率的ではありません。

milterがどのように動作するかがわかれば、複数のmilterをどのように組み合わせることが効果的かを検討することができます。そのため、milterを用いて効果的な迷惑メール対策システムを構築する場合はmilterの挙動を理解しておく必要があります。

milterの挙動の詳細

milterプロトコル

miltrの挙動を理解するためには、以下の3つの要素があることを理解するのが早道です。要素の名前はここでの説明のために便宜的に付けたもので、milterで一般的な用語ではないので注意してください。

  • ステージ: milterが処理を行うタイミング
  • アクション: milterが処理を行ったときにメールサーバに返す結果
  • メッセージ変更機能: メールフィルターとして行えること

ステージ

milterが処理を行うタイミングはSMTPのコマンド+αだけあります。どのようなタイミングかを一言解説を付けています8

  • connect: SMTPクライアントが接続してきたとき。
  • helo: SMTPクライアントがHELO/EHLOコマンドを実行したとき。
  • mail from: SMTPクライアントがMAIL FROMコマンドを実行したとき。
  • rcpt to: SMTPクライアントがRCPT TOコマンドを実行したとき。
  • data: SMTPクライアントがDATAコマンドを実行したとき。
  • header: メールサーバがメールのヘッダー1つを解析したとき。ヘッダーの数だけn回発生する。
  • end of header: メールサーバがメールのヘッダーをすべて解析し終わったとき。
  • body: メール本文を処理しているとき。
  • end of message: メール全体を処理したとき。

「メッセージ変更機能」は「end of message」のときしか実行できません。「メッセージ変更機能」は後述します。

アクション

milterは処理を行うたびにメールサーバに結果を返さないといけません。メールサーバに返せる結果は以下の通りです。

  • continue: 処理を続行する。普通はこれ。
  • accept: 受信する。このmilterは以降の処理を行わない。
  • reject: メールを受信拒否する。SMTPレベルでは500番台の受信拒否になる。
  • tempfail: メールを一時受信拒否する。SMTPレベルでは400番台の受信拒否になる。
  • discard: メールを受信するが、配送せずにそのまま廃棄する。このmilterは以降の処理を行わない。
  • quarantine: メールを受信するが、配送はしない。(本当はアクションではないので、end of messageステージのときしか使えない。)

メッセージ変更

以下のようなメッセージ変更機能があります。メールフィルターとしては十分な機能です。

  • From変更
  • To追加
  • To削除
  • 本文変更
  • ヘッダー追加
  • ヘッダー削除
  • ヘッダー変更

それでは、以下、実例を元に実際の動作を確認します。

ケース1: mail fromでaccept

実例は5つ用意しましたが、ここでは1つだけ紹介します。他はスライドやUstreamの録画を観てください。

ケース1: accept

mail fromステージでacceptアクションを返した場合です。SMTP Authをしている場合は何も処理をしないmilterが多くあります。その場合はこのような動作になります。

mail fromステージでacceptアクションを返すmitlerはRubyで実装するとこのようになります。これはスライドのアーカイブの中のmilters/milter-accept.rbに入っています。

require 'milter'

class MilterAccept < Milter::ClientSession
  def envelope_from(from)
    p from
    accept
  end

  def envelope_recipient(to)
    p to
  end
end

command_line = Milter::Client::CommandLine.new
command_line.run do |client, _options|
  client.register(MilterAccept)
end

このとき、SMTPクライアントがRCPT TOコマンドを送ったらどうなるでしょうか。

ケース1: 答え

この場合、milterはacceptを返しているのでメールを受信します。ただし、SMTPクライアントがMAIL FROMより後のRCPT TO以降のコマンドを送ってもmitler側では何も起こりません。それ以降のステージではメールサーバがmilterに通知しないからです。

発表時には実際にSMTPを話しながら動作を確認しています。この様子はUstreamの録画を観てください。また、他の例もUstreamの録画やスライドページを見てください。

milter managerが便利なところ

milter managerの特徴

例を使ってmilterの動作を確認した通り、milterを連携させることが難しいケースがあります。milter managerを使うことでそこを補うことができます。

例えば、「milter評価モード」機能を使うことによって、「milter1がreject/temp failを返した」という情報をmilter2で利用することができます。通常はend of messageまで待たなければ情報を利用することができませんが、reject/temp failはどのステージでも利用することができるので、もっと早い段階で情報を利用することができます。

以下、milter managerの利用例を1つ紹介します。他の利用例などはまた別の機会にでも紹介できるとよいですね。

管理例: ユーザ毎の設定

milter managerを使うことによってユーザ毎に迷惑メール対策方法を変えることができるシステムを構築することができます。例えば、MySQLにユーザ毎の設定を格納しておきます。格納した情報は各milterではなくmilter managerが参照してmilterの挙動を動的に制御します。各milterがそれぞれMySQLの情報を参照するようなシステムにすることもできますが、そうすると、複数のmilterを使う場合に大変です。それぞれのmilterがMySQLに対応し、さらに、MySQL内の情報をどのように使うかを判断しなければいけません。milter managerを使うことにより、その部分を一括管理することができます。

まとめ

管理例: ユーザ毎の設定

迷惑メールが多様化しているため、複数のmilterでそれに対応する必要があります。ただし、複数のmilterを利用した場合の挙動は少し複雑です。まるで理解しがたいというものではなく、少し落ち着いて考えれば理解できるものなので、milterを利用する場合は一度くらいは落ち着いてmilterの挙動を確認しましょう。

複数のmilterを利用する場合はmitler managerも一緒に導入するとより便利です。

お知らせ

宣伝1: クリアコードではプログラミングが好きな開発者を2名募集中です。興味がある人は応募してみてください。よさそうな人を知っていたら教えてあげてください。

宣伝2: クリアコードの開発者は全員オープンソースソフトウェアの開発に関わって磨いてきた技術力を持っています。オープンソースソフトウェアに関して技術的にお困りのことがあったらぜひご相談ください。もちろん、milterに関することもOKです。

  1. その場だけで使う用のものや再配布用ではないものを抜いている。

  2. これは仕組みのこと。

  3. この「milter」はmilterの仕組みを使って実現されたメールフィルターそのもののこと。

  4. これもメールフィルターそのもののこと。

  5. ちょっとウソ。milterの他のことも少し知らないとこれだけだとわからない。

  6. これもメールフィルターのこと。これ以降は注釈をつけませんがわかるはずです。

  7. 例えば、SPFやGreylisting(グレイリスト)。SPFは迷惑メール対策にも使われることが多い。

  8. わかりやすさを重視したので、厳密には違う場合もあるので注意してください。