doveadmとjqコマンドを組み合わせて使用率の値だけを取り出す

doveadmとjqコマンドを組み合わせて既定パーセントを超えているユーザを一覧化する、というスクリプトを考えた

前提として、LDAP連携時、1000件を超えるアドレスがある場合、doveadm quota get -A での一括取得ができない、という制約がある。

このため、ldapserchコマンドを使ってユーザ一覧を出力した上で個別に処理を行っていく必要がある。

doveadm quota get -u ユーザ名の通常出力は以下となる

[root@mail ~]# doveadm  quota get -u testuser2
Quota name Type    Value Limit                                                             %
User quota STORAGE     6    50                                                            12
User quota MESSAGE     8     -                                                             0
[root@mail ~]#

doveadmコマンドには-fオプションがあってフォーマットを変えられ、例えばdoveadm -f tab quota get -u ユーザ名を実行するとタブ区切りで処理するためタブが1個だけで表示される

[root@mail ~]# doveadm -f tab quota get -u testuser2
Quota name      Type    Value   Limit   %
User quota      STORAGE 6       50      12
User quota      MESSAGE 8       -       0
[root@mail ~]#

最近のLinuxならjqコマンドがインストールされているのでJSONで形式で出力することにする

[root@mail ~]# doveadm -f json quota get -u testuser2
[{"root":"User quota","type":"STORAGE","value":"6","limit":"50","percent":"12"},{"root":"User quota","type":"MESSAGE","value":"8","limit":"-","percent":"0"}][root@mail ~]#

これを単純にjqコマンドに通す

[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r
[
  {
    "root": "User quota",
    "type": "STORAGE",
    "value": "6",
    "limit": "50",
    "percent": "12"
  },
  {
    "root": "User quota",
    "type": "MESSAGE",
    "value": "8",
    "limit": "-",
    "percent": "0"
  }
]
[root@mail ~]#

今回は 容量のみ見るので「STORAGE」に関してだけ取得したい。この場合は「.[]」で1段階掘り下げた上で「|」でその下に対する処理を行い、「select」で条件にあうものを選択する、という処理を行う

[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r '.[] | select(.type =="STORAGE") '
{
  "root": "User quota",
  "type": "STORAGE",
  "value": "6",
  "limit": "50",
  "percent": "12"
}
[root@mail ~]#

selectは複数つなげられる、というのでpercentについても追加するも、90以上なら表示のところ、12なのに表示されている

[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r '.[] | select(.type =="STORAGE" and .percent>90 ) '
{
  "root": "User quota",
  "type": "STORAGE",
  "value": "6",
  "limit": "50",
  "percent": "12"
}
[root@mail ~]#

じゃあ、percentが12なら表示に変更してみるが、何も表示されない。

[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r '.[] | select( .percent == 12 ) '
[root@mail ~]#

文字列問題か?ということで、数値を「””」でくくってみると表示される

[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r '.[] | select( .percent == "12" ) '
{
  "root": "User quota",
  "type": "STORAGE",
  "value": "6",
  "limit": "50",
  "percent": "12"
}
[root@mail ~]#

jqコマンド処理内で、文字列として認識されているものを数値として認識させるのは「tonumber」を通す

[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r '.[] | select(.type =="STORAGE" and (.percent|tonumber) > 90 ) '
[root@mail ~]#

10より上なら表示に変更

[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r '.[] | select(.type =="STORAGE" and (.percent|tonumber) > 10 ) '
{
  "root": "User quota",
  "type": "STORAGE",
  "value": "6",
  "limit": "50",
  "percent": "12"
}
[root@mail ~]# 

で、percentの数値だけを出力させるには以下となる

[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r '(.[] | select(.type =="STORAGE" and (.percent|tonumber) > 10 ) ).percent'
12
[root@mail ~]# doveadm -f json quota get -u testuser2 | jq -r '(.[] | select(.type =="STORAGE" and (.percent|tonumber) > 90 ) ).percent'
[root@mail ~]#

これを環境変数で実行できるように修正を試みるがエラーとなる

[root@mail ~]# limitpercent=10
[root@mail ~]# username=testuser2
[root@mail ~]# doveadm -f json quota get -u $username | jq -r '(.[] | select(.type =="STORAGE" and (.percent|tonumber) > $limitpercent ) ).percent'
jq: error: $limitpercent is not defined at <top-level>, line 1:
(.[] | select(.type =="STORAGE" and (.percent|tonumber) > $limitpercent ) ).percent
jq: 1 compile error
[root@mail ~]#

jqコマンドの–argオプションを使って jqコマンド内部用の$limitpercentを設定する必要があった

[root@mail ~]# doveadm -f json quota get -u $username | jq --arg limitpercent 10 -r '(.[] | select(.type =="STORAGE" and (.percent|tonumber) > $limitper
cent ) ).percent'
[root@mail ~]#

今回も文字列数値問題があるようなのでtonumberを追加

[root@mail ~]# doveadm -f json quota get -u $username | jq --arg limitpercent $limitpercent -r '(.[] | select(.type =="STORAGE" and (.percent|tonumber) > ($limitpercent|tonumber) ) ).percent'
12
[root@mail ~]#

これで、使用率の値だけの取り出しが出来た。

samba 4.23.3 で立てたActive Directoryサーバの機能レベルが2008R2から動かせない件を修正する

ESXi8 Free環境上に Active Directoryサーバを立てるか、と、AlmaLinux 9 で samba 4.23.3 をソースからコンパイルして構築した

# /usr/local/samba/bin/samba-tool domain provision --use-rfc2307 --interactive
Realm [ADSAMPLE.LOCAL]:
Domain [ADSAMPLE]:
Server Role (dc, member, standalone) [dc]:
DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE) [SAMBA_INTERNAL]:
DNS forwarder IP address (write 'none' to disable forwarding) [8.8.8.8]:  8.8.8.8
Administrator password:
Retype password:
INFO 2025-11-10 14:24:37,370 pid:1551 /usr/local/samba/lib64/python3.9/site-packages/samba/provision/__init__.py #2112: Looking up IPv4 addresses
<略>
INFO 2025-11-10 14:24:49,826 pid:1551 /usr/local/samba/lib64/python3.9/site-packages/samba/provision/__init__.py #501: DOMAIN SID:            S-1-5-21-1830428519-1651848948-1698044471
#

これで起動したActive Directoryサーバのフォレストレベル / ドメインレベル は下記の様にWindows 2008 R2 となっていた。

# samba-tool domain level show
Domain and forest function level for domain 'DC=adsample,DC=local'

Forest function level: (Windows) 2008 R2
Domain function level: (Windows) 2008 R2
Lowest function level of a DC: (Windows) 2008 R2
#

これをアップグレードしようと samba-tool domain level raiseコマンドを実行してみてもエラーとなる。

# samba-tool domain level raise --forest-level=2012_R2
ERROR: Forest function level can't be higher than the domain function level(s). Please raise it/them first!
# samba-tool domain level raise --domain-level=2012_R2
ERROR: Domain function level can't be higher than the lowest function level of a DC!
#

これはデフォルトのsamba設定で”ad dc functional level”が2008R2までとなっているからそういうことになっているのだという(参考:Samba domain controller: raising (all kinds of) level)

testparamコマンドを実行して現在の設定値を確認する

# /usr/local/samba/bin/testparm -s --section-name=global --parameter-name="ad dc functional level"
Load smb config files from /usr/local/samba/etc/smb.conf
Loaded services file OK.
Weak crypto is allowed by GnuTLS (e.g. NTLM as a compatibility fallback)

2008_R2
#

現状の /usr/local/samba/etc/smb.conf に記載はないが、 samba設定としては 2008_R2 として認識されている、ということを確認出来た

この結果を受けて/usr/local/samba/etc/smb.conf のglobalセクションに「ad dc functional level = 2016」という記述を追加する

# cat /usr/local/samba/etc/smb.conf
# Global parameters
[global]
        dns forwarder = 8.8.8.8
        netbios name = ADSERVER
        realm = ADSAMPLE.LOCAL
        server role = active directory domain controller
        workgroup = ADSAMPLE
        idmap_ldb:use rfc2307 = yes
        ad dc functional level = 2016

[sysvol]
        path = /usr/local/samba/var/locks/sysvol
        read only = No

[netlogon]
        path = /usr/local/samba/var/locks/sysvol/adsample.local/scripts
        read only = No
#

testparamで記述が反映されているかを確認

# /usr/local/samba/bin/testparm -s --section-name=global --parameter-name="ad dc functional level"
Load smb config files from /usr/local/samba/etc/smb.conf
Loaded services file OK.
Weak crypto is allowed by GnuTLS (e.g. NTLM as a compatibility fallback)

2016
#

sambaを再起動して、機能レベルがどうなったのかを確認

# systemctl restart samba-ad-dc
# samba-tool domain level show
Domain and forest function level for domain 'DC=adsample,DC=local'

Forest function level: (Windows) 2008 R2
Domain function level: (Windows) 2008 R2
Lowest function level of a DC: (Windows) 2016
#

Lowest function level of a DC が変更されたので、上2つも変更できるようになった

まずはドメインの機能レベルを変更

# samba-tool domain level raise --domain-level=2012_R2
Domain function level changed!
All changes applied successfully!
# samba-tool domain level show
Domain and forest function level for domain 'DC=adsample,DC=local'

Forest function level: (Windows) 2008 R2
Domain function level: (Windows) 2012 R2
Lowest function level of a DC: (Windows) 2016
#

続いてフォレストの機能レベルを変更

# samba-tool domain level raise --forest-level=2012_R2
Forest function level changed!
All changes applied successfully!
# samba-tool domain level show
Domain and forest function level for domain 'DC=adsample,DC=local'

Forest function level: (Windows) 2012 R2
Domain function level: (Windows) 2012 R2
Lowest function level of a DC: (Windows) 2016
#

これで問題なくなった。

dovecot / postfix と Active Directory連携時の動作調査手法

dovecot / postfix と Active Directoryを連携させようと設定してみたところ、最初はうまく動かなかった。

この動かない原因をどうやって調べていくか、というのを解説してるものがなく、非常に難儀したのでメモ書きとして残す

dovecot, postfixの現在の設定を確認

RHEL9の場合、 /etc/dovecot および /etc/postfix に設定ファイル群があるが、コメントやサブディレクトリにあるファイルとの結合により、最終的な設定が何になっているのかわかりにくい

「doveconf」および「postconf」を実行することで最終的な設定を確認することができる

また、デフォルト値と異なる部分は何かを「doveconf -n」「postconf -n」を実行することで確認できる。

なお、doveconfの場合、ssh_keyなど一部のパラメータについては「doveconf -P」と-Pオプションをつけないと実際の値が表示されない

dovecotのログ出力を増やす

ログ出力を増やすための設定がいろいろあった(Dovecot Logging)ため、 /etc/dovecot/conf.d/99-debug.conf と1つのファイルにまとめて必要ない場合は /etc/dovecot/conf.d/99-debug._conf と.conf という名前じゃなくすることで無効化できるようにした

[root@mail dovecot]# cat /etc/dovecot/conf.d/99-debug.conf
auth_debug=yes
auth_debug_passwords=yes
auth_verbose=yes
auth_verbose_passwords=yes
verbose_proctitle=yes
verbose_ssl=yes

[root@mail dovecot]#

設定後は「systemctl restart dovecot」で設定を有効にする

なお「doveadm log find」を実行するとdovecotのログがどのファイルに出力されているかを確認することができる

[root@mail dovecot]# doveadm log find
Looking for log files from /var/log
Debug: /var/log/maillog
Info: /var/log/maillog
Warning: /var/log/maillog
Error: /var/log/maillog
Fatal: /var/log/maillog
[root@mail dovecot]#

LDAP検索のログを増やす

dovecotの場合は dovecot本体への auth_verbose=yes 設定だけでLDAPでどういったqueryを投げているかも確認できる

ただ、もっと詳細を確認したい、という場合 /etc/dovecot/dovecot-ldap.conf.ext などのLDAP接続情報を書いたファイルに「debug_level」を追加することでログを増やすこともできる。

dovecot Common LDAP Settings for both auth and sieve
iredmail Turn on debug mode in Dovecot

dovecot標準値は「debug_level=0」。ログを増やす場合は「debug_level=1」、最大量に増やす場合は「debug_level=-1」とする

postfixの場合も同様に LDAP接続情報を書いたファイル /etc/postfix/ldap-mailbox.cf などに「debuglevel」を追加することでログを増やすことができる。

postfix ldap_table – Postfix LDAP client configuration

postfix標準値は「debuglevel=0」。増やす場合は「debuglevel=1」から最大量は「debuglevel=10」とする

postfixのログ出力を増やす

Postfix Debugging Howto に記載があるが正直めんどい

/etc/postfix/master.cf の smtpd起動に関して「-v」オプションか「-D」オプションをつける、という形となる。

とはいえ、postfix/dovecotを組み合わせた場合、ActiveDirectory/LDAP側の処理をdovecot側で行うということもあるので、まずはdovecot側の動作がちゃんとするのを先に確認したほうがよい。

dovecotでの認証確認

dovecotで認証動作を確認する場合、まずは「doveadm auth login ユーザ名」を実行する

[root@mail ~]# doveadm auth login testuser1@adsample.local
Password: <パスワード入力>
passdb: testuser1@adsample.local auth succeeded
extra fields:
  user=testuser1@adsample.local
  uid=1000
  gid=1000
userdb extra fields:
  testuser1@adsample.local
  uid=1000
  gid=1000
  auth_mech=PLAIN
[root@mail ~]#

なお、「doveadm auth login testuser1@adsample.local パスワード」とパスワードをつけて実行すると入力しないで済むので検証時は便利(ログに残るので一時なパスワードにすること)

ただし、このdoveadm auth loginコマンドは模擬的に確認しているだけで、実際にログイン処理は行っていないようで、テストしたユーザのディレクトリがない場合でもディレクトリが作成されない。(ちゃんとログインした場合は自動的に作成される)

ldapsearchコマンドを使った検証

doveadm auth loginコマンドでうまく認証が実行できない場合、 ldapsearchコマンドを使って原因を調査していったりする。

その場合、 /etc/dovecot/conf.d/99-debug.conf の設定を有効にしてから行う。

期待通りに動作していない場合、ログにある下記のような「ldap」「base=~」「filter=~」という記述に注目する

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&lt;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&lt;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&lt;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&lt;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&lt;1>: Finished

上記の場合注目するのは以下の部分

base=cn=Users,dc=adsample,dc=local scope=subtree filter=(objectClass=posixAccount) fields=uid

これをldapsearchコマンドに与える

-bオプションの後ろにbase=の後ろにある「cn=Users,dc=adsample,dc=local」
-sオプションの後ろにscope=の「subtree」
そしてfilterの「objectClass=posixAccount」

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

この出力結果を確認し、fields=で指定した「uid」という項目があるかを確認します

なければfilterで指定した条件が不適切、ということとなる

fieldsで適切かなぁ、というものが出るまでfilterの条件式の調整と、fieldsで選ぶ項目の調整を行う

各ユーザで取得できるLDAPの情報が何かを調べる場合は下記のようにuserPrincipalName、もしくはsAMAccountName で検索して確認する

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 userPrincipalName=testuser2@adsample.local

ldapsearchが1000件表示ぐらいで止まってしまう

openldapでの仕様で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 sAMAccountName -E pr=1000/noprompt

なお-Eオプションの意味は下記

   -E [!]ext[=extparam]

          Specify general extensions with -e and search extensions with -E.  ´!´ indicates
          criticality.

          General extensions:
            [!]assert=&lt;filter>    (an RFC 4515 Filter)
            !authzid=&lt;authzid>    ("dn:&lt;dn>" or "u:&lt;user>")
            [!]bauthzid           (RFC 3829 authzid control)
            [!]chaining[=&lt;resolve>[/&lt;cont>]]
            [!]manageDSAit
            [!]noop
            ppolicy
            [!]postread[=&lt;attrs>] (a comma-separated attribute list)
            [!]preread[=&lt;attrs>]  (a comma-separated attribute list)
            [!]relax
            sessiontracking[=&lt;username>]
            abandon,cancel,ignore (SIGINT sends abandon/cancel,
            or ignores response; if critical, doesn't wait for SIGINT.
            not really controls)

          Search extensions:
            !dontUseCopy
            [!]domainScope                       (domain scope)
            [!]mv=&lt;filter>                       (matched values filter)
            [!]pr=&lt;size>[/prompt|noprompt]       (paged results/prompt)
            [!]sss=[-]&lt;attr[:OID]>[/[-]&lt;attr[:OID]>...]  (server side sorting)
            [!]subentries[=true|false]           (subentries)
            [!]sync=ro[/&lt;cookie>]                (LDAP Sync refreshOnly)
                    rp[/&lt;cookie>][/&lt;slimit>]     (LDAP Sync refreshAndPersist)
            [!]vlv=&lt;before>/&lt;after>(/&lt;offset>/&lt;count>|:&lt;value>)  (virtual list view)
            [!]deref=derefAttr:attr[,attr[...]][;derefAttr:attr[,attr[...]]]
            [!]&lt;oid>[=:&lt;value>|::&lt;b64value>]

ldapsearchで特定に値があるエントリのみ出力

LDAPでmailアトリビュートが設定されているアカウント名とメールアドレスだけを出力させたい場合

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 mail=* sAMAccountName mail

-s substreeの後の語句がfilterで検索条件。「mail=*」はmailに値がなんでもいいのであれば良い、ということ

続く「sAMAccountName mail」が出力させたいattiribute名。いくつでも列挙可

mail=*の部分の条件を複雑化させることもできる

doveadmでメールボックスの状態確認

dovecotで認識しているメールボックスの状態を確認する「doveadm mailbox status」コマンド

全ユーザについてとりあえず全ステータスをとる場合はfieldをallにすればいい、というので「doveadm mailbox status -A all ‘*’」を実行してみる

[root@mail ~]# doveadm mailbox status -A all '*'
testuser1@adsample.local Trash messages=0 recent=0 uidnext=2 uidvalidity=1745978754 unseen=0 highestmodseq=5 vsize=0 guid=40b0b82833051c68570600003a0de1d0 firstsaved=never
testuser1@adsample.local INBOX messages=16 recent=0 uidnext=18 uidvalidity=1745978753 unseen=0 highestmodseq=22 vsize=613873 guid=3fa5f40281851168ee0500003a0de1d0 firstsaved=1746666817
testuser2@adsample.local Drafts messages=4 recent=0 uidnext=5 uidvalidity=1745978410 unseen=0 highestmodseq=9 vsize=2313 guid=fe4beb08b4d91268980500003a0de1d0 firstsaved=1746066862
testuser2@adsample.local Sent messages=28 recent=0 uidnext=35 uidvalidity=1745978409 unseen=0 highestmodseq=37 vsize=10545 guid=a28a5e0b7c201268cb0600003a0de1d0 firstsaved=1746018428
testuser2@adsample.local Trash messages=0 recent=0 uidnext=12 uidvalidity=1745978408 unseen=0 highestmodseq=11 vsize=0 guid=ab2c081a361e1268260600003a0de1d0 firstsaved=never
testuser2@adsample.local INBOX messages=9 recent=0 uidnext=15 uidvalidity=1745978407 unseen=0 highestmodseq=33 vsize=10810 guid=7e51333727841168d30500003a0de1d0 firstsaved=1746084330
testuser3@adsample.local INBOX messages=2 recent=2 uidnext=3 uidvalidity=1746166331 unseen=2 highestmodseq=3 vsize=1330 guid=7d83dc163b621468950800003a0de1d0 firstsaved=1746166331
[root@mail ~]#

ん?? vsizeは表示されていない?…マニュアルを再度確認すると -tオプションで messages, recent, unseen, vsizeだけを表示できるとのこと

[root@mail ~]# doveadm mailbox status -A all '*' -t
testuser1@adsample.local messages=16 recent=0 unseen=0 vsize=613873
testuser2@adsample.local messages=41 recent=0 unseen=0 vsize=23668
testuser3@adsample.local messages=2 recent=2 unseen=2 vsize=1330
testuser4@adsample.local messages=0 recent=0 unseen=0 vsize=0
vmail@adsample.local messages=0 recent=0 unseen=0 vsize=0
[root@mail ~]#

messages: メール総数
recent: Recentフラグがついてるメール数(新着メール=まだメールソフトに取り込んでない)
unseen: 未読メール数
vsize: メールの総容量(バイト)

dovecotの起動プロセス数確認

メールソフトからpop3/imapで接続すると、dovecot/pop3やdovecot/imapプロセスが起動する

例えば雑にpsコマンドの結果をとってみるとこんな感じ

[root@mail ~]# ps -ef|grep imap
vmail       2096    2088  0 17:36 ?        00:00:00 dovecot/imap [testuser2@adsample.local 192.168.122.1 IDLE]
vmail       2097    2088  0 17:36 ?        00:00:00 dovecot/imap [testuser2@adsample.local 192.168.122.1]
dovenull    2099    2088  0 17:37 ?        00:00:00 dovecot/imap-login [192.168.122.1 TLS proxy]
vmail       2101    2088  0 17:37 ?        00:00:00 dovecot/imap [testuser1@adsample.local 192.168.122.1 IDLE]
root        2118    1441  0 17:45 pts/0    00:00:00 grep --color=auto imap
[root@mail ~]#

これをdovecot側のコマンドで状態取得する場合は「doveadm service status サービス名」で取得する

[root@mail ~]# doveadm service status imap
name: imap
process_count: 3
process_avail: 0
process_limit: 1024
client_limit: 1
throttle_secs: 0
exit_failure_last: 0
exit_failures_in_sec: 0
last_drop_warning: 0
listen_pending: n
listening: y
doveadm_stop: n
process_total: 4
[root@mail ~]# doveadm service status imap-login
name: imap-login
process_count: 1
process_avail: 0
process_limit: 100
client_limit: 1
throttle_secs: 0
exit_failure_last: 0
exit_failures_in_sec: 0
last_drop_warning: 0
listen_pending: n
listening: y
doveadm_stop: n
process_total: 4
[root@mail ~]#

IMAPの場合、メールクライアントの実装によっては接続が維持されるのでprocess_limitの値に抵触しないかを注意する必要がある

Service Limits

Then client_limit needs to be set high enough to be able to serve all the needed connections (max connections=process_limit * client_limit).

acvite directory連携のdovecotでdoveadm quota get -Aが動かない

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&lt;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&lt;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&lt;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&lt;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&lt;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 &lt;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_fieldsuserdb_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で一時ファイルを作る方法」のものを使用しました。

postfixとactive directoryを連携させるときのLDA向け設定を検証した

postfix/dovecotを使ったメールサーバを作成する際、そのサーバ上でユーザ向けメールサービスを提供する場合にLDA設定を行う必要がある。

調べると dovecot-lda を使って保存するのがよさそうではある

参考資料
 dovecot側 「Dovecot LDA」「Dovecot LDA Examples」「Dovecot LDA with Postfix

ただ、これをActive Directory/LDAPと連携させる場合の記述についてがいまいちはっきりしない。

ベースとするのは「Dovecot LDA with Postfix」の「Virtual users」にある記述で、/etc/postfix/master.cf に dovecotの記述を追加して、 /etc/postfix/main.cf に dovecot_destination_recipient_limit , virtual_mailbox_domains , virtual_transport の設定を追加する、と読める。

ただ、これだけだとメールを格納する場所について書いてないなぁ、と思いつつ試してみた

その結果、 virtual_transport=dovecot を設定した場合は、 postfix側で “virtual_mailbox_maps= ldap:/etc/postfix/ldap-mailbox.cf”とか”virtual_mailbox_base= /var/vmail”とかの設定を入れて /etc/postfix/ldap-mailbox.cf でLDAPに関する設定を書いたりする必要なく、dovecot側で行ったLDAP連携設定をもとにdovecot側で処理してくれる、ということが分かった

で・・・これの確認をするための副産物として、 virtual_transport=dovecotとしているのに virtual_mailbox_maps= ldap:/etc/postfix/ldap-mailbox.cf とかpostfix側でLDAPを直接見に行くような設定をしてしまうとどうなるの?というのを確認していた(意図せずに・・・

gihyoにあるそろそろLDAPにしてみないか?第15回「FDS+Postfixでメールサーバ管理」と「デージーネットのOSS postLDAPadmin Appendix」を参照しつついろいろ検討

まず、今回、/etc/postfix/master.cf の最下行にdovecotに関する2行を追加した

[root@mail postfix]# tail /etc/postfix/master.cf
#
#scalemail-backend unix -       n       n       -       2       pipe
#  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store
#  ${nexthop} ${user} ${extension}
#
#mailman   unix  -       n       n       -       -       pipe
#  flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
#  ${nexthop} ${user}
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/dovecot-lda -f ${sender} -d ${recipient}
[root@mail postfix]#

そして、/etc/postfix/main.cf には以下を追加した

[root@mail postfix]# tail -10 /etc/postfix/main.cf
#
#
virtual_mailbox_domains = adsample.local
virtual_mailbox_maps= ldap:/etc/postfix/ldap-mailbox.cf
virtual_mailbox_base= /var/vmail
virtual_uid_maps= static:1000
virtual_gid_maps= static:1000
virtual_transport= dovecot
dovecot_destination_recipient_limit = 1

[root@mail postfix]#

で、postfix側のLDAP連携設定は以下とした

[root@mail postfix]# cat /etc/postfix/ldap-mailbox.cf
#server_host=192.168.122.10
server_host=ldaps://192.168.122.10
#server_port=636
#search_base=dc=adsample,dc=local
search_base=cn=Users,dc=adsample,dc=local
scope=sub
#query_filter = (&(objectClass=user)(mail=%s))
query_filter = (&(objectClass=user)(userPrincipalName=%s))
result_attribute = samAccountName
result_filter = /var/mail/%s/Maildir/

bind=yes
bind_dn=vmail@adsample.local
bind_pw=パスワード
version=3
#start_tls=yes
#debuglevel=10
[root@mail postfix]#

とりあえず「debuglevel=10」というのは調査中に有効にしていた値

virtual_mailbox_maps で取得できる情報として期待されているものは メールを保存するディレクトリ名 の模様

コメントとなっている「query_filter = (&(objectClass=user)(mail=%s))」はActive Directoryベースだとmailってないので使わない。ユーザ名のみの場合は「samAccountName=%s」、ドメイン名付きの場合は「userPrincipalName=%s」かな、というところで設定

取得できてるかどうかは「postmap -q ユーザ名 ldap:/~」で確認(パッケージ postfix-ldap がインストールされていない場合、”postmap: fatal: unsupported dictionary type: ldap”というエラーになる)

[root@mail ~]# postmap -q testuser1@adsample.local ldap:/etc/postfix/ldap-mailbox.cf
/var/mail/testuser1/Maildir/
[root@mail ~]#

想定しているディレクトリ名が出力されればOK

-vオプションをつけると検索内容の詳細が確認できる。

[root@mail ~]# postmap -v -q testuser1@adsample.local ldap:/etc/postfix/ldap-mailbox.cf
postmap: name_mask: all
postmap: inet_addr_local: configured 2 IPv4 addresses
postmap: inet_addr_local: configured 2 IPv6 addresses
postmap: dict_ldap_open: Using LDAP source /etc/postfix/_ldap-mailbox.cf
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: server_host = ldaps://192.168.122.10
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: server_port = 389
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: version = 3
postmap: dict_ldap_open: /etc/postfix/_ldap-mailbox.cf server_host URL is ldaps://192.168.122.10
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: scope = sub
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: search_base = cn=Users,dc=adsample,dc=local
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: timeout = 10
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: query_filter = (&(objectClass=user)(userPrincipalName=%s))
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: result_format = <NULL>
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: result_filter = /var/mail/%s/Maildir/
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: domain =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: terminal_result_attribute =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: leaf_result_attribute =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: result_attribute = samAccountName
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: special_result_attribute =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: bind = yes
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: bind_dn = vmail@adsample.local
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: bind_pw = パスワード
postmap: cfg_get_bool: /etc/postfix/_ldap-mailbox.cf: cache = off
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: cache_expiry = -1
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: cache_size = -1
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: recursion_limit = 1000
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: expansion_limit = 0
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: size_limit = 0
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: dereference = 0
postmap: cfg_get_bool: /etc/postfix/_ldap-mailbox.cf: chase_referrals = off
postmap: cfg_get_bool: /etc/postfix/_ldap-mailbox.cf: start_tls = off
postmap: cfg_get_bool: /etc/postfix/_ldap-mailbox.cf: tls_require_cert = off
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: tls_ca_cert_file =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: tls_ca_cert_dir =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: tls_cert =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: tls_key =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: tls_random_file =
postmap: cfg_get_str: /etc/postfix/_ldap-mailbox.cf: tls_cipher_suite =
postmap: cfg_get_int: /etc/postfix/_ldap-mailbox.cf: debuglevel = 0
postmap: dict_open: ldap:/etc/postfix/_ldap-mailbox.cf
postmap: dict_ldap_lookup: In dict_ldap_lookup
postmap: dict_ldap_lookup: No existing connection for LDAP source /etc/postfix/_ldap-mailbox.cf, reopening
postmap: dict_ldap_connect: Connecting to server ldaps://192.168.122.10
postmap: dict_ldap_connect: Actual Protocol version used is 3.
postmap: dict_ldap_connect: Binding to server ldaps://192.168.122.10 with dn vmail@adsample.local
postmap: dict_ldap_connect: Successful bind to server ldaps://192.168.122.10 with dn vmail@adsample.local
postmap: dict_ldap_connect: Cached connection handle for LDAP source /etc/postfix/_ldap-mailbox.cf
postmap: dict_ldap_lookup: /etc/postfix/_ldap-mailbox.cf: Searching with filter (&(objectClass=user)(userPrincipalName=testuser1@adsample.local))
postmap: dict_ldap_get_values[1]: Search found 1 match(es)
postmap: dict_ldap_get_values[1]: search returned 1 value(s) for requested result attribute sAMAccountName
postmap: dict_ldap_get_values[1]: Leaving dict_ldap_get_values
postmap: dict_ldap_lookup: Search returned /var/mail/testuser1/Maildir/
/var/mail/testuser1/Maildir/
postmap: dict_ldap_close: Closed connection handle for LDAP source /etc/postfix/_ldap-mailbox.cf
[root@mail ~]#

それっぽい動作をすることを確認して、postfixを再起動してみたところ”virtual_transport= dovecot”設定を無視して postfix側でLDAPを見に行くことを確認。(/etc/postfix/ldap-mailbox.cfにdebuglevel=10を設定すると、 /var/log/maillog に詳細ログが出ているので)

どうやら、2025年5月時点では postfix/dovecotでActive Directory連携するときは、postfix側ではLDAP連携をする必要はないようだ(aliasなどをAD側で設定している場合は別)

最終的に行った設定内容

結局のところ「Dovecot LDA with Postfix」の Virutal users記載ベースで/etc/postfix/master.cfの末尾にdovecot用設定2行追加。(元ネタでは /usr/local/libexec以下にあるけどRHEL9では/usr/libexecという違いに注意)

[root@mail ~]# tail /etc/postfix/master.cf
#
#scalemail-backend unix -       n       n       -       2       pipe
#  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store
#  ${nexthop} ${user} ${extension}
#
#mailman   unix  -       n       n       -       -       pipe
#  flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
#  ${nexthop} ${user}
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/dovecot-lda -f ${sender} -d ${recipient}
[root@mail ~]#

/etc/postfix/main.cf に以下を追加

[root@mail ~]# tail -5 /etc/postfix/main.cf
#
virtual_mailbox_domains = adsample.local
virtual_transport= dovecot
dovecot_destination_recipient_limit = 1
[root@mail ~]#

なお、この時にdovecot側のLDAP連携設定は以下となっている。(メールソフト側でIMAP/POP3用に入力するユーザ名をドメイン名付きにしたい場合はuserPrincipalName、ユーザ名のみにしたい場合は samAccountName を使う)

[root@mail ~]# cat /etc/dovecot/dovecot-ldap.conf.ext
dn= cn=vmail,cn=Users,dc=adsample,dc=local
dnpass= パスワード
#pass_filter= (samAccountName=%u)
#user_filter= (samAccountName=%u)
user_filter= (userPrincipalName=%u)
pass_filter= (userPrincipalName=%u)
user_attrs = =uid=1000, =gid=1000
pass_attrs = =uid=1000, =gid=1000

auth_bind=yes
uris=ldaps://192.168.122.10
base= cn=Users,dc=adsample,dc=local
scope=subtree

[root@mail ~]# cat /etc/dovecot/conf.d/auth-ldap.conf.ext
# Authentication for LDAP users. Included from 10-auth.conf.
#
# <doc/wiki/AuthDatabase.LDAP.txt>

passdb {
  driver = ldap

  # Path for LDAP configuration file, see example-config/dovecot-ldap.conf.ext
  args = /etc/dovecot/dovecot-ldap.conf.ext
}

# "prefetch" user database means that the passdb already provided the
# needed information and there's no need to do a separate userdb lookup.
# <doc/wiki/UserDatabase.Prefetch.txt>
#userdb {
#  driver = prefetch
#}

userdb {
  driver = ldap
  args = /etc/dovecot/dovecot-ldap.conf.ext

  # Default fields can be used to specify defaults that LDAP may override
  #default_fields = home=/home/virtual/%u
  default_fields = uid=vmail gid=vmail
}

# If you don't have any user-specific settings, you can avoid the userdb LDAP
# lookup by using userdb static instead of userdb ldap, for example:
# <doc/wiki/UserDatabase.Static.txt>
#userdb {
  #driver = static
  #args = uid=vmail gid=vmail home=/var/vmail/%u
#}
[root@mail ~]#

また /etc/dovecot/conf.d/10-mail.conf に mail_location = maildir:/var/mail/%n/Maildir という設定を追加している。

[root@mail ~]# diff -u /etc/dovecot/conf.d/10-mail.conf.org /etc/dovecot/conf.d/10-mail.conf
--- /etc/dovecot/conf.d/10-mail.conf.org        2025-04-25 03:13:54.044373479 +0900
+++ /etc/dovecot/conf.d/10-mail.conf    2025-04-30 10:59:12.661404241 +0900
@@ -27,7 +27,7 @@
 #
 # <doc/wiki/MailLocation.txt>
 #
-#mail_location =
+mail_location = maildir:/var/mail/%n/Maildir

 # If you need to set multiple mailbox locations or want to change default
 # namespace settings, you can do it by defining namespace sections.
[root@mail ~]#

エラー対処

/var/log/maillog を見てたら下記のようなpermission問題があった

May  1 18:28:19 mail dovecot[924]: auth: Debug: userdb out: USER#0111#011testuser2@adsample.local#011uid=1000#011gid=1000
May  1 18:28:19 mail dovecot[2244]: lda(testuser2@adsample.local)&lt;2244>&lt;/OWMMLM+E2jECAAAOg3h0A>: msgid=&lt;9da96806-84e5-4f26-9752-acf16b48d4dc@adsample.local>: saved mail to INBOX
May  1 18:28:19 mail postfix/pipe[2238]: AD3C12037F14: to=&lt;testuser2@adsample.local>, orig_to=&lt;testuser2@adosakana.local>, relay=dovecot, delay=0.13, delays=0.02/0/0/0.11, dsn=2.0.0, status=sent (delivered via dovecot service (lda(testuser2@adsample.local): Error: net_connect_unix(/run/dovecot/stats-writer) failed: Permission))
May  1 18:28:19 mail postfix/qmgr[2205]: AD3C12037F14: removed

エラー対処を調べると2019年1月のiredmailフォーラムの「Re: Error: net_connect_unix(/var/run/dovecot/stats-writer) failed」 が出てくる

これだと /etc/dovecot/dovecot.conf に直接追加しているが、 /etc/dovecot/conf.d/に新しいファイルを作って追加することにした

[root@mail conf.d]# vi /etc/dovecot/conf.d/12-stat.conf
[root@mail conf.d]# cat /etc/dovecot/conf.d/12-stat.conf
service stats {
    unix_listener stats-reader {
        user = vmail
        group = vmail
        mode = 0660
    }

    unix_listener stats-writer {
        user = vmail
        group = vmail
        mode = 0660
    }
}
[root@mail conf.d]# systemctl restart dovecot
[root@mail conf.d]#

これで出力されなくなった


メール送信時の細かい設定について

/etc/dovecot/dovecot-ldap.conf.ext と /etc/dovecot/conf.d/auth-ldap.conf.ext のそれぞれでuid/gidに関する設定を入れているが、それぞれの必要性について再検証

というのは、設定してるなかで、片方だけ設定を入れているとmissingログが出てたから両方入れた・・・はずだったんだけど、それって正しいの?という検証

まずは/etc/dovecot/conf.d/99-debug.conf にdebugログ出力設定を書いて 実施

[root@mail ~]# cat /etc/dovecot/conf.d/99-debug.conf
auth_debug=yes
auth_debug_passwords=yes
auth_verbose=yes
auth_verbose_passwords=yes
verbose_proctitle=yes
verbose_ssl=yes

[root@mail ~]#

ケース1

conf.d/auth-ldap.conf.ext に 「default_fields = uid=vmail gid=vmail」
dovecot-ldap.conf.ext で user_atrrs,pass_attrsの設定なし

この時メールを送信すると以下のような”result: uid missing”, “result: homeDirectory missing; uidNumber missing; gidNumber missing”ログがある

May  2 09:45:39 mail dovecot[1564]: auth: Debug: client in: AUTH#0111#011PLAIN#011service=imap#011session=BnUjeBw0hcjAqHoB#011lip=192.168.122.12#011rip=192.168.122.1#011lport=143#011rport=51333
May  2 09:45:39 mail dovecot[1564]: auth: Debug: client passdb out: CONT#0111
May  2 09:45:39 mail dovecot[1564]: auth: Debug: client in: CONT#0111#011AHRlc3R1c2VyMkBhZHNhbXBsZS5sb2NhbABkaWdpdGFsMTIzQSM= (previous base64 data may contain sensitive data)
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): Performing passdb lookup
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): bind search: base=cn=Users,dc=adsample,dc=local filter=(userPrincipalName=testuser2@adsample.local)
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): no fields returned by the server
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): result:  uid missing
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): Finished passdb lookup
May  2 09:45:39 mail dovecot[1564]: auth: Debug: auth(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): Auth request finished
May  2 09:45:39 mail dovecot[1564]: auth: Debug: client passdb out: OK#0111#011user=testuser2@adsample.local
May  2 09:45:39 mail dovecot[1564]: auth: Debug: master in: REQUEST#0112287206401#0111566#0111#011bef1c5eb0e8a2b05cf297e143bfb1cc6#011session_pid=1569#011request_auth_token
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): Performing userdb lookup
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): user search: base=cn=Users,dc=adsample,dc=local scope=subtree filter=(userPrincipalName=testuser2@adsample.local) fields=homeDirectory,uidNumber,gidNumber
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): no fields returned by the server
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): result:  homeDirectory missing; uidNumber missing; gidNumber missing
May  2 09:45:39 mail dovecot[1564]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<BnUjeBw0hcjAqHoB>): Finished userdb lookup
May  2 09:45:39 mail dovecot[1564]: auth: Debug: master userdb out: USER#0112287206401#011testuser2@adsample.local#011uid=1000#011gid=1000#011auth_mech=PLAIN#011auth_token=aaa072efd8fc1ceb4051c9c51b1fdbc9437e7f81

ケース2

conf.d/auth-ldap.conf.ext に default_fields 設定なし
dovecot-ldap.conf.ext で “user_attrs = =uid=1000, =gid=1000″,”pass_attrs = =uid=1000, =gid=1000” 設定あり

この場合、ログに xxx missing が出ない?

May  2 09:54:48 mail dovecot[959]: auth: Debug: client in: CONT#0111#011AHRlc3R1c2VyMkBhZHNhbXBsZS5sb2NhbABkaWdpdGFsMTIzQSM= (previous base64 data may contain sensitive data)
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): Performing passdb lookup
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): bind search: base=cn=Users,dc=adsample,dc=local filter=(userPrincipalName=testuser2@adsample.local)
May  2 09:54:48 mail dovecot[959]: auth: Debug: master in: USER#0111#011testuser1@adsample.local#011service=lda
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser1@adsample.local): Performing userdb lookup
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser1@adsample.local): user search: base=cn=Users,dc=adsample,dc=local scope=subtree filter=(userPrincipalName=testuser1@adsample.local) fields=
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): result: objectClass=top,top,top,top cn=testuser2 givenName=testuser2 distinguishedName=CN=testuser2,CN=Users,DC=adsample,DC=local instanceType=4 whenCreated=20250417094639.0Z whenChanged=20250430015944.0Z displayName=testuser2 uSNCreated=12616 uSNChanged=40983 name=testuser2 objectGUID=�u&#016$|-E�Z#034\O?�#036 userAccountControl=66048 badPwdCount=0 codePage=0 countryCode=0 badPasswordTime=133893655225944004 lastLogoff=0 lastLogon=133894260848965097 pwdLastSet=133893567990887154 primaryGroupID=513 objectSid=<no values> accountExpires=9223372036854775807 logonCount=16 sAMAccountName=testuser2 sAMAccountType=805306368 userPrincipalName=testuser2@adsample.local objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=adsample,DC=local dSCorePropagationData=20250418015428.0Z,20250418015428.0Z lastLogonTimestamp=133904519848472342; objectGUID,uSNCreated,objectCategory,objectClass,primaryGroupID,cn,givenName,objectSid,sAMAccountType,dSCorePropagationData,userAccountControl,name,codePage,lastLogon,logonCount,countryCode,lastLogoff,uSNChanged,pwdLastSet,distinguishedName,sAMAccountName,whenChanged,userPrincipalName,instanceType,badPwdCount,accountExpires,whenCreated,displayName,badPasswordTime,lastLogonTimestamp unused
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser1@adsample.local): result: objectClass=top,top,top,top cn=testuser1 givenName=testuser1 distinguishedName=CN=testuser1,CN=Users,DC=adsample,DC=local instanceType=4 whenCreated=20250417094618.0Z whenChanged=20250425001141.0Z displayName=testuser1 uSNCreated=12609 uSNChanged=36883 name=testuser1 objectGUID=#037��#�!#020F�j#020#010��#011x userAccountControl=66048 badPwdCount=0 codePage=0 countryCode=0 badPasswordTime=133900339076624909 lastLogoff=0 lastLogon=133900339256453379 pwdLastSet=133893567784742554 primaryGroupID=513 objectSid=<no values> accountExpires=9223372036854775807 logonCount=0 sAMAccountName=testuser1 sAMAccountType=805306368 userPrincipalName=testuser1@adsample.local objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=adsample,DC=local dSCorePropagationData=20250418015428.0Z,20250418015428.0Z lastLogonTimestamp=133900135017739905 mail=testuser1@example.com; objectGUID,uSNCreated,objectCategory,objectClass,primaryGroupID,cn,givenName,objectSid,sAMAccountType,dSCorePropagationData,userAccountControl,name,mail,codePage,lastLogon,logonCount,countryCode,lastLogoff,uSNChanged,pwdLastSet,distinguishedName,sAMAccountName,whenChanged,userPrincipalName,instanceType,badPwdCount,accountExpires,whenCreated,displayName,badPasswordTime,lastLogonTimestamp unused
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser1@adsample.local): Finished userdb lookup
May  2 09:54:48 mail dovecot[959]: auth: Debug: userdb out: USER#0111#011testuser1@adsample.local#011uid=1000#011gid=1000
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): Finished passdb lookup
May  2 09:54:48 mail dovecot[959]: auth: Debug: auth(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): Auth request finished
May  2 09:54:48 mail dovecot[959]: auth: Debug: client passdb out: OK#0111#011user=testuser2@adsample.local#011uid=1000#011gid=1000
May  2 09:54:48 mail dovecot[1423]: lda(testuser1@adsample.local)<1423><jFPYJdgXFGiPBQAAOg3h0A>: msgid=<c923fd79-8135-46e3-aa56-afd1a1282ff8@adsample.local>: saved mail to INBOX
May  2 09:54:48 mail postfix/pipe[1422]: 7FF342020373: to=<testuser1@adsample.local>, orig_to=<testuser1@adosakana.local>, relay=dovecot, delay=0.15, delays=0.03/0.01/0/0.12, dsn=2.0.0, status=sent (delivered via dovecot service)
May  2 09:54:48 mail postfix/qmgr[884]: 7FF342020373: removed
May  2 09:54:48 mail dovecot[959]: auth: Debug: master in: REQUEST#0113980787713#0111424#0111#011d2548dbb496c7cf3c62e2637869d2d69#011session_pid=1425#011request_auth_token
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): Performing userdb lookup
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): user search: base=cn=Users,dc=adsample,dc=local scope=subtree filter=(userPrincipalName=testuser2@adsample.local) fields=
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): result: objectClass=top,top,top,top cn=testuser2 givenName=testuser2 distinguishedName=CN=testuser2,CN=Users,DC=adsample,DC=local instanceType=4 whenCreated=20250417094639.0Z whenChanged=20250430015944.0Z displayName=testuser2 uSNCreated=12616 uSNChanged=40983 name=testuser2 objectGUID=�u&#016$|-E�Z#034\O?�#036 userAccountControl=66048 badPwdCount=0 codePage=0 countryCode=0 badPasswordTime=133893655225944004 lastLogoff=0 lastLogon=133894260848965097 pwdLastSet=133893567990887154 primaryGroupID=513 objectSid=<no values> accountExpires=9223372036854775807 logonCount=16 sAMAccountName=testuser2 sAMAccountType=805306368 userPrincipalName=testuser2@adsample.local objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=adsample,DC=local dSCorePropagationData=20250418015428.0Z,20250418015428.0Z lastLogonTimestamp=133904519848472342; objectGUID,uSNCreated,objectCategory,objectClass,primaryGroupID,cn,givenName,objectSid,sAMAccountType,dSCorePropagationData,userAccountControl,name,codePage,lastLogon,logonCount,countryCode,lastLogoff,uSNChanged,pwdLastSet,distinguishedName,sAMAccountName,whenChanged,userPrincipalName,instanceType,badPwdCount,accountExpires,whenCreated,displayName,badPasswordTime,lastLogonTimestamp unused
May  2 09:54:48 mail dovecot[959]: auth: Debug: ldap(testuser2@adsample.local,192.168.122.1,<D2PcmBw0S8rAqHoB>): Finished userdb lookup
May  2 09:54:48 mail dovecot[959]: auth: Debug: master userdb out: USER#0113980787713#011testuser2@adsample.local#011uid=1000#011gid=1000#011auth_mech=PLAIN#011auth_token=68518eb1e5a886522b5dcabaae828704633386c9

missing 出力がない??

そもそも、同じログレベル出力なのに、出力された内容に差がありすぎるんですが・・・

dovecot-ldap.conf.ext に “user_attrs = =uid=1000, =gid=1000″,”pass_attrs = =uid=1000, =gid=1000” を設定するだけでいける?