2023/06/16 RockyLinux 9.2





PostfixAdminでクォータ管理をする(SQLite編)


PostfixAdminはユーザー管理、パスワード、メールグループなどいくつかのことがGUIで出来ます。
メールボックスのクォータ管理もその一つです。


1.PostfixAdminのインストール (SQLite編)
2.PostfixAdminで PostfixとDovecotを管理する(SQLite編)
3.PostfixAdminでクォータ管理をする(SQLite編)
4.PostfixAdminで SQLiteを使用した場合のパフォーマンスについて


PostfixAdminはユーザーごとのクォータ情報をデータベースで提供するだけなので、実際にクォータとして制限するのは PostfixとDovecotです。




PostfixAdminからメールクォータを一覧表示できるので、運用できるとけっこう便利です。


ネット上でメールボックスのクォータを検索すると様々なサイトが出てきますが、時代もタイプも様々なので情報としては結構とっちらかっていま す。

ネットの情報をざっくりとまとめると

・メールのクォータは昔はPostfixにVDAパッチというを当てて有効化していた。
 しかし今はVDAパッチの開発は停止しており、クォータ機能は扱えない。
 →PostfixはSMTPサーバなのだから、メールを受信するたびにメールボックスの中身をチェックするのはSMTPサーバの仕事じゃな くね? ということなんじゃないかと想像します。

・現在クォータ管理はDovecotによって行われる。
 DovecotはPOP/IMAPでPOPを取り出そうとするたびに、メールボックスのメール容量を把握しているのだからDovecotに やらせるのがいい、ということだと思います。

・Dovecotのクォータ方式には複数あり。どれを選ぶかで設定パラメータが異なる。
 しかし現在、Dovecotはcount形式を推奨。ならcount形式でやります。

https://doc.dovecot.org/configuration_manual/quota_plugin/#quotaより
count 形式
現 在Dovecotが推奨
もともとはメールサイズではなく、メール数でカウントして制限する方式。
現在ではメールの合計サイズでの指定も可能。
メールサイズは各インデックスファイルに埋め込まれている。
dict形式
ディクショナリ形式。データベースもしくはテキストファイ ルにユーザー別クォータを定義して管理する。
Dovecot Ver2.2.19でcountが登場するまでは一般的だったので、ネット上には情報がたくさんありますい。DBを用意することが多い。
maildri形式
ごく普通、と表現されている。
Maildir形式のメールボックスでしか動作しない。
maildirsizeファイルに基づいてquotaを実施。
本体のサイズとゴミ箱のサイズを別カウントできるできる利点もありますが、使用しているメールボックスサイズをユーザーが詐 称することもできなくないとか。
dirsize形式
メール受信のたびにフォルダ内のファイルサイズを全て確認 する。
簡単だがディスク/CPU負荷が高いので避けるべき、とマニュアルにあります。
fs形式
ファイルシステムクォータ。
OSのクォータ機能で制御。た だしファイルシステムへクォータを設定する必要があり、個別管理も煩雑になりやすい。

・Dovecotがメール取り出しのたびにメールボックスサイズを把握できるのはわかったが、じゃあメールボックスがいっぱいになったとき、 新たなメール受け入れを断るのはどうやるのか? DovecotはSMTPで着信するメールに関与できないよね? という疑問がわきま す。
 →Dovecotが現在のメールボックスサイズを教えるためTCPポートを用意し、Postfixがそのポートへ問い合わせることで、メー ルボックスサイズを把握します。
 メールボックスがクォータ限界ならPosttfixは新着メールを断る、という動作になります。
 Postfixは容量確認するだけ、Dovecotは現在のメールボックスサイズを教えるだけ、という分担になります。


1.Dovecotでクォータを有効にする
2.Postfixからクォータの連携をする
3.PostfixAdminからユーザーにクォータを設定する
4.PostfixAdminのクォータ値を参照する
5.動作試験
6.デフォルトのクォータサイズを設定する
7.SQLiteのコマンド




1.Dovecotでクォータを有効にする

DovecotにCount方式のクォータを有効に設定します。
Count形式を用いる場合、設定は意外と多くありません。


10-mail.confを以下のように設定
/etc/dovecot/conf.d/10-mail.conf の編集

mail_plugins = $mail_plugins quota
mailbox_list_index = yes
mail_pluginsはどこで設定してもいいようですが、一応10-mail.confへ追加しました。
またDovecotの本家に「最高のパフォーマンスを得るためにはインデックスを有効にしましょう」とあるので、 mailbox_list_indexを設定しています。


90-quota.confへ以下の全てを追加します。 クォータ設定の大本です。
/etc/dovecot/conf.d/90-quota.conf の編集

以下セクションを全て追加

#容量限界のときに返すメッセージとエラーコード
plugin {
 quota_status_success = DUNNO
 quota_status_nouser = DUNNO
 quota_status_overquota = "552 5.2.2 Mailbox is full"
}

#Postfixからの問い合わせに答えるためのローカルポート番号
service quota-status {
  executable = quota-status -p postfix
  inet_listener {
    port = 12340
  }
  client_limit = 1
}

#クォータのサイズ設定
plugin {
  quota_vsizes = yes
  quota = count:User quota   #Countモードで設定
  #quota_max_mail_size = 5M  #1通辺りの最大サイズ。必要なら

  quota_rule = *:bytes=5M

  #全ユーザークォータを5MBで設定(試験用)
  #ただしこのルールはPostfixAdminのSQL値で上書きされるので有効ではありません
}

serviceセクションは、PostfixがDovecotへクォータサイズを問い合わせる際に使用するローカルポートです。 TCP12340番が使用済みなら、他のポートを指定してください。

クォーターモードの設定でポイントとなるのは、quota_ruleの項目です。
「全てのクォータ方式(*)で」「計算基準はバイト単位で5MBサイズ」というクォータを設定していますが、これは仮の設定です。
このquota_ruleは、後のSQLクエリにより上書きします。つ まり消えます。
なぜ記載しているかというと、クォータが正常に動作しているかの試験用です。
この項目はSQLクエリが正しく動作していれば、残しても消しても影響ありません。
(データベースから正しくクォータサイズを取得できていない場合、こちらのクォータが有効になってしまっている可能性はあります)


Dovecotを再起動して、設定を有効化します。
#systemctl restart dovecot
#netstat -ant | grep 12340
tcp        0      0 0.0.0.0:12340           0.0.0.0:*               LISTEN
tcp6       0      0 :::12340                :::*                    LISTEN
12340ポートが開いていることが確認できます。
Dovecotではクォータが有効になりました。
といってもこの時点でもまだ、クォータは機能していません。




2.Postfixからクォータの連携をする


新しいメールがメールサーバに着信したとき、「メールボックスがもう一杯です」、といって拒否するのはPostfixの役目です。
PostfixがDovecotへ「メールが到着したけどクォータはまだ大丈夫?」と質問する設定を追加します。

/etc/postfix/main.cf の編集

smtpd_recipient_restrictions =  check_policy_service inet:localhost:12340

Postfixを再起動します。
#systemctl restart postfix

この時点では、Dovecotの90-quota.confで指定した5MBクォータが有効になっています。
ここでメールの送受信をクォータを試験しておくといいでしょう。

テストメールへ繰り返し大きな添付ファイルを送信してみます。

メールボックスの5MBクォータを超えるように添付ファイルを送ったら、正しく拒否されました。

ポイントは、添付ファイルもメールとして着信するとエンコードの都合上20〜30%くらい大きくなります。
4MBぐらいのファイルを送付すると、ファイルによっては1通でサイズ超過します。
あるいは1MBぐらいずつ送ると、着信時に10%くらい超過しても格納されることもあります。
クォータの試験をするときは、多少のサイズ誤差は考慮しておきましょう。


実際のユーザーごとのクォータ有効値、現在の容量はコマンドによって確認できます。
空のメールボックスに1MBの添付ファイルを入れた場合

# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE  1405  5120                        27
User quota MESSAGE     2     -                         0
※1Mというより、1.4MBぐらいになっています。


空のメールボックスに3MBの添付ファイルを入れた場合

# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE  4213  5120                        82
User quota MESSAGE     2     -                         0
※4MBくらいになっています。5MB中3MBのつもりでしたが80%を超えています 
クォータの動作試験が確認できたら、設定を続けます。




3.PostfixAdminからユーザーにクォータを設定する


クォータのテストをするため、PostfixAdminの適当なユーザーにクォータを設定します。



各ユーザーのクォータ項目は始めから表示されています。
ですが「必要な人だけ追加してね」、ということなのでしょうかユーザーの一覧表にはクォータ値は表示されていません。
これでは不便なので、config.local.phpへクォータの設定を追加します。
/srv/postfixadmin/config.local.php の編集

$CONF['quota'] = 'YES';

画面をリロードすると、ユーザー一覧にクォータ表示が増えています。


クォータをYESに設定すると、ドメインクォータの容量制限の項目が有効化されてしまいます。
ドメイン一覧から、ドメインのクォータを編集しておきます。



容量制限とドメインクォータに0を設定しておきます。


容量制限は、各ユーザーのクォータに設定できる最大値で す。例えば500を設定すると、各ユーザーのクォータは500までしか設定できません。(デフォルト値の0も設定できません)
ドメインクォータは全ユーザーメールボックスの総使用量 を制限したいときに使用する値です。サービスプロバイダ向きの機能と言えます。(今回の設定ではSQLクエリに含めない、つまりクォータ制限 値として採用しないので影響しません)




4.PostfixAdminのクォータ値を参照する


PostfixAdminのクォータ情報をDovecotから参照するためのSQLクエリ用意します。


作業前に、現在のクォータサイズを確認してみます。
90-quota.confで設定した5MBのはずです。
# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE     1  5120                         0
User quota MESSAGE     1     -                         0

DovecotがユーザーごとのクォータサイズをPostfixAdminデータベースから取り出すためのSQLクエリを作成します。
といっても、すでにあるPostfixAdminと連携するためのSQLクエリに、1行追加するだけです。
/etc/dovecot/dovecot-sql.conf.ext の編集

driver = sqlite
connect = /srv/postfixadmin/database/postfixadmin.db

password_query = \
    SELECT \
        username as user, \
        password, \
        '/var/vmail/%d/%n' as userdb_home, \
        'maldir:/var/vmail/%d/%n' as userdb_mail \
    FROM \
        mailbox \
    WHERE username = '%u' AND active = '1'

user_query = \
    SELECT \
        '/var/vmail/%d/%n' as home, \
        'maildir:/var/vmail/%d/%n' as mail ,カンマの追加
        '*:bytes='||quota as quota_rule \      追加部分
    FROM \
        mailbox \
    WHERE username = '%u' AND active = '1'

データベースから取り出したuser01の中のquota値を、文字列結合して、quota_ruleという名前で扱います。
「||(パイプパイプ)」は、文字列の結合を意味します。(CONCATと同じ)
クエリのasが示す通り、ヘッダ名がプロパティ名として扱われます。(90-quota.confのquota_ruleの行として見なす、 という意味です)

前述で90-quota.confのプロパティを上書きする、とはこの部分の動作です。

このクエリファイルには注意点があります。
・'*.bytes='||quota の一連の文字にスペースを入れると、クエリでエラーが発生します。
・PostfixAdminとクォータを連携させる場合、quota_ruleには「bytes」を使用する必要があります。 「*:storage」を使用すると正常に容量計算されません。


SQLクエリファイルを編集したので、サービスを再起動します。
#systemctl restart dovecot

再度、doveadmコマンドでクォータを確認します。
# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE  1405  30000                        4
User quota MESSAGE     2     -                         0
連携前は容量5MBだったのが、PostfixAdminで設定した30MBになっているのが確認できます。


PostfixAdminからユーザーのクォータを変更してみます。


# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE  1405  15000                        9
User quota MESSAGE     2     -                         0
Dovecotが認識するクォータサイズが変更されています。
PostfixAdminとのクォータサイズの連動は成功です。




5.動作試験


実際にメールを送信して、クォータを試験します。
3MBの添付ファイルを送信していき、クォータの変化を確認します。
# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE     0 15000                         0
User quota MESSAGE     0     -                         0

3MBファイルメール 1通目
# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE  4211 15000                        28
User quota MESSAGE     1     -                         0

2通目
# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE  8422 15000                        56
User quota MESSAGE     2     -                         0

3通目
# doveadm quota get -u user01@example.com
Quota name Type    Value Limit                         %
User quota STORAGE 12633 15000                        84
User quota MESSAGE     3     -                         0

4通目でクォータエラーが出ました。想定通り、15MBのクォータが有効になっていると考えてよさそうです。




以上で、PostfixAdminによるクォータ管理が有効になりました。


以降はクォータサイズのデフォルト管理のテクニックと、デバッグ方法です。





6.デフォルトのクォータサイズを設定する


実際にユーザーのクォータサイズを運用するときは、「デフォルトのクォータサイズ」について検討したくなります。
例えば、ほとんどのユーザーには100MBのクォータが割り当てられ、一部ユーザーのみ1GBのクォータを割り当てる、という運用は多いで しょう。

PostfixAdminがメールアドレスを作る時のデフォルトクォータサイズは0MB(無制限)なので、メールアドレスを作成する時は、忘 れることなく毎回100MBを入力する必要があります。
デフォルトクォータの設定があれば、ユーザー作成の手間も減らせます。後から全ユーザーのクォータを変更したい、などの要望もあります。


いろいろ研究した結果、この問題には2つのアプローチが考えられます。

A.ユーザー作成時に、始めからデフォルトサイズを入 力済みにしておく
B.クエリでクォータサイズが0MB(無制限)のとき に、100MBのクォータを割り当てる



A.ユーザー作成時に、始めからクォータを入力済みに設定しておく

ユーザー作成時にクォータサイズが始めから設定されていれば、クォータの設定忘れもありません。
この設定は、始めから機能が用意されているので一番簡潔な対応です。


ユーザー作成ページで、始めからクォータサイズが入力されています。

設定は簡単です。
/srv/postfixadmin/model/MailboxHander.phpファイルの中から、41行目あたりにクォータのデフォ ルト値を入力する行があります。
/srv/postfixadmin/model/MailboxHandler.php の 編集

(略)

'quota' => pacol(1, 1, 1, 'int' , 'pEdit_mailbox_quota', 'pEdit_mailbox_quota_text', '100' ),

空白を一部削除しています

(略)


該当箇所の構文は長くて探しにくいので「pEdit_mailbox_quota」というユニークなキーで検索すれば見つかるでしょう。

この方法では、各ユーザーに100MBのクォータサイズを設定していることには変わりないので、後で全員のクォータサイズを300MBへ一括 変更したいときはどうすればいい? となります。
その場合はPostfixAdmin附属のユーザープロパティ変更コマンドでクォータサイズを変更します。
# cd /srv/postfixadmin/scripts
# postfixadmin-cli mailbox update user01@example.com --quota 300
GUIからユーザー情報をCSVでエクスポートします。その中からデフォルトクォータサイズのユーザーだけを抽出すればコマンドを作成するの は簡単です。
意外にも、CSVをそのままインポートする機能はないようです。



B.クエリでクォータサイズが0MB(無制限)のときに、100MBのクォータを割り当てる

こちらは少しテクニカルな手法です。
DovecotのSQLクエリでクォータサイスを取得するとき、0MBだったら100MBへ置換することで対応します。
0MB以外、例えば200MBのクォータサイズだったらそのまま200MBとして扱います。
/etc/dovecot/dovecot-sql.conf.ext の編集

driver = sqlite
connect = /srv/postfixadmin/database/postfixadmin.db

password_query = \
    SELECT \
        username as user, \
        password, \
        '/var/vmail/%d/%n' as userdb_home, \
        'maldir:/var/vmail/%d/%n' as userdb_mail \
    FROM \
        mailbox \
    WHERE username = '%u' AND active = '1'

user_query = \
    SELECT \
        '/var/vmail/%d/%n' as home, \
        'maildir:/var/vmail/%d/%n' as mail ,\

        case quota \
          when '0' then '*:bytes=100M' \
          else '*:bytes='||quota \
        end as quota_rule\

    FROM \
        mailbox \
    WHERE username = '%u' AND active = '1'
この運用の利点は、後で「全員のクォータを300MBへ変更したい」という時でも、SQLクエリを300MBへ変更するだけで対応できます。
デメリットとしては、トリッキーなので他の運用者が見た時「どこでクォータを設定しているかわからん」という混乱を招きかねないことです。

PostfixAdminのクォータ一覧で、0が設定されていればそのクォータは100MBと覚えておけば良いわけです。
では実際に0MB(無制限)を指定したいときはどうすればよいか?
そこは100GBでも1TBでも十分に大きい値を設定して対応します。





7.SQLiteのコマンド


不具合が起きたり、クォータが情報なうまく適用されない場合、PostfixAdmin.dbの中身を直接確認したくなります。
SQLiteのコマンドが分かれば、動作確認がスムーズに出来ます。
# cd /srv/postfixadmin/database/
#sqlite3 postfixadmin.db

テーブル一覧を表示。この中からFrom対象を選びます。
sqlite> .table
admin                  domain_admins          quota2
alias                  fetchmail              vacation
alias_domain           log                    vacation_notification
config                 mailbox
domain                 quota

mailboxリストテーブルの中身を全て表示
sqlite> select * from mailbox;
user01@example.com|$1$c9809462$0pB4CJFP8LCr3Bf3IbVGb/||example.com/user01/|
15360000|user01|example.com|2023-06-07 06:39:00|2023-06-10 08:37:11|1||||2023-06-07 06:39:00|2023-06-07 06:39
(略)


user01@example.comの行だけを表示
sqlite> select * from mailbox where username = 'user01@example.com';
user01@example.com|$1$c9809462$0pB4CJFP8LCr3Bf3IbVGb/||example.com/user01/|
15360000|user01|example.com|2023-06-07 06:39:00|2023-06-10 08:37:11|1||||2023-06-07 06:39:00|2023-06-07 06:39


表示を表形式へ変更
sqlite> .mode column

ヘッダ情報を表示
sqlite> .header on

ユーザー名、メールボックスパス、有効無効、クォータのみを列挙
sqlite> select username,maildir,active,quota from mailbox;
username            maildir              active  quota
------------------  -------------------  ------  --------
user01@example.com  example.com/user01/  1       15360000
user02@example.com  example.com/user02/  1       5360000
(略)


有効なユーザーの内、クォータサイズが15MBのユーザーだけ絞り込み
sqlite> select username from mailbox WHERE quota='15360000'and active ='1';
username
------------------
user01@example.com


user01のクォータサイズを変更
sqlite> update mailbox set quota = '5120000' WHERE username = 'user01@example.com';


終了
sqlite> .exit







prev.gif