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

※2023/4/12追記。「ギフティブリサーチ合同会社」様 情報ありがとうございました。

# postconf -e procmail_destination_recipient_limit=1
# postconf -n | grep procmail_destination_recipient_limit
procmail_destination_recipient_limit = 1

変更は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を導入する前にテストをしていたので、問題ないかに見えていた。(2023/4/12 解決策追記)

/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で定義される配信先には複数のメールをまとめて標準入力に送るという設計だったからだった。

トラブルの回避方法

2023/4/12追記 /etc/postfix/main.cfに記載することでトラブル解決

この現象については下記の設定をmain.cfに記載することで回避できるという情報を頂きました。

参考:パソコンおやじの掲示板 https://www.aconus.com/~oyaji/bbs/wforum.cgi?mode=allread&no=8487&page=0

情報を寄せていただいた「ギフティブリサーチ合同会社」様ありがとうございました。

[root@mail ~]# postconf -e procmail_destination_recipient_limit=1

※maillogには変化がないので省略。

procmail_destination_recipient_limitについて

気になって「procmail_destination_recipient_limit」についての公式文書があるかを調べてみたものの公式ウェブサイトを検索しても出てこない。仕方ないのでman を確認するとreadmeがあるという。

[root@mail ~]# postconf readme_directory
readme_directory = /usr/share/doc/postfix/README_FILES

grepで検索しても出てこない。

[root@mail ~]# fgrep -r ‘procmail_destination_recipient_limit’ /usr/share/doc/postfix/README_FILES
[root@mail ~]# fgrep -r ‘procmail_destination_recipient_limit’ /usr/share/doc/postfix/README_FILES*
[root@mail ~]# fgrep -r ‘procmail_destination_recipient_limit’ /usr/share/doc/postfix/*
[root@mail ~]# fgrep -r ‘procmail_destination_recipient_limit’ /usr/share/*
[root@mail ~]# fgrep -r ‘procmail_destination_recipient_limit’ /usr/*
[root@mail ~]# fgrep -i -r ‘procmail_destination_recipient_limit’ /var/*
[root@mail ~]# fgrep -r ‘procmail_destination_recipient_limit’ /etc/*
/etc/postfix/main.cf:procmail_destination_recipient_limit = 1

ここまで何も出てこないという設定は珍しいと思った。どの参考サイトも利用したという事実のみで公式文書にあたることができない。

どうやら、xxxxx_destination_recipient_limit という形で記載されていて、xxxxxにあたる部分はmaster.cfの配送先を当てはめることができるようだった。※日本語のサイトでは扱っているバージョンが古く情報量が不足している気がする。

default_destination_recipient_limit (default: 50)

The default maximal number of recipients per message delivery. This is the default limit for delivery via the lmtp(8)pipe(8)smtp(8) and virtual(8) delivery agents.

Setting this parameter to a value of 1 affects email deliveries as follows:

  • It changes the meaning of the corresponding per-destination concurrency limit, from concurrency of deliveries to the same domain into concurrency of deliveries to the same recipient. Different recipients are delivered in parallel, subject to the process limits specified in master.cf.
  • It changes the meaning of the corresponding per-destination rate delay, from the delay between deliveries to the same domain into the delay between deliveries to the same recipient. Again, different recipients are delivered in parallel, subject to the process limits specified in master.cf.
  • It changes the meaning of other corresponding per-destination settings in a similar manner, from settings for delivery to the same domain into settings for delivery to the same recipient.

Use transport_destination_recipient_limit to specify a transport-specific override, where transport is the master.cf name of the message delivery transport.

https://www.postfix.org/postconf.5.html

default_destination_recipient_limit (デフォルト: 50)

メッセージ配送ごとの受信者数の、デフォルトの最大数。これは lmtp(8) や pipe(8)、smtp(8)、virtual(8) 配送エージェントを使った配送のデフォルトの 制限です。

このパラメータの値を1に設定すると、対応する配送先ごとの並列制限は ドメインごとの並列度から受信者ごとの並列度へと意味を変えます。

http://www.postfix-jp.info/trans-2.1/jhtml/postconf.5.html

下記は根本解決をせず、procmailで振り分けで回避しようとした例(非推奨)

メール振り分け専用のユーザーを作って、複製したい場合はそのユーザーに。で、そのユーザーの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