▼nss_ldap使用時に ForestDnsZones への問い合わせを切る▼

nss_ldapや courier-authlib の authldap で ActiveDirectoryを参照していると、以下のような一見不要なDNS問い合わせが 大量に発生し、

Feb  8 10:15:29 hostname named[3184]: client 172.16.0.1#33596: query: ForestDnsZones.ad.example.jp IN AAAA
Feb  8 10:15:29 hostname named[3184]: client 172.16.0.1#33596: query: ForestDnsZones.ad.example.jp IN A
Feb  8 10:15:29 hostname named[3184]: client 172.16.0.1#33596: query: DomainDnsZones.ad.example.jp IN AAAA
Feb  8 10:15:29 hostname named[3184]: client 172.16.0.1#33596: query: DomainDnsZones.ad.example.jp IN A
Feb  8 10:15:29 hostname named[3184]: client 172.16.0.1#33596: query: ad.example.jp IN AAAA
Feb  8 10:15:29 hostname named[3184]: client 172.16.0.1#33596: query: ad.example.jp IN A
さらにこれらのアドレスが解決できない場合 (Windows系とUNIX系のDNSを分離している等)、 解決できないLDAP問い合わせが発生して長時間待たされたり、 設定は間違っていないのにアカウントが索けない、場合があります。

$Keywords: nss_ldap, ActiveDirectory, OpenLDAP, chase referrals, ForestDnsZones, DomainDnsZones, courier-authlib, spurious DNS query, nss_ldap slow $

$Id: ForestDnsZones-ldap.html,v 1.4 2011-03-10 20:33:31+09 kabe Exp $


▼原因

たとえば ActiveDirectory 側で "ad.example.jp" ドメインをこしらえると、 アカウントは LDAP的には CN=Users,DC=ad,DC=example,DC=jp 以下に格納されます。

OUを自分で作ると OU=uchi_no_ou,DC=ad,DC=example,DC=jp になるのが普通なので、

両方を参照したい場合は、LDAP問合せの BASEDN 設定は 最上位の "DC=ad,DC=example,DC=jp" (CN=Usersなし) にするのが普通です。

	[-] DC=ad,DC=example,DC=jp
	 |
	 r--[+] CN=Builtin
	 r--[+] OU=uchi_no_ou
	 r--[+] CN=Users

で、このBASEDNが最上位の設定で ActiveDirectoryに訊くと、 以下のように ref: の参照がくっついてきます。 CN=Users をつけると、くっついてきません。

% ldapsearch -x -D 'CN=ldapbind,CN=Users,DC=ad,DC=example,DC=jp' -w 'passwd' -H ldap://activedirectory/ '(CN=user)'
# extended LDIF
#
# LDAPv3
# base <> with scope sub
....
# search reference
ref: ldap://ForestDnsZones.ad.example.jp/DC=ForestDnsZones,DC=ad,DC=example,DC=jp

# search reference
ref: ldap://DomainDnsZones.ad.example.jp/DC=DomainDnsZones,DC=ad,DC=example,DC=jp

# search reference
ref: ldap://ad.example.jp/CN=Configuration,DC=ad,DC=example,DC=jp

LDAPライブラリは、ref:があった場合には順次 ldap://ForestDnsZones/ にも 問合せをしようとするので、

∴「nss_ldap+ActiveDirectory 使えねー」の評価となる

なお、冒頭で例示したDNSのログは named(8) のものですが、 通常の設定ではDNS queryのログは残りません。(量が多いから。) 見たい場合はnamedの設定を変えます。


▼対処

OpenLDAPライブラリ(ldap.conf(5))の REFERRALS 機能を 切ってしまえばとりあえず解決します。

▽nss_ldap

nss_ldap.confに "referrals no" を追加します。 マニュアルには記載が無い (というかマニュアルが無い) ですが、 nss_ldap のChangeLog には書いてあったりします。 以下のように nss_ldap.conf を加工してみたり。

nss_ldap.conf: 
...
# Without CN=Users, it will make AD return referral to
# ldap://ForestDnsZones.ad.example.jp/
# ldap://DomainDnsZones.ad.example.jp/
# ldap://ad.example.jp/
# which LDAP client tries to bind and wait for bind_timelimit timeout.
# To eliminate referral timeout, do one of:
# - make dummy entry in /etc/hosts: "0.0.0.1 ForestDnsZones.ad.example.jp"
# - make DNS return nothing for "ForestDnsZones.ad.example.jp"
#   (you need DNS other than the ActiveDirectory)
# - disable referrral tracking in ldap library/nss_ldap.conf as
deref never
referrals no
...

注: Red Hat 系では /usr/local/etc/nss_ldap.conf ではなく /etc/ldap.conf になっています。

▽courier-authlib

Courier-IMAP + courier-authlib + authldap の設定では nss_ldap の設定は関係ないので、直接 authdaemonrc (.(ドット) で読まれるシェルスクリプト)を加工します。

authdaemonrc:
...
## Squelch referrral and dereference to avoid connecting to
# ldap://ForestDnsZones.ad.example.jp/
export LDAPDEREF=never
export LDAPREFERRALS=no

環境変数での設定方法は ldap.conf(5) の冒頭を参照。 LDAP_REFERRALSLDAPREFERRAL ではないことに注意!

▽全部まとめて

そのホストの他の OpenLDAPクライアントでも REFERRALS を使うことは無い!と 断言していいなら、 グローバル設定の /etc/openldap/ldap.conf(5)に書き込んでしまう。 上の個別アプリケーション対処は不要になります。

/etc/openldap/ldap.conf: 

# See ldap.conf(5) for details
# This file should be world readable but not world writable.
...
REFERRALS no
...

▽再起動

設定変更した場合はサービスも再起動します。ライブラリ設定は デーモンの起動時に1回しか読まれないことが多いため。 crond, postfix, courier-authlib, courier-imap, sshdなど、できる範囲で restartしておきましょう。


▼対処2

LDAPクライアントの管理者が違っていて、 修正してもらえない!言っても理解しない! て場合は、 /etc/hosts や DNSを細工して ForestDnsZones に繋がらないようにします。

▽DNS

UNIX系とWindows系のDNSが別になっているのであれば、以下を設定してみます:

▽/etc/hosts

/etc/hosts に細工する方法もあります。

	/etc/hosts:
	0.0.0.1		ForestDnsZones.ad.example.jp
	0.0.0.2		DomainDnsZones.ad.example.jp
	0.0.0.3		ad.example.jp


▼ref: (referrals)を辿りたい場合は?

マルチドメインでフォレストを組んだ ActiveDirectory に対して nss_ldapを使って自動的にフォレストを辿りたい場合は、 ref: ldap://ForestDnsZones も解決して 索きなおす必要がありますが、

ほとんどの OpenLDAP クライアントは対応してません

ref: で飛ばされた別LDAPサーバに対しては、別のアカウントで ldap_bind(3)しなければならないこともある (ActiveDirectoryでは必須)ため、 OpenLDAPにはそれ用のフックである ldap_set_rebind_proc(3) が用意されていますが、

ldap_set_rebind_proc(3) のマニュアル部分は コメントアウト されているので、 開発者でも存在自体を知る人が少なく、 ほとんどのLDAPクライアントは対応していません。 nss_ldapも対応してません。

何も設定されていない状態だと referrals 参照は Anonymousバインドになるので、 それに対応しているLDAPサーバならともかく ActiveDirectory では 認証エラーになる。

man/man3/ldap_bind.3.gzには、以下のように コメントアウトされた状態で説明が載っています。

.\" .SH RE-BINDING WHILE FOLLOWING REFERRALS
.\" The
.\" .B ldap_set_rebind_proc()
.\" call is used to set a routine that will be called back to obtain bind
.\" credentials used when a new server is contacted during the following of
.\" an LDAP referral.  Note that this function is only available when the
.\" LDAP libraries are compiled with LDAP_REFERRALS defined and is only
.\" used when the ld_options field in the LDAP structure has
.\" LDAP_OPT_REFERRALS set (this is the default).  If
.\" .B ldap_set_rebind_proc()
.\" is never called, or if it is called with a NULL \fIrebindproc\fP
.\" parameter, an unauthenticated simple LDAP bind will always be done
.\" when chasing referrals.
.\" .LP
.\" \fIrebindproc\fP should be a function that is declared like this:
.\" .LP
.\" .nf
.\" int rebindproc( LDAP *ld, char **whop, char **credp,
.\" int *methodp, int freeit );
.\" .fi
.\" .LP
.\" The LDAP library will first call the rebindproc to obtain the
.\" referral bind credentials, and the \fIfreeit\fP parameter will be
.\" zero.  The \fIwhop\fP, \fIcredp\fP, and \fImethodp\fP should be
.\" set as appropriate.  If the rebindproc returns LDAP_SUCCESS, referral
.\" processing continues, and the rebindproc will be called a second
.\" time with \fIfreeit\fP non-zero to give your application a chance to
.\" free any memory allocated in the previous call.
.\" .LP
.\" If anything but LDAP_SUCCESS is returned by the first call to
.\" the rebindproc, then referral processing is stopped and that error code
.\" is returned for the original LDAP operation.


かべ@sra-tohoku.co.jp