[Site]Mailmanをrecipient_delimiter=-なPostfixで動かす
週末ということで、apt-getの後ほとんど進展がなかったMailmanのHack。
Mailmanって?
Mailmanってのは簡単に言えばメーリングリスト。特徴はWebとの連携が強くて、WebからのSubscribeだとか、アーカイブ閲覧だとか、管理だとかができてしまう、eGroupsのオープンソース版みたいなもの。
Postfixにネイティブに対応してて、aliasファイルはpostmapを自動実行してくれたり、バウンスメールを自動処理してくれたりと非常に頼もしいのだが……。一点最大の問題点が。
以下暇じゃない人はすっ飛ばして「Mailmanをrecipient_delimiter=-なPostfixで動かす最善の方法」をどうぞ。
拡張アドレス
qmailから(SPAMとかDoubleBounceに疲れ果てて)Postfixに流れた人なら判るだろうけど、拡張アドレスという便利な機能があって、someuserというユーザにsomeuser@domainは普通だけど、someuser-test@domainとかsomeuser-alert@domainというメールも配送させるという機能。qmailなら.qmail-testとか、Postfixなら.forward-testとかでprocmail呼んだり別のメールボックスに振り分けたりできた訳だ。
で、そのデリミタとして"-"が使われていたんだけど、Postfixになって(デフォルトが)"+"になってしまった。当然ながらデフォルトをえいやっと変えて(recipient_delimiter = -)前に使ってたメールアドレスをそのまま使っていた訳で。
Mailmanの利用するメールアドレス
で、その拡張アドレスが何だというと、Mailmanはデリミタが"+"であることを前提として作られていて、testというMLを作ると、以下のようなaliasを自動的に生成してくれたりする。
test: "|/var/lib/mailman/mail/mailman post test" test-admin: "|/var/lib/mailman/mail/mailman admin test" test-bounces: "|/var/lib/mailman/mail/mailman bounces test" test-confirm: "|/var/lib/mailman/mail/mailman confirm test" test-join: "|/var/lib/mailman/mail/mailman join test" test-leave: "|/var/lib/mailman/mail/mailman leave test" test-owner: "|/var/lib/mailman/mail/mailman owner test" test-request: "|/var/lib/mailman/mail/mailman request test" test-subscribe: "|/var/lib/mailman/mail/mailman subscribe test" test-unsubscribe: "|/var/lib/mailman/mail/mailman unsubscribe test"
ここまではデリミタが"-"であっても大丈夫。問題はここから。
バウンスメールの扱い
上記test-bouncesというのがエラーメールの戻り先となっている。
Return-Path: <test-bounces+user=domain.jp@mldomain.com>
いわゆるVERPというやつ。配送先でaliasかけたり転送したりしてあるメールがトラブルを起こすと、メーリングリストでは、そのトラブルの原因を探すのにかなり骨を折ることになる(場合があった)。
そのため、エラーメールがそれぞれ「どこ宛に配送されたのか」という情報を返してくれるようにするのがVERPという仕組み(厳密には嘘を言ってるかも)。
Postfixのデフォルトだと"+"がデリミタなので、$userは"test-bounces"で$extensionが"user=domain.jp"となる。
ところがデリミタを"-"にすると、$userは"test"で$extensionが"bounces+user=domain.jp"となる。そのため、バウンスがpost用の処理に廻ってしまうということ。(つまりループするんだと思う)
回避策1
まずは、メーリングリスト専用のサブドメインを作ってやる方法を試す。いわゆるバーチャルドメイン型。連携方法には2種類あって、master.cfのpipeによって処理する方法(先例の2の方)を検討。頑張ってpostfix-to-mailman.pyの先頭にある英文の説明を読む。
海外の情報を元に(DebianがMailmanインストール時に勝手に生成した)master.cfの以下の箇所を修正。
mailman unix - n n - - pipe flags=FR user=list argv=/etc/mailman/postfix-to-mailman.py ${nexthop} ${user}
これを
mailman unix - n n - - pipe flags=FR user=list argv=/etc/mailman/postfix-to-mailman.py ${nexthop} ${user}${extension?-}${extension}
こんな感じに。
結末1
warning: file /etc/postfix/master.cf: service mailman: unknown macro name: "extension?-"
Postfixは2.3なのに〜。多分このセマンティクスでは解釈されないんだろうなぁ。(;_;)
回避策2
回避策1の情報にあった、wrapperをかます方を試す。master.cfの以下の箇所を修正。
mailman unix - n n - - pipe flags=FR user=list argv=/etc/mailman/postfix-to-mailman.sh ${nexthop} ${user} ${extension}
こんな感じに。で、postfix-to-mailman.shは
test -z "$3" || DELIM=- cat | /usr/lib/mailman/bin/postfix-to-mailman.py "$1" "$2$DELIM$3"
ただこれは、あまりにも幼稚*1なので、この方式を使うなら、
test -z "$3" || DELIM=- exec /usr/lib/mailman/bin/postfix-to-mailman.py "$1" "$2$DELIM$3"
としたほうが、余計なプロセス作らずに済むのでbetter。(といえど、結局master.cfの「${user} ${extension}」の代わりに「${mailbox}」が使えるし、そうなるとwrapper不要になって、結局無意味)
結末2
エラーなし。よしよし。
bouncesの際に渡されている情報を確認すると、「postfix-to-mailman.py mldomain.com test-bounces+user=domain.jp」になってる。Pythonは学習してないので読めないのだが、まぁそれなりに読むと、第2引数が'-bounces'で終わっているかどうかを見てbounces処理に渡してる。ご〜ん。ということは、「postfix-to-mailman.py mldomain.com test-bounces」を渡さなきゃ。ということでNGなり。
ちなみに、回避策1の「${user}${extension?-}${extension}」は「${mailbox}」に置き換え判明。なんにせよこのシェルスクリプトは無意味だった。
回避策3
ならば、Postfixの持つVERP機能を使えば何とかなるのではと、探したところHatuka*nezumiさんの作ったパッチにあるドキュメントからヒントを得て、バウンスメールのenvelope-fromそのものを変えてしまうことで、PostfixがVERPのところを削除してくれたりしないかと思って実験。
結末3
自分のところのPostfixは、「default_verp_delimiters = -=」だったので、
VERP_FORMAT = '%(bounces)s-%(mailbox)s=%(host)s' VERP_REGEXP = r'^(?P<bounces>[^=]+?)-(?P<mailbox>[^=]+)=(?P<host>[^@]+)@.*$'
とか考えてみたけど、ハイフンが複数出てきた時点で曖昧になるからダメと、main.cfを
default_verp_delimiters = +=
とした上でVERP_FORMATとVERP_REGEXPを元に戻して確認したが、結局NG。なぜだ〜。多分***-***+***=***@***と、ハイフンがあるからだろうなぁ。(先に拡張アドレスを決定してしまうのではないか、と)
(ということは'%(bounces)s~%(mailbox)s=%(host)s'とかにしてもダメなんだろうなぁ)
回避策4
となると、結局どこかで個別処理して不要な部分を切り落とすか、不要な部分を無視するしかない、ということに。
結末4
じゃ、wrapperを何で記述するかといえば、
- perlはメール配送のたびに呼ばれる事を考えると重すぎるから却下。
- sh/bashは文字列操作は面倒っぽいし、bashがない人とか考えると却下。
- C/C++が良さそうだがmakeとか考えると大事過ぎ。却下。
という30秒くらいの思考を経て、googleさんに「python 入門」とか聞いてみる。
それから1時間。できましたっ。
Mailmanをrecipient_delimiter=-なPostfixで動かす最善の方法
最善の定義は「俺様の決めたこと」なので、あまり気にしないように。
前提はメーリングリスト専用のメールドメインを用意できること。そしてDebianのapt-getでMailmanを入れていること。他の方法でのインストールで同じpostfix-to-mailman.pyが入るかどうかが判らないので。多分同じだと思うけど。
追加するメーリングリスト専用のメールドメインはml.domain.comとしておく。
- /etc/mailman/mm_cfg.py
バーチャルなサーバで運用する時と同じように設定する。MTA変数をNoneにしておくことで、Postfix専用にaliasを作ったりpostmapしたりというのを抑止する。
DEFAULT_EMAIL_HOST = ml.domain.com DEFAULT_URL_HOST = ****.domain.com add_virtualhost(DEFAULT_URL_HOST, DEFAULT_EMAIL_HOST) MTA = None
以下の5行を追加する。
*** postfix-to-mailman.py.orig 2007-11-17 21:01:20.000000000 +0900 --- postfix-to-mailman.py 2007-11-17 21:11:46.000000000 +0900 *************** *** 105,114 **** --- 105,119 ---- sys.stderr.write('Did you forget to set ' 'mailman_destination_recipient_limit=1 ' 'in main.cf?') sys.exit(EX_USAGE) + # 'mylist-bounces+member=dom.ain' -> 'mylist-bounces' + pluspos = local.find('+') + if pluspos > 0: + local = local[:pluspos] + # Redirect required addresses to if local in ('postmaster', 'abuse', 'mailer-daemon'): os.execv("/usr/sbin/sendmail", ("/usr/sbin/sendmail", MailmanOwner)) sys.exit(0)
- /etc/postfix/master.cf
以下の行を編集(あるいは追加)。最後の「${mailbox}」に注意。
mailman unix - n n - - pipe flags=FR user=list argv=/etc/mailman/postfix-to-mailman.py ${nexthop} ${mailbox}
- /etc/postfix/main.cf
以下の項目を編集(あるいは追加)。
recipient_delimiter = - (多分これじゃなくても動くけど、おやくそく) relay_domains = $mydestination ml.domain.com transport_maps = hash:$config_directory/transport mailman_destination_recipient_limit = 1
- /etc/postfix/transport
新規作成する。既存のものがある人は上記main.cfのものも含めて適当な名前に変更してよろし。
ml.domain.com mailman:
作成後postmap transport とかして、/etc/postfix/transport.dbを作っておくこと。
ちなみに、このあとpostfixもmailmanもrestartしてください。main.cfとかmaster.cfはpostfixのreloadやrestart前にも読まれる可能性があるので注意したほうが良かったり。
ふぅ。長かった。
*1:コメント参照