sambaで作ったActive DirectoryではGet-ADDomainコマンドなどはエラーになる

sambaで作ったActive Directoryサーバ環境で、Windows ServerからPowerShellのGet-ADDomainコマンドを実行してみたところエラーとなった

PS C:\Users\administrator.ADSAMPLE> Get-ADDomain
Get-ADDomain : Active Directory Web サービスが実行されている状態で既定のサーバーを検索することはできません。
発生場所 行:1 文字:1
+ Get-ADDomain
+ ~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (ADSAMPLE:ADDomain) [Get-ADDomain], ADServerDownException
    + FullyQualifiedErrorId : ActiveDirectoryServer:1355,Microsoft.ActiveDirectory.Management.Commands.GetADDomain

PS C:\Users\administrator.ADSAMPLE>

サーバ名を指定すればいけるかな?と「Get-ADDomain -Server サーバ名」にしてもエラー

PS C:\Users\administrator.ADSAMPLE> Get-ADDomain -Server adsample.local
Get-ADDomain : サーバーと通信できません。サーバーが存在しないか、現在ダウンしているか、サーバー上で Active Directory Web サービスが実行されていない可能性があります。
発生場所 行:1 文字:1
+ Get-ADDomain -Server adsample.local
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (:) [Get-ADDomain], ADServerDownException
    + FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.GetADDomain

PS C:\Users\administrator.ADSAMPLE>

今度は「Active Directory Web サービスが実行されていない」ことがエラーの原因とされている。

Active Directory Web サービス について確認すると、どうやらsamba では提供されていないようだ。

2021年4月13日に作成されたsamba wikiのページ「ADWS / AD Powershell compatibility」に、「Samba does not support many of the AD PowerShell commands that manipulate AD objects,」とある

そこによると、2016年頃に「samba-adws」というPowerShellコマンドを使えるようにするプロジェクトが立ち上がって、開発中とのこと。

masterブランチの最終更新は2018年12月であるが、「garming-main」ブランチを見ると、2024年9月頃までなんかやっていたようで、それは https://github.com/GSam/samba-adws にておもに開発してたようで、それは https://github.com/GSam/samba で公開されているパッチ版で利用できるようだ

ただ、どちらにせよ、1年以上更新はされていないようだ

このため、PowerShellのActive Directory関連のコマンドレット群はsamba環境で使用できない、ということになるようだ

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