Dovecotには、「クォータ」と呼ばれる、ユーザ毎に「容量制限」を設けることができます。ここでは、Postfix Adminから設定した「容量制限」をDovecotに反映させる設定を行いたいと思います。クォータの管理はDovecotが行い、そのバックエンドとしては最新の(そしてDovecotのマニュアルでは推奨している)「count」を用います。
FreeBSD 14.1で動作確認していますが、設定ファイルのあるディレクトリを読み替えれば、Linuxでも同様に設定できると思います。
Postfix Adminのクォータ設定
Postfix Adminで、クォータ設定を表示させるには「/usr/local/www/postfixadmin/config.local.php」に次の設定を追記します。
$CONF['quota'] = 'YES';
$CONF['domain_quota'] = 'NO';
$CONF['quota_multiplier'] = '1048576';
クォータを有効にします。が、今回はドメインクォータは有効化しません。ドメインクォータを有効にする場合は、「$CONF['domain_quota'] = 'NO';」を削除してください。クォータはメガバイト単位で設定しますが、デフォルトでは「1024000バイト」がメガバイトの単位になっています。これを「1024 × 1024 = 1048576バイト」にしています(これは好みの問題なのですが、私の愛用のメーラーであるThunderbirdは、この設定を行うとキリのいい表示をしてくれます)。この設定を行うと、ユーザの設定画面でクォータを設定することができます。
Dovecotのクォータ設定
Postfixからユーザのメールボックスへの配送は、DovecotのLMTPに任せているので、クォータ設定はDovecotに集約されます。
/usr/local/etc/dovecot/dovecot-sql.con
Postfix Adminで設定されたクォータ情報をSQLで検索できるように、変更を行います。次のパッチを当ててください。
*** dovecot-sql.conf.ext.orig Mon Nov 4 21:03:16 2024
--- dovecot-sql.conf.ext Mon Nov 4 21:03:22 2024
***************
*** 129,135 ****
#
user_query = \
SELECT '/var/spool/vmail/' || maildir AS home, \
! 'vmail' AS uid, 'vmail' AS gid \
FROM mailbox WHERE username = '%u' AND active = true
# If you wish to avoid two SQL lookups (passdb + userdb), you can use
--- 129,136 ----
#
user_query = \
SELECT '/var/spool/vmail/' || maildir AS home, \
! 'vmail' AS uid, 'vmail' AS gid, \
! '*:bytes=' || quota AS quota_rule \
FROM mailbox WHERE username = '%u' AND active = true
# If you wish to avoid two SQL lookups (passdb + userdb), you can use
これで、DovecotはPostfix Adminで設定されたクォータ情報を読み取れるようになりました。
/usr/local/etc/dovecot/conf.d/90-quota.conf
クォータ設定の肝となるファイル、「90-quota.conf」です。これに、次のパッチファイルを当ててください。
*** ../example-config/conf.d/90-quota.conf Sat Oct 12 17:44:22 2024
--- 90-quota.conf Tue Nov 5 00:23:09 2024
***************
*** 39,59 ****
# Note that % needs to be escaped as %%, otherwise "% " expands to empty.
plugin {
! #quota_warning = storage=95%% quota-warning 95 %u
! #quota_warning2 = storage=80%% quota-warning 80 %u
}
# Example quota-warning service. The unix listener's permissions should be
# set in a way that mail processes can connect to it. Below example assumes
# that mail processes run as vmail user. If you use mode=0666, all system users
# can generate quota warnings to anyone.
! #service quota-warning {
! # executable = script /usr/local/bin/quota-warning.sh
! # user = dovecot
! # unix_listener quota-warning {
! # user = vmail
! # }
! #}
##
## Quota backends
--- 39,61 ----
# Note that % needs to be escaped as %%, otherwise "% " expands to empty.
plugin {
! quota_warning = storage=95%% quota-warning 95 %u %d
! quota_warning2 = storage=80%% quota-warning 80 %u %d
! quota_warning3 = -storage=50%% quota-warning below %u %d
}
# Example quota-warning service. The unix listener's permissions should be
# set in a way that mail processes can connect to it. Below example assumes
# that mail processes run as vmail user. If you use mode=0666, all system users
# can generate quota warnings to anyone.
! service quota-warning {
! executable = script /usr/local/bin/quota-warning.sh
! user = vmail
! unix_listener quota-warning {
! mode = 0600
! user = vmail
! }
! }
##
## Quota backends
***************
*** 80,83 ****
--- 82,108 ----
#quota2 = dict:domain:%d:proxy::quota_domain
#quota_rule = *:storage=102400
#quota2_rule = *:storage=1048576
+ }
+
+ # Use count as quota backend
+ plugin {
+ quota_vsizes = yes
+ quota = count:User quota
+ quota_rule2 = Trash:storage=+100M
+ quota_rule3 = Trash.*:storage=+100M
+ quota_rule4 = Junk:ignore
+ quota_status_success = DUNNO
+ quota_status_nouser = DUNNO
+ quota_status_overquota = "552 5.2.2 Mailbox is full"
+ }
+
+ ##
+ ## Quota status service for postfix
+ ##
+ service quota-status {
+ executable = quota-status -p postfix
+ inet_listener {
+ port = 12340
+ }
+ client_limit = 1
}
変更点の説明をします。
plugin {quota_warning ...}では
- 95%、80%を超えたユーザに警告メールを送信、また、使用容量が50%を切ったユーザには「通常状態に戻った」旨を通知
- マルチドメインを念頭に、ドメイン情報を追加するように「%d」を追記
の設定をしています。「service quota_warning {...}」が実際にメールを出す設定になります。今回は「/usr/local/bin/quota-warning.sh」を呼び出す設定にしています。
「# Use count as quota backend」以下に追記しているのが実際のクォータの設定です。今回は
- クォータの計算方法(バックエンド)にDovecotが推奨する「count」を採用(そのために必要な「quota_vsizes = yes」も追記)
- ゴミ箱(とそのサブフォルダ)にはクォータの計算時に100MBを追加して、メールの削除時にクォータ制限に引っかかってメールが消せない事態を起こさないようにする
- 迷惑メールが届くJunkフォルダはクォータの計算から除く。ただし、このフォルダが「無制限に使えるフォルダ」として使われないように、サブフォルダは除かない
という設定を行っています。クォータの設定は、
- 最初にデフォルトの設定として「quota_rule」を適応する
- 次に、その例外として「quota_rule2」「quota_rule3」…を適用する
となるのですが、デフォルトの「quota_rule」は「dovecot-sql.conf.ext」で設定しているようにPostfix Adminから読み込んでいるので、このファイルでは設定していません。もし設定しても、SQLから引っ張ってきた「quota_rule」のほうが優先します。
「service quota-status {...}」では、Postfixがメール受信時に「この大きさのメールを受信しようとしているが、クォータの制限は大丈夫かどうか?」を問い合わせるための設定をしています。UNIXソケットではなくTCP/12340を開くので、このポートに対してルータの設定を適切に行う必要があります(外部にセカンダリMXマシンが無いなら、このポートをルータで開く必要はありません)。
/usr/local/bin/quota-warning.sh
クォータの制限に引っかかりそうなユーザに「警告メール」を出すスクリプトを設置します。次の内容のスクリプトを「/usr/local/bin/quota-warning.sh」として作成してください。
#!/bin/sh
PERCENT=$1
USER=$2
DOMAIN=$3
MSG=""
if [ $PERCENT = "below" ]; then
MSG="Your mailbox is now back to normal."
else
MSG="Your mailbox on the server is now more then ${PERCENT}% full."
fi
cat << EOF | /usr/local/libexec/dovecot/dovecot-lda -f postmaster@$DOMAIN -d $USER -o "plugin/quota=count:User quota:noenforcing"
From: Postmaster <postmaster@$DOMAIN>
To: $USER
Subject: Quota Warning
$MSG
Regards,
Postmaster
EOF
いろいろなサイトで紹介されているスクリプトを参考にしていますが、マルチドメインであるということで引数として「ドメイン名」を受け取り、そのドメイン名の「postmaster」から送信しています。「/usr/local/libexec/dovecot/dovecot-lda」を用いてメールを送信していますが、このコマンドに「-o "plugin/quota=count:User quota:noenforcing"」を指定し、クォータがいっぱいであっても強制的にメールをメールボックスに入れる、という設定を行います。この設定が無いと、メールボックス満杯の場合、このメールがクォータ制限で受け入れられなくなり、キューに溜まってしまいます。
/usr/local/etc/dovecot/conf.d/10-mail.conf
次のパッチを当てて、クォータを有効にしてください。
*** 10-mail.conf.orig Mon Nov 4 20:56:57 2024
--- 10-mail.conf Mon Nov 4 21:11:40 2024
***************
*** 215,221 ****
# Space separated list of plugins to load for all services. Plugins specific to
# IMAP, LDA, etc. are added to this list in their own .conf files.
! #mail_plugins =
##
## Mailbox handling optimizations
--- 215,221 ----
# Space separated list of plugins to load for all services. Plugins specific to
# IMAP, LDA, etc. are added to this list in their own .conf files.
! mail_plugins = quota
##
## Mailbox handling optimizations
/usr/local/etc/dovecot/conf.d/20-imap.conf
「20-imap.conf」ファイルへのパッチです。このパッチを当てると、サポートしているIMAPクライアントでは、クォータの使用状況を見ることができます。
*** ../example-config/conf.d/20-imap.conf Sat Oct 12 17:44:22 2024
--- 20-imap.conf Mon Nov 4 21:14:24 2024
***************
*** 91,97 ****
protocol imap {
# Space separated list of plugins to load (default is global mail_plugins).
! mail_plugins = $mail_plugins imap_zlib
# Maximum number of IMAP connections allowed for a user from each IP address.
# NOTE: The username is compared case-sensitively.
--- 91,97 ----
protocol imap {
# Space separated list of plugins to load (default is global mail_plugins).
! mail_plugins = $mail_plugins imap_zlib imap_quota
# Maximum number of IMAP connections allowed for a user from each IP address.
# NOTE: The username is compared case-sensitively.
/usr/local/etc/dovecot/conf.d/20-lmtp.conf
「20-lmtp.conf」へのパッチです。メールを受信した際に、クォータをチェックする設定を行います。クォータの容量制限に引っかかる場合は、受信拒否します。
*** 20-lmtp.conf.orig Sat Oct 12 17:44:22 2024
--- 20-lmtp.conf Mon Nov 4 21:15:02 2024
***************
*** 11,17 ****
#lmtp_save_to_detail_mailbox = no
# Verify quota before replying to RCPT TO. This adds a small overhead.
! #lmtp_rcpt_check_quota = no
# Add "Received:" header to mails delivered.
#lmtp_add_received_header = yes
--- 11,17 ----
#lmtp_save_to_detail_mailbox = no
# Verify quota before replying to RCPT TO. This adds a small overhead.
! lmtp_rcpt_check_quota = yes
# Add "Received:" header to mails delivered.
#lmtp_add_received_header = yes
/usr/local/etc/dovecot/conf.d/10-master.conf
クォータの警告メールを送信する際に、ワーニングが発生しないようにします。次のパッチを当ててください。
*** 10-master.conf.orig Mon Nov 4 23:15:43 2024
--- 10-master.conf Mon Nov 4 23:16:26 2024
***************
*** 135,137 ****
--- 135,148 ----
#group =
}
}
+
+ service stats {
+ unix_listener stats-reader {
+ mode = 0600
+ user = vmail
+ }
+ unix_listener stats-writer {
+ mode = 0600
+ user = vmail
+ }
+ }
これで必要なファイルは揃いました。次のコマンドでDovecotに設定ファイルを再読み込みさせましょう。
service dovecot reload
次に、バックエンドに「count」を用いているのですが、これはDovecotが管理しているインデックスファイルにメールの大きさを書き込むもので、最初は書き込まれていないのでIMAPクライアントが接続した際にDovecotがメールをスキャンして書き込むのですが、メール数が多い場合にはスキャンに時間がかかり、IMAPクライアントとの接続に時間がかかってしまいます。そのため、あらかじめスキャンして書き込んでしまいます。次のコマンドを実行します。
doveadm mailbox status -A vsize '*'
次に、Postfix Adminでクォータを設定します(詳しい手順は割愛します)。設定したら、次のコマンドを実行してみます。
doveadm quota get -A
このコマンドで、次のような表示を得られるはずです。
Username Quota name Type Value Limit %
test@example.com User quota STORAGE 31264 35840 87
test@example.com User quota MESSAGE 61 - 0
すべてのユーザのクォータを得られるはずです。これでDovecot側の設定は完了です。
Postfixの設定
Postfixでは、「/usr/local/etc/postfix/main.cf」に次のパッチを当てます。
*** main.cf.orig Wed Nov 6 07:00:40 2024
--- main.cf Wed Nov 6 07:01:43 2024
***************
*** 718,723 ****
--- 718,725 ----
permit_mynetworks,
reject_invalid_hostname,
permit
+ smtpd_recipient_restrictions =
+ check_policy_service inet:localhost:12340
smtpd_sender_restrictions =
hash:/usr/local/etc/postfix/reject_sender,
reject_unknown_sender_domain,
これで、メールを受信する際に、Dovecotに対して「クォータの容量は大丈夫かどうか?」を問い合わせるようになります。ただし、ローカル送信の場合は一度送信されたうえでMAILER-DAEMONから「クォータがいっぱいでした」と返事が来ます(外部からメールを受信する際には、メール受信の前にクォータチェックを行い、クォータがいっぱいの場合はメールを拒否します)。次のコマンドを実行して、設定を読み込ませましょう。
service postfix reload
バックアップMXサーバを設ける場合
いろいろな考え方があると思いますが、PostfixでバックアップMXサーバを設ける場合、Dovecotが動いているマシンで「TCP/12340」を開放の上、バックアップMXサーバで動いているPostfixに
smtpd_recipient_restrictions =
...
check_policy_service {
inet:mailserver.example.com:12340,
default_action=DUNNO
}
...
の設定を行えば、メールを受信する前にクォータの状態を確認し、クォータの容量制限に引っかかるような大きなメールの場合は受信拒否できます。ただし、Dovecotが動いていないと問い合わせが失敗するので、「check_policy_service」のオプションで「default_action=DUNNO」を指定し、とりあえずは受け入れる設定をしています。DovecotがプライマリMXサー上にある今回の設定例では、Dovecotが動いていない間メールの受け入れを拒否すれば、プライマリMXサーバが動いていない場合に代行して受信するというバックアップMXサーバの意味がなくなってしまいます。
バックアップMXサーバは、「プライマリのMXサーバが停止している間」のみならず、プライマリMXサーバが動いている間も少ないながらもメールを受信します。その時のための設定となります。
長期間プライマリMXサーバが停止している間は、プライマリMXサーバのクォータを超えていてもバックアップMXサーバが受信し続けるので、「この設定に意味はあるのか?」と自問自答しつつこの設定を施しています。
本質的な解決方法は送信サーバと受信サーバを分ける等の方法ですが、セキュリティ的に「クォータの情報を外に出して大丈夫なのか?」というのも、問題になってくると思います。メールを送ってみれば、送信者にはクォータがいっぱいであるかどうかがわかるのですが…。