postfix とdovecot (SMTP Auth) でvirtual その7(postfix とprocmail)

procmail をvirtual環境で使いたい

届いたメールを携帯で見たいというときに、できるだけ最小限のものにしたいと思って、procmailを利用してきた。こちらは、ホワイトリスト形式で広告などが配信されるようなパターンは除外している。主に顧客や非常時の連絡に使われるFromアドレス、ドメイン名を登録していくパターンで構成。

今まではOSアカウント=メールアカウントだったので、問題はなかったが、virtual環境にしたことでOSユーザーが1つになったことによるトラブルを体験した。そのことを中心にまとめてみた。

procmailの導入

# dnf install procmail

OS提供のパッケージを入れる。色々な方法があると思われるけれど、virtual環境で導入されたprocmailを参考に入れていった。
参考:Debian squeezeでvirtual mailにprocmail: 引き籠もりっぽい主夫のメモ帖
参考:Postfix + MySQL 環境で Procmail | あたがわの日記
この仕組みを文字に起こすと下記の通り。

  • Postfixがメールを受け取り、local(自分自身)/virtual の配送と判断する。
  • virtual環境では「virtual_transport = procmail」の設定にて配送処理を決める。
  • 「/etc/postfix/master.cf」に記載されているprocmail の記述に従って、procmailを起動する。
    「user=vmail argv=/usr/bin/procmail -t -m USER=${user} EXTENSION=${extension} /etc/procmailrc」
  • 「/etc/procmailrc」には引き渡されたUSER変数に従って、.procmailrcファイルを探し、存在したらそれに処理を引き渡す。(僕が定義したファイル位置はこれ。/var/spool/mail/vhosts/$DOMAIN/$USER/.procmailrc)

図に書き起こすと下記の通り。

処理概念図
処理概念図

関係している設定ファイル

/etc/postfix/main.cf

# postconf -e virtual_transport=procmail
# postconf -n | grep virtual_transport
virtual_transport = procmail

変更はpostconfコマンドを利用して設定。

/etc/postfix/master.cf

これもコマンドを利用して設定する。参考にしたのはこのサイト。Change postfix master.cf with postconf – Mind IT
-Mで作って、-Pでオプションを追加するという仕組みらしい。でも今回のprocmailの記述は-o の部分がなかったので、-Mで全部入力する形になった。

# postconf -M procmail/unix=”procmail unix – n n – – pipe flags=R user=mailuser argv=/usr/bin/procmail -t -m USER=${user} DOMAIN=${nexthop} /etc/procmailrc”

設定されているか確認するには-M 以外の入力をしなければ、一覧表示される。

# postconf -M
smtp inet n – n – – smtpd
submission inet n – n – – smtpd -o smtpd_sasl_auth_enable=yes
smtps inet n – n – – smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes
pickup unix n – n 60 1 pickup
cleanup unix n – n – 0 cleanup
qmgr unix n – n 300 1 qmgr
tlsmgr unix – – n 1000? 1 tlsmgr
rewrite unix – – n – – trivial-rewrite
bounce unix – – n – 0 bounce
defer unix – – n – 0 bounce
trace unix – – n – 0 bounce
verify unix – – n – 1 verify
flush unix n – n 1000? 0 flush
proxymap unix – – n – – proxymap
proxywrite unix – – n – 1 proxymap
smtp unix – – n – – smtp
relay unix – – n – – smtp -o syslog_name=postfix/$service_name
showq unix n – n – – showq
error unix – – n – – error
retry unix – – n – – error
discard unix – – n – – discard
local unix – n n – – local
virtual unix – n n – – virtual
lmtp unix – – n – – lmtp
anvil unix – – n – 1 anvil
scache unix – – n – 1 scache
procmail unix – n n – – pipe flags=R user=vmail argv=/usr/bin/procmail -t -m USER=${user} DOMAIN=${nexthop} /etc/procmailrc
postlog unix-dgram n – n – 1 postlogd

間違えてしまったときなど、項目削除をするときには-MX のオプションをつかう。(入力しなおしを何度もやったので覚えた。procmail をTYPOしてprcomailとかの時とかも)

# postconf -MX “procmail/unix”

/etc/procmailrc

SHELL=/bin/bash
PATH=/usr/bin:/usr/local/bin
MAILDIR=/var/spool/mail/vhosts/$DOMAIN/$USER
DEFAULT=$MAILDIR/
LOCKFILE=/var/spool/mail/vhosts/procmail.lock
LOGFILE=/var/spool/mail/vhosts/procmail.log
VERBOSE=ON

:0
* ? test -e “$MAILDIR/.procmailrc”
| /usr/bin/procmail -t -m USER=$USER DOMAIN=$DOMAIN $MAILDIR/.procmailrc

多分ほかのサイトとかなり違う部分に赤字を入れた。通常はこんな構造はしていないと思う。

/var/spool/mail/vhosts/r******.jp/r****/.procmailrc

伏字にする必要はないんだけど、**** のところはドメイン名だったりユーザー名だったりがはいるので固定ファイル名じゃないから伏せてみた。ほんの一部を抜粋したんだけれど、特定のドメイン名からのメールを別アカウントに転送しているというもの。

SHELL=/bin/bash
PATH=$HOME/bin:/usr/bin:/usr/local/bin
MAILDIR=/var/spool/mail/vhosts/$DOMAIN/$USER
LOGFILE=$MAILDIR/procmail.log
LOCKFILE=$MAILDIR/.lockfile
DEFAULT=$MAILDIR/
NULL=/dev/null

:0 c
* 9876543210^0 ^From:.notify@twitter.com.
* 9876543210^0 ^From:.@microsoft.com.
* 9876543210^0 ^From:.@portal.eltax.jp.
* 9876543210^0 ^From:.@sg-m.jp.
* 9876543210^0 ^From:.@qiita.com.
* 9876543210^0 ^From:.@tribeat.com.
* 9876543210^0 ^From:.@bizstation-ml2.bk.mufg.jp.
! r****-.**.****@redalarm.jp

procmailのテスト方法について

以前から、procmailをテストするのに毎回メールを送っていては大変だったり、メーラーによって条件が変わってしまったり、文字エンコードのテストをしたいのに適切なデータを作りにくいなどの問題があり、コマンドライン上でできる方法を使っていた。(参考にしたサイトがあるはずなんだけど、かなり前の話で思い出せない)

# cat a.txt | sudo -u r**** /usr/bin/procmail

通常は上記のような形でa.txtはメールのソースそのまま。root実行、sudoでprocmail 起動、そのユーザーのホームディレクトリにある.procmailrcが起動されるという流れだったんだけど、今回はvirtual環境なのでやり方が変わったところを記載する。

# cat a.txt | sudo -u vmail /usr/bin/procmail -t -m USER=r***** DOMAIN=r******.jp /etc/procmailrc

上記のように、vmailユーザーで起動して、環境変数にバーチャルのユーザーを指定する。procmailrcは一段階目のファイルを指定する。

virtual環境に入れたprocmailで発生したトラブル

このドメインにはユーザーが少ないので、面倒くさがってメーリングリストのソフトウェアを入れていなかった。/etc/aliasesに複数のユーザーを記載することで簡易的に複製するだけとしていた。今回も移行にあたり、同様の設定ができればと考えていた。

virtual環境では、/etc/aliasesでの複製ができないということが検証によってわかっていたので、/etc/postfix/virtualのファイルの中でメールの複製をしようと試みた。で、それはprocmailを導入する前にテストをしていたので、問題ないかに見えていた。

/etc/postfix/virtual に記載したのはこんな感じ。
furitest@redalarm.jp acc1@redalarm.jp acc2@redalarm.jp acc3@redalarm.jp

下記のログを見る限りは正常に届いているように見える。

Jun 8 09:07:33 mail postfix/smtpd[239823]: connect from unknown[240f:73:17ee:1:dd14:6b2d:89cc:c59]
Jun 8 09:07:33 mail postfix/smtpd[239823]: 3E780F969B: client=unknown[240f:73:17ee:1:dd14:6b2d:89cc:c59], sasl_method=PLAIN, sasl_username=rebine@redalarm.jp
Jun 8 09:07:33 mail postfix/cleanup[239826]: 3E780F969B: message-id=20210608090734.7B2D.76D2DE17@redalarm.jp
Jun 8 09:07:33 mail opendkim[728]: 3E780F969B: DKIM-Signature field added (s=s20210501, d=redalarm.jp)
Jun 8 09:07:33 mail postfix/qmgr[213540]: 3E780F969B: from=r12345@redalarm.jp, size=837, nrcpt=3 (queue active)
Jun 8 09:07:33 mail postfix/pipe[239827]: 3E780F969B: to=acc3@redalarm.jp, orig_to=furitest@redalarm.jp, relay=procmail, delay=0.15, delays=0.15/0/0/0, dsn=2.0.0, status=sent (delivered via procmail service)
Jun 8 09:07:33 mail postfix/pipe[239827]: 3E780F969B: to=acc1@redalarm.jp, orig_to=furitest@redalarm.jp, relay=procmail, delay=0.15, delays=0.15/0/0/0, dsn=2.0.0, status=sent (delivered via procmail service)
Jun 8 09:07:33 mail postfix/pipe[239827]: 3E780F969B: to=acc2@redalarm.jp, orig_to=furitest@redalarm.jp, relay=procmail, delay=0.15, delays=0.15/0/0/0, dsn=2.0.0, status=sent (delivered via procmail service)
Jun 8 09:07:33 mail postfix/qmgr[213540]: 3E780F969B: removed
Jun 8 09:07:33 mail postfix/smtpd[239823]: disconnect from unknown[240f:73:17ee:1:dd14:6b2d:89cc:c59] ehlo=2 starttls=1 auth=1 mail=1 rcpt=1 data=1 rset=1 quit=1 commands=9

でも実際に各ユーザーのディレクトリを見てみると、acc3にしかメールは届いていなった。ログで最初に記述されているユーザーしか届かない。procmailは重複メールを削除しないし…さっぱり訳が分からなかったが、原因が判明した。
procmailはロックする機能がある。同時に複製されたメールを処理しようとすると、ロックされてしまって2つ目3つ目のメールは処理されていない様子が出てきた。(procmail をVERBOSEモードにしてログを見た)
理由はprocmailがEOF区切りで動作するからと推測。追記(2021/9/11)にて記載した。

procmail: [240095] Tue Jun 8 09:23:51 2021
procmail: Executing “test,-e,/var/spool/mail/vhosts/r ***** .jp/r ***** /.procmailrc”
procmail: [240095] Tue Jun 8 09:23:51 2021
procmail: Match on “test -e /var/spool/mail/vhosts/r ***** .jp/r*****/.procmailrc”
procmail: Assigning “LASTFOLDER=/usr/bin/procmail -t -m USER=r ***** DOMAIN=r ***** .jp /var/spool/mail/vhosts/r ***** .jp/r ***** /.procmailrc”
Subject: test20210608-04
Folder: /usr/bin/procmail -t -m USER=r ***** DOMAIN=r ***** .jp /var/ 1242
procmail: Unlocking “/var/spool/mail/vhosts/procmail.lock
procmail: Executing “/usr/bin/procmail,-t,-m,USER=rebine,DOMAIN=redalarm.jp,/var/spool/mail/vhosts/r ***** .jp/r ***** /.procmailrc”

図にするとこんな感じに。

複製したメールを処理できない。

これは、一度vmailユーザーのprocmailrc で処理をさせて、そのあとで個々のユーザーのprocmailが起動するという構造が起因となったトラブルだとわかった。
と、断定したのは誤りで、procmailは起動時にEOFまでを処理対象として設計されているし、master.cfで定義される配信先には複数のメールをまとめて標準入力に送るという設計だったからだった。

トラブルの回避方法

メール振り分け専用のユーザーを作って、複製したい場合はそのユーザーに。で、そのユーザーのprocmailrcにそれぞれのユーザーを記載した。
furitest@xxxxx.jp → furiwake@xxxxx.jp
furiwake のprocmailrc には、下記のように記載。

:0 c
* $ ^To:.*furitest@r******.jp
! acc1@redalarm.jp acc2@redalarm.jp acc3@redalarm.jp

ちょっと面倒になった気がするけれど、今から設計を変えるのもなーっていう感じで仕方ない。

試していないトラブルの回避方法

すべてのユーザーにprocmailrcを作ればよいのでは?と思ったけれど。ユーザー作成スクリプトなりに.procmailrc を基本的に作るような記述をするという回避方法もあると思った。
これを試したが結果は変わらなかった。

懸念点

もしこの状態で、mailman とかFMLなどのalias設定との絡みがあるようなメーリングリストソフトウェアを入れた場合、どのように動くことになるのかな?という懸念が生まれてしまう。(最初にprocmailには任せないだろうから大丈夫だとは思うんだけれど…メールは1通ずつメーリングソフトウェアが複製していくと思うので…)

追記(2021/9/11)

procmailのlock機構がこの状態を生んでいると思い込んでいたが、下記の2つの作業を試してみても解決しなかった。
1.ロックファイル名にユーザー名を追加して生成
2.すべてのユーザーに.procmailrcを設定し、/etc/procmailrc を経由せずに各ユーザの.procmailrc 読み込んで起動

https://manpages.debian.org/unstable/manpages-ja/procmail.1.ja.html
こちらに、procmailはメールをEOFまで読み込んで…とある。なので、1つのメールとして認識される区切りがEOFだとすると、2通、3通のメールをまとめてmaster.cfに記載された形の配送先として標準入力に出されれば1通しか処理ができないということも想像がつく。

ロックファイルにユーザー名を追加する設定

[root@mail ~]# cat /etc/procmailrc
SHELL=/bin/bash
PATH=/usr/bin:/usr/local/bin
MAILDIR=/var/spool/mail/vhosts/$DOMAIN/$USER
DEFAULT=$MAILDIR/
LOCKFILE=/var/spool/mail/vhosts/procmail.lock.$USER
LOGFILE=/var/spool/mail/vhosts/procmail.log
VERBOSE=ON
:0
* ? test -e “$MAILDIR/.procmailrc”
| /usr/bin/procmail -t -m USER=$USER DOMAIN=$DOMAIN $MAILDIR/.procmailrc

すべてのユーザーに.procmailrcを設定して個別procmail起動

procmail unix – n n – – pipe flags=R
user=vmail argv=/usr/bin/procmail -t -m USER=${user} DOMAIN=${nexthop}
/var/spool/mail/vhosts/${nexthop}/${user}/.procmailrc