dovecot 2.2.19以降で登場した各ユーザのメールフォルダ内にあるindexファイルを使ったquotaを設定しようとした際に発見した出来事です。
doveadm quota get -Aの動作
doveadm quotaのマニュアルを見ると「doveadm quota get -A」を実行すると全ユーザの結果が表示されそうな気がするので実行してみたがされない
[root@mail dovecot]# doveadm quota get -A
Username Quota name Type Value Limit %
[root@mail dovecot]#
dovecotにdebug系ログ出力を有効にした状態での /var/log/maillog には下記のログ
May 2 11:18:59 mail dovecot[959]: auth: Debug: Loading modules from directory: /usr/lib64/dovecot/auth
May 2 11:18:59 mail dovecot[959]: auth: Debug: Module loaded: /usr/lib64/dovecot/auth/lib20_auth_var_expand_crypt.so
May 2 11:18:59 mail dovecot[959]: auth: Debug: Module loaded: /usr/lib64/dovecot/auth/libdriver_sqlite.so
May 2 11:18:59 mail dovecot[959]: auth: Debug: Loading modules from directory: /usr/lib64/dovecot/auth
May 2 11:18:59 mail dovecot[959]: auth: Debug: Module loaded: /usr/lib64/dovecot/auth/libauthdb_ldap.so
May 2 11:18:59 mail dovecot[959]: auth: Debug: Read auth token secret from /run/dovecot/auth-token-secret.dat
May 2 11:18:59 mail dovecot[959]: auth: Debug: ldap(/etc/dovecot/dovecot-ldap.conf.ext): LDAP initialization took 22 msecs
May 2 11:18:59 mail dovecot[959]: auth: Debug: master in: LIST#0111
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: Loading modules from directory: /usr/lib64/dovecot/auth
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: Module loaded: /usr/lib64/dovecot/auth/lib20_auth_var_expand_crypt.so
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: Module loaded: /usr/lib64/dovecot/auth/libdriver_sqlite.so
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: Loading modules from directory: /usr/lib64/dovecot/auth
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: Module loaded: /usr/lib64/dovecot/auth/libauthdb_ldap.so
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: ldap(/etc/dovecot/dovecot-ldap.conf.ext): LDAP initialization took 14 msecs
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: conn unix:auth-worker (pid=1541,uid=97): Server accepted connection (fd=14)
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: conn unix:auth-worker (pid=1541,uid=97): Sending version handshake
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: conn unix:auth-worker (pid=1541,uid=97): auth-worker<1>: Handling LIST request
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: conn unix:auth-worker (pid=1541,uid=97): auth-worker<1>: ldap(): Performing userdb lookup
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: conn unix:auth-worker (pid=1541,uid=97): auth-worker<1>: ldap: iterate: base=cn=Users,dc=adsample,dc=local scope=subtree filter=(objectClass=posixAccount) fields=uid
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: conn unix:auth-worker (pid=1541,uid=97): auth-worker<1>: ldap(): Finished userdb lookup
May 2 11:18:59 mail dovecot[959]: auth-worker(1542): Debug: conn unix:auth-worker (pid=1541,uid=97): auth-worker<1>: Finished
「objectClass=posixAccount」でフィルターをかけているが、Active DirectoryベースのLDAPサーバ標準では posixAccountは存在していないため、フィルター文字列を変える必要がある、という話である
確認のためldapsearchコマンドで出力がないことを確認
[root@mail dovecot]# ldapsearch -x -H ldaps://192.168.122.10 -D "cn=vmail,cn=Users,dc=adsample,dc=local" -w "パスワード" -b "dc=adsample,dc=local" -s subtree objectClass=posixAccount
# extended LDIF
#
# LDAPv3
# base <dc=adsample,dc=local> with scope subtree
# filter: objectClass=posixAccount
# requesting: ALL
#
# search reference
ref: ldaps://ForestDnsZones.adsample.local/DC=ForestDnsZones,DC=adsample,DC=lo
cal
# search reference
ref: ldaps://DomainDnsZones.adsample.local/DC=DomainDnsZones,DC=adsample,DC=lo
cal
# search reference
ref: ldaps://adsample.local/CN=Configuration,DC=adsample,DC=local
# search result
search: 2
result: 0 Success
# numResponses: 4
# numReferences: 3
[root@mail dovecot]#
どこの設定を変えればいいのか調べていくと userdb_ldap_iterate_fields
とuserdb_ldap_iterate_filter
で行っているので /etc/dovecot/dovecot-ldap.conf.ext に iterate_filter と iterate_attrs の設定を行う、ということがわかる
うまいことユーザ一覧っぽいのを取得するにはどうすればいいかな、とldapsearchコマンドをこねくり回して「ldapsearch -x -H ldaps://192.168.122.10 -D “cn=vmail,cn=Users,dc=adsample,dc=local” -w “パスワード” -b “dc=adsample,dc=local” -s subtree objectClass=user userPrincipalName」とすればいいかな、というのがわかった。
この結果をもとに、/etc/dovecot/dovecot-ldap.conf.ext に以下を追加してみたところおおむね期待通りの動作となった
iterate_filter=objectClass=user
iterate_attrs=userPrincipalName=user
これは、”objectClass=user”に該当するオブジェクトを表示させたあと、 userPrincipalName の値を dovecot上の user として認識させる、という意味合いの設定となる。
doveadm quota get -Aの実行結果
[root@mail dovecot]# doveadm quota get -A
Username Quota name Type Value Limit %
testuser1@adsample.local User quota STORAGE 9 10240 0
testuser1@adsample.local User quota MESSAGE 13 - 0
testuser2@adsample.local User quota STORAGE 14 10240 0
testuser2@adsample.local User quota MESSAGE 31 - 0
testuser3@adsample.local User quota STORAGE 0 10240 0
testuser3@adsample.local User quota MESSAGE 0 - 0
testuser4@adsample.local User quota STORAGE 0 10240 0
testuser4@adsample.local User quota MESSAGE 0 - 0
vmail@adsample.local User quota STORAGE 0 10240 0
vmail@adsample.local User quota MESSAGE 0 - 0
[root@mail dovecot]#
/etc/dovecot/conf.d/90-quota.conf を編集し、容量制限を1MBに変更
<略>
plugin {
# 10MB quota limit
quota = count:User quota
quota_rule = *:storage=1M
# This is required - it uses "virtual sizes" rather than "physical sizes"
# for quota counting:
quota_vsizes = yes
}
この状態でメールを送って容量を増やして確認・・・
[root@mail dovecot]# doveadm quota get -A
Username Quota name Type Value Limit %
testuser1@adsample.local User quota STORAGE 895 1024 87
testuser1@adsample.local User quota MESSAGE 16 - 0
testuser2@adsample.local User quota STORAGE 907 1024 88
testuser2@adsample.local User quota MESSAGE 38 - 0
testuser3@adsample.local User quota STORAGE 0 1024 0
testuser3@adsample.local User quota MESSAGE 0 - 0
testuser4@adsample.local User quota STORAGE 0 1024 0
testuser4@adsample.local User quota MESSAGE 0 - 0
vmail@adsample.local User quota STORAGE 0 1024 0
vmail@adsample.local User quota MESSAGE 0 - 0
[root@mail dovecot]#
2025/08/21 追記
動作試験のため、Active DirectoryにPowerShellのNew-Aduserコマンドを使って6万アカウントを作成した。
for($I=0;$I -lt 60000; $I++){
$count="{0:000000}" -f $I
$username="h"+$count
New-Aduser -emailaddress $username"@adsample.local" -SamAccountName $username -Name $username -DisplayName $username -GivenName $username -UserPrincipalName $username"@adsample.local"
}
そして、doveadm quota get -Aを実行したところ約1000件表示したところでエラー発生
# doveadm quota get -A|head -20
Username Quota name Type Value Limit %
h000000 User quota STORAGE 0 5120 0
h000000 User quota MESSAGE 0 - 0
h000001 User quota STORAGE 0 5120 0
h000001 User quota MESSAGE 0 - 0
h000002 User quota STORAGE 0 5120 0
h000002 User quota MESSAGE 0 - 0
<略>
h000999 User quota STORAGE 0 5120 0
h000999 User quota MESSAGE 0 - 0
doveadm(2465): Error: auth-master: userdb list: User listing returned failure
doveadm: Error: Failed to iterate through some users
#
この時、ログには以下が出力されていた
Aug 21 17:38:30 rhel9 dovecot[1135]: auth-worker(2463): Error: conn unix:auth-worker (pid=2462,uid=97): auth-worker<2>: ldap(): ldap_search(base=cn=Users,dc=adsample,dc=local filter=objectClass=user) failed: Size limit exceeded
これはopenldapの仕様で1000件までしか表示してくれないため、という制限だった。
dovecotのソースファイルを確認してみたけど、 ldap検索時のsizelimitについて解除できるような設定が見当たらなかった。
で、doveadmコマンドのqoutaについてdocを確認すると-Fオプションで確認したいユーザを列挙したファイルを与えて取得ができることがわかった
-F file
Execute the command for all the users in the file. This is similar to the -A option, but instead of getting the list of users from the userdb, they are read from the given file. The file contains one username per line.
ldapsearchコマンドによるユーザ名一覧作成についても1000件制限がかかるが、「-E pr=1000/noprompt」オプションを付けると解除されるため、全ユーザ一覧の取得ができる。
元の一覧作成で以下を使っていた場合
ldapsearch -x -H ldaps://192.168.122.10 -D "cn=vmail,cn=Users,dc=adsample,dc=local" -w "パスワード" -b "dc=adsample,dc=local" -s subtree objectClass=user userPrincipalName
下記の様に実行すると userPrincipalName だけを取得できる
ldapsearch -x -H ldaps://192.168.122.10 -D "cn=vmail,cn=Users,dc=adsample,dc=local" -w "パスワード" -b "dc=adsample,dc=local" -s subtree objectClass=user userPrincipalName -E "pr=1000/noprompt" |grep "userPrincipalName:" | awk '{ print $2 }'
これをファイルに保存して「doveadm quota get -F ファイル名」を実行することで対応可能となる。
で、これをスクリプト化したいわけですが、スクリプトに直接パスワードを書くのではなく、 /etc/dovecot/dovecot-ldap.conf.ext に書いたヤツを流用してくれるとうれしいわけです。
概ねこんな感じでしょうか?(注:これを使う場合、例えば「dn=」のあとにスペースがあるとエラーになります)
#/bin/bash
# dovecotのLDAP設定が書いてあるファイル
ldapconf=/etc/dovecot/dovecot-ldap.conf.ext
### 一時ファイル関連の処理
# 一時ファイルを作る
tmpfile=$(mktemp)
# 生成した一時ファイルを削除する
function rm_tmpfile {
[[ -f "$tmpfile" ]] && rm -f "$tmpfile"
}
# 正常終了したとき
trap rm_tmpfile EXIT
# 異常終了したとき
trap 'trap - EXIT; rm_tmpfile; exit -1' INT PIPE TERM
### 実際の処理
# 設定ファイルから値を読み込み
tmpuris=`grep "^uris=" $ldapconf|awk -Fis= '{ print $2 }'`
tmpdn=`grep "^dn=" $ldapconf|awk -Fdn= '{ print $2 }'`
tmpdnpass=`grep "^dnpass=" $ldapconf|awk -Fss= '{ print $2 }'`
tmpbase=`grep "^base=" $ldapconf|awk -Fse= '{ print $2 }'`
tmpfilter=`grep "^iterate_filter=" $ldapconf|awk -Fer= '{ print $2 }'`
ldapsearch -x -H "$tmpuris" -D "$tmpdn" -w "$tmpdnpass" -b "$tmpbase" -s subtree $tmpfilter userPrincipalName -E "pr=1000/noprompt" |grep "userPrincipalName:" | awk '{ print $2 }' > $tmpfile
doveadm quota get -F $tmpfile
一時ファイルを作成するあたりの処理は晴耕雨読「Bashで一時ファイルを作る方法」のものを使用しました。