Ryo.F (3896) の日記
check_compat/sendmailと全メールの保存
メールサーバを構築するに当たって、とあるお客さんの要求。
- ドメイン内部のみでやり取りできるユーザ(以下、制限ユーザと呼ぶ)とそうでないユーザ(つまり、他のドメインともメールをやり取りできる。以下、一般ユーザと呼ぶ)に分ける
- メールサーバを通過するすべてのメールのカーボンコピーをとる
前者については、sendmailのcheck_compatをhackすれば実現できる。後者については、後で議論する。
前者を実現するためには、制限ユーザを登録するデータベースを用意し、これとマッチする場合は、外部とのやり取りを許さないよう設定する。つまり、外部へのメールの転送を許さないし、外部からメールを受け取ることも許さない。これには、送信者(sender)と受信者(recipient)の両方を同時に検査する必要があり、check_compatルールで対処する。他のcheck_mail・check_rcptはそれぞれ、送信者・受信者のいずれかしか検査できないし、check_relayはメールサーバのアドレスを検査するものだからだ。
制限ユーザを登録するデータベースだが、これには二通りの実現方法がある。一つは、sendmail.cfの中でクラスマクロとして定義する方法、もう一つは、例えば、/etc/mail/restricteduser.dbのようなデータベースファイルで定義する方法だ(LDAPで実現する方法も考えられるが、ここでは触れない)。前者の方が、check_compatルールを書くのが簡単になるのだが、制限ユーザを追加・削除・変更するたびにsendmailの再起動が必要になる。後者は、sendmailの再起動は不要だが、makemapコマンドによってデータベースファイルの更新をする必要があり、check_compatがちょっと難しくなる。
ルールセットとデータベースのそれぞれの具体的な実装方法は書かないが、それほど難しいものではない。
一方、すべてのメールに対するカーボンコピーの件だが、以下の二つ場合に対して、それぞれ対応する必要がある。
- 自ドメイン宛(/var/spool/mail以下に保存される)のメールと
- 他ドメイン宛(ESMTPを使って配送される)のメール
このうち、前者は簡単で、カーボンコピー用のアカウント(これをメールアーカイバと呼ぶ)を作成し、通常ユーザの.forwardに、そのアカウントへの転送と、自分自身への転送を(バックスラッシュを付けて)設定しておけばよい。問題は後者だ。
通常、他ドメイン宛のメールは、esmtpメーラで処理されるよう、sendmail.cfで指定されている。このesmtpメーラの定義(Mesmtp)をhackして、[IPC]というsendmailの内部ルーチンを呼ぶ代わりに、外部プログラムを呼び出し、外部プログラムの中でメールアーカイバにローカル配送しつつ、別のsendmailを起動してESMTPで転送すればよい。ローカル配送については、procmailコマンドを呼び出す。引数については、sendmail.cf内のlocalメーラの定義(Mlocal)を参照して欲しい。別のsendmailの呼び出しについては、当然だがデーモンとして上がっているsendmailとは別の設定ファイル(sendmail.cf)を用意しなければならない。この設定ファイルは、通常通りesmtpメーラを[IPC]で処理する。そうでなければ、メールのループが発生する。
この外部プログラムだが、それほど難しくはない。スクリプト言語で書いて数十行と言ったところだ。いつもならRubyを使うが、私に万一のことがあった場合のメンテナンス性を考慮して、Perlを使うことにした。また、動作のログをsyslogに出力するようにした。なお、このプログラムは、root権限もしくは、メールアーカイバ権限で実行される必要がある。上で述べたesmtpメーラのhackの中で指定する。
さて、この二つを組み合わせる場合、注意しなければならないことがある。それは、check_compatの呼び出し順序である。このchekc_compatは、aliasesや.forwardが展開された後に呼び出される。従って、他ドメインfoo@otherdomain.co.jpから自ドメインの一般ユーザbar@mydomain.co.jpに宛てたメールが来た場合は、上のように.forwardを指定していると、
- foo@otherdomain.co.jpからメールアーカイバへ
- foo@otherdomain.co.jpから\barへ
の二つについて検査することになる(バックスラッシュは無視してよい)。一方、自ドメインの一般ユーザbar@mydomain.co.jpから他ドメインfoo@otherdomain.co.jpに宛てたメールの場合は、そのままbar@mydomain.co.jpからfoo@otherdomain.co.jpへの検査を行うことになる。
つまり、check_compatは、aliasesや.forwardが展開された後に呼び出されるため、一語のアドレス(本当は一語に限らないけど)に注意する必要がある、ということだ。また、制限ユーザが外部から・外部へのメールを拒否された場合、送信者にエラーメールが返るが、これは当然、MAILER-DAEMON(一語)から、と言うことになる。これについても同様の注意が必要だ。
メールのカーボンコピーについては、メールアーカイバ宛のメールとして蓄積されるが、これは膨大な量になるため、ローテートを考える必要がある。これには、logrotateコマンドを使うのが簡単だ。ただ、logrotateコマンドで圧縮オプションは指定しない方が良いかもしれない。というのは、メールボックスファイルをmvした直後にはまだ、ローカルメーラがメールを書き込んでいる可能性があるためだ。これがsyslog程度であれば、一つのメッセージが短いし、そもそもUDPなのでメッセージが落ちることは十分考えられるため、mv直後にgzipしても問題はない。しかし、メールの場合は数メガバイト程度の書き込みを行っている可能性があるため、この問題を無視できないかもしれない。
ま、杞憂のような気もするが。
ところで、全メールのカーボンコピー機能を持ったアプライアンス製品もあるようだ。今まではこれらの製品は、その機能に比べ非常に高価だったが、こちらの製品は安価でかつ導入も容易だ。しかし、これはメールサーバとは別に導入しなければならない。いくら安いといっても、Ryo.Fがsendmail.cfをhackするのより安いということはない。
もし希望があれば、hack内容を公開しようかなと思ったり思わなかったり。
教えてください (スコア:1)
Re:教えてください (スコア:1)
sendmail.{cf,mc}(と{perl,ruby})が解る方なら、だいたい実装可能なように書いたつもりですが。
これで解らないとなると、導入はともかく、運用が難しいと思います。
実装はともかく、運用は無料サポートできませんので(笑)。
解らないことがあれば、ここで質問してください。できる限りお答えしたいと思います。