現在位置: ホーム / OSSブログ / よろずブログ記事アーカイブ / RHEL6 系での OpenLDAP サーバと PAM 認証

RHEL6 系での OpenLDAP サーバと PAM 認証

旧OSSよろずブログより人気の高いコンテンツをArchiveとして公開します。 アクセス数No.1の記事は「RHEL6 系での OpenLDAP サーバと PAM 認証」です。

お世話になっております、SIOS 那賀です。

Scientific Linux 6.1 で OpenLDAP のサーバを立てようとして、はたと止まってしまいました。RHEL5 系まででお馴染みの /etc/openldap/slapd.conf がありません。どうやら RHEL6 からは、以前は slapd.conf に記述していたメタデータも、論理的には DN "cn=config" のノードの下に、物理的には /etc/openldap/slapd.d/ 以下に、LDIF を用いた「dynamic runtime configuration engine」なるデータ構造で格納されるように変更されたようです。それに伴い他のノードと同様、汎用の DIT 操作コマンドを用いて、しかも動的に設定できるようになっています。詳しくは RHEL6 の「移行管理ガイド」の「7.3.1 slapd 設定を変換する」や、本家ドキュメントの「OpenLDAP Software 2.4 Administrator's Guide: Configuring slapd」等を参照してください。

追記 (Mon Dec 12 2011): こちらの記事では slapd.d/ で頑張っていましたが、面倒になってしまい slapd.conf に戻ってしまった記事はこちら→「SIOS OSS Tech: RHEL6.1~系の OpenLDAP (slapd.conf) で SSH 公開鍵配布」。

RHEL6 系の初期 slapd.d/ は何のため?

さて、ここで一旦、少々脇道にそれます。上記のサイトを参照すると、既存の slapd.conf があるならば、slaptest コマンドで変換して slapd.d/ を作れば良いように読めますが、では、新規インストールをした場合にはどうすれば良いのでしょう? アップデートではなく新規で openldap-servers パッケージを入れても、以下のように、ちゃんとデータが構築されているように見えます。

[root@dirserv ~]# find /etc/openldap/slapd.d/
/etc/openldap/slapd.d/
/etc/openldap/slapd.d/cn=config.ldif
/etc/openldap/slapd.d/cn=config
/etc/openldap/slapd.d/cn=config/olcDatabase={-1}frontend.ldif
/etc/openldap/slapd.d/cn=config/olcDatabase={2}monitor.ldif
/etc/openldap/slapd.d/cn=config/olcDatabase={0}config.ldif
/etc/openldap/slapd.d/cn=config/cn=schema
/etc/openldap/slapd.d/cn=config/cn=schema/cn={11}collective.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={10}ppolicy.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={3}duaconf.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={9}openldap.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={0}corba.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={7}misc.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={2}cosine.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={6}java.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={4}dyngroup.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={5}inetorgperson.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={8}nis.ldif
/etc/openldap/slapd.d/cn=config/cn=schema/cn={1}core.ldif
/etc/openldap/slapd.d/cn=config/cn=schema.ldif
/etc/openldap/slapd.d/cn=config/olcDatabase={1}bdb.ldif

しかしこのデータ、アクセスしようにも、rootdn のパスワードが分かりません。RHEL の文書を見ても書かれていないようです。以下のように、ファイルも誰の持ち物でもありません。

[root@dirserv ~]# rpm -qf /etc/openldap/slapd.d/*
file /etc/openldap/slapd.d/cn=config is not owned by any package
file /etc/openldap/slapd.d/cn=config.ldif is not owned by any package

ということは、インストールの際に動的に作成しているのでしょう。%postinstall のスクリプトを見てみます。

[root@dirserv ~]# rpm -q --scripts openldap-servers
(中略。↓ インストール後のスクリプトにおいて、)
postinstall scriptlet (using /bin/sh):
(中略。↓ slapd.d/ ディレクトリが存在しなければ、)
# generate configuration in slapd.d
if ! ls -d /etc/openldap/slapd.d/* &>/dev/null; then
    (中略。↓ slapd.conf.obsolete なるファイルをコピーしてきて、)
    [ $fresh_install -eq 0 ] && \
        cp /usr/share/openldap-servers/slapd.conf.obsolete \
         /etc/openldap/slapd.conf
    (中略。↓ config 用のエントリを加えて、)
    cat >> /etc/openldap/slapd.conf << EOF
database config
rootdn   "cn=admin,cn=config"
#rootpw   secret
EOF
    (中略。slaptest コマンドで slapd.d/ 形式に変換します。)
    slaptest -f /etc/openldap/slapd.conf \
     -F /etc/openldap/slapd.d > /dev/null 2> /dev/null

上記のとおり、「#rootpw secret」となっています。パスワードは未設定です。これではアクセスできませんね。

さらに余談として、Ubuntu でも rootdn のパスワードは設定されていないのですが、かわりに、root ユーザで SASL EXTERNAL 認証でアクセスすると、何でもできるように考慮されています。Ubuntu 10.04 サーバガイドの「OpenLDAP Server」の項などを見ると、SASL EXTERNAL の接続でサクサクと rootpw を設定しているのに、なぜ RHEL で同じことができないのかと思えば、そういうことでした。

debian/slapd.init.ldif:

# Frontend settings
dn: olcDatabase={-1}frontend,cn=config
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: {-1}frontend
(中略)
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break

debian/slapd.install:

debian/slapd.init.ldif usr/share/slapd:

debian/slapd.postinst:

create_new_configuration() {
  (中略)
  init_ldif="/usr/share/slapd/slapd.init.ldif"
  (中略)
  capture_diagnostics slapadd -F "${SLAPD_CONF}" \ -b "cn=config" -l ${init_ldif} || failed=1

もっとも Ubuntu でも、従来 (2.4.21-0ubuntu5 以前) はこの点が考慮されていなかったらしく、アップデートの際には既存のデータに対して、下記のように sed で内部表現をゴリゴリと書き換えているんですけれどね。

debian/slapd.postinst:

postinst_upgrade_configuration() {
  (中略)
  # Grant manage access to connections made by the root user via
  # SASL EXTERNAL
  if previous_version_older 2.4.21-0ubuntu5 ; then
    if [ -d "$SLAPD_CONF" ]; then
      # Stick the new olcAccess at the begining of the
      # olcAccess list (using an index of 0 *and*
      # adding it as early as possible in the ldif file)
      # to make sure that local root has access to the
      # database no matter what other acls say.
      sed -i 's/^\(olcDatabase: {-1}frontend\)/\0\nolcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break/' "${SLAPD_CONF}/cn=config/olcDatabase={-1}frontend.ldif"
      sed -i 's/^\(olcDatabase: {0}config\)/\0\nolcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break/' "${SLAPD_CONF}/cn=config/olcDatabase={0}config.ldif"
    fi
  fi

これ同様に sed で書き換えるなり、以下のように slapd.d/cn=config/olcDatabase={0}config.ldif に "olcRootPW" のエントリをねじ込むなりしてサービスを上げ直せば、パスワードを設定することは可能です。これは、いよいよの「rootdn のパスワード忘れちゃった!」の際にも役に立つでしょう。

[root@dirserv ~]# service slapd stop
Stopping slapd:                                            [  OK  ]
[root@dirserv ~]# slappasswd -s secret1
{SSHA}QVHYz50xBxpbbEa5qO+5cn7jDktBVXr4
[root@dirserv ~]# echo 'olcRootPW:{SSHA}QVHYz50xBxpbbEa5qO+5cn7jDktBVXr4' >> /etc/openldap/slapd.d/cn\=config/olcDatabase\=\{0\}config.ldif
[root@dirserv ~]# service slapd startx
Starting slapd:                                            [  OK  ]
[root@dirserv ~]# ldapsearch -x -D "cn=admin,cn=config" -b "cn=config" -w secret1 "(olcRootDN=*)"
# extended LDIF
#
# LDAPv3
# base <cn=config> with scope subtree
# filter: (olcRootPW=*)
# requesting: ALL
#

# {0}config, config
dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcAccess: {0}to *  by * none
olcAddContentAcl: TRUE
olcLastMod: TRUE
olcMaxDerefDepth: 15
olcReadOnly: FALSE
olcRootDN: cn=admin,cn=config
olcRootPW: {SSHA}QVHYz50xBxpbbEa5qO+5cn7jDktBVXr4
olcSyncUseSubentry: FALSE
olcMonitoring: FALSE

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

ではなぜ RHEL6 では、アクセス手段のない slapd.d/ をわざわざ作っているのでしょうか? 実は最新の OpenLDAP の slapd でも、slapd.d/ が無いところへユーザが slapd.conf を置けば、従来と同様に slapd は slapd.conf から設定を読み込んでしまうのです (詳しくは openldap-*/servers/slapd/bconfig.c の read_config() 関数を読んでください)。よって、新規の際の slapd.d/ は、従来の slapd.conf 流の設定をあっさりとは使わせないようにするために、教育的見地からダミーとして置かれているものと見て良いでしょう。

さて、以上のようにデフォルトの slapd.d/ が単なるダミーであることが分かりましたし、本家の文書Red Hat の文書も共に、slapd.d/ 以下を手で編集するのはイカン、お行儀が悪いと言っておりますので、何ら躊躇することなく slapd.d/ 以下を削除して、自前の slapd.conf から slaptest コマンドで slapd.d/ を新規生成することにしましょう。

閑話休題、やっと本題に入ります。

サーバの設定

まずはいつものように、SELinux と iptables の無効化です。実運用のサーバでは、運用ポリシーに従って、それぞれ適切に設定してください。

[root@dirserv ~]# setenforce permissive
[root@dirserv ~]# getenforce
Permissive
[root@dirserv ~]# service iptables stop
iptables: ファイアウォールルールを消去中:                  [  OK  ]
iptables: チェインをポリシー ACCEPT へ設定中filter         [  OK  ]
iptables: モジュールを取り外し中:                          [  OK  ]
[root@dirserv ~]#

サーバと、操作用のクライアントをインストールします。

[root@dirserv ~]# yum install -y openldap-clients openldap-servers

"Obsolete" のファイルを元に、slapd.conf を作成します。

[root@dirserv ~]# slappasswd -s secret2
{SSHA}Z42Qdb/r/YAw0QRD8vI/SYcLgoxtzF4l
[root@dirserv ~]# cp /usr/share/openldap-servers/slapd.conf.obsolete slapd.conf
[root@dirserv ~]# vi slapd.conf
[root@dirserv ~]# diff -uNr /usr/share/openldap-servers/slapd.conf.obsolete slapd.conf
--- /usr/share/openldap-servers/slapd.conf.obsolete
+++ slapd.conf
@@ -64,9 +64,9 @@
 # /etc/pki/tls/certs, running "make slapd.pem", and fixing permissions on
 # slapd.pem so that the ldap user or group can read it.  Your client software
 # may balk at self-signed certificates, however.
-# TLSCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
-# TLSCertificateFile /etc/pki/tls/certs/slapd.pem
-# TLSCertificateKeyFile /etc/pki/tls/certs/slapd.pem
+TLSCACertificateFile /etc/pki/tls/certs/ca-bundle.crt +TLSCertificateFile /etc/pki/tls/certs/slapd.pem +TLSCertificateKeyFile /etc/pki/tls/certs/slapd.pem 
 # Sample security restrictions
 #      Require integrity protection (prevent hijacking)
@@ -95,14 +95,30 @@
 #
 # rootdn can always read and write EVERYTHING!

+access to attrs=userPassword + by dn="cn=Manager,dc=example,dc=com" write + by self write + by anonymous auth + by * none + +access to * + by dn.exact="cn=Manager,dc=example,dc=com" write + by self write + by * read +
 #######################################################################
 # ldbm and/or bdb database definitions
 #######################################################################

+database config +rootdn "cn=admin,cn=config" +rootpw {SSHA}QVHYz50xBxpbbEa5qO+5cn7jDktBVXr4 +  database       bdb
-suffix         "dc=my-domain,dc=com"
+suffix "dc=example,dc=com"  checkpoint     1024 15
-rootdn         "cn=Manager,dc=my-domain,dc=com"
+rootdn "cn=Manager,dc=example,dc=com" +rootpw {SSHA}Z42Qdb/r/YAw0QRD8vI/SYcLgoxtzF4l  # Cleartext passwords, especially for the rootdn, should
 # be avoided.  See slappasswd(8) and slapd.conf(5) for details.
 # Use of strong authentication encouraged.

(動いているようであれば) サービスを停止し、現在のデータを削除してから、新しいデータを作り、再度起動します。今回はテストなので、インデックスも BDB の設定も、そのまま放置します。実運用では、パフォーマンスやバックアップにも気をつかってあげてください。

[root@dirserv ~]# service slapd stop
slapd を停止中:                                            [失敗]
[root@dirserv ~]# rm -fr /etc/openldap/slapd.d/* /var/lib/ldap/*
[root@dirserv ~]# slaptest -f slapd.conf -F /etc/openldap/slapd.d/
bdb_db_open: warning - no DB_CONFIG file found in directory /var/lib/ldap: (2).
Expect poor performance for suffix "dc=example,dc=com".
bdb_db_open: database "dc=example,dc=com": db_open(/var/lib/ldap/id2entry.bdb) failed: No such file or directory (2).
backend_startup_one (type=bdb, suffix="dc=example,dc=com"): bi_db_open failed! (2)
slap_startup failed (test would succeed using the -u switch)
[root@dirserv ~]# chown -R ldap.ldap /etc/openldap/slapd.d/* /var/lib/ldap/*
[root@dirserv ~]# service slapd start
slapd を起動中:                                            [  OK  ]

以下は任意ですが、サーバの挙動が分かりやすいように、ログの出力も設定しておきます。slapd は local4 のファシリティで syslog 出力をするのですが、せっかくの RHEL6 系の rsyslog ですので、アプリケーション名 ("slapd") でログを振り分けるようにしてみます。実運用では、ログが溢れないように、ローテートの設定もしておいてください。

[root@dirserv ~]# cp /etc/rsyslog.conf /etc/rsyslog.conf.orig
[root@dirserv ~]# vi /etc/rsyslog.conf
[root@dirserv ~]# diff -uNr /etc/rsyslog.conf.orig /etc/rsyslog.conf
--- /etc/rsyslog.conf.orig
+++ /etc/rsyslog.conf
@@ -76,3 +76,6 @@
 # remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional
 #*.* @@remote-host:514
 # ### end of the forwarding rule ###
+ +!slapd +*.* /var/log/slapd.log [root@dirserv ~]# service rsyslog reload
Reloading system logger...                                 [  OK  ]

アクセスできるかどうか、試してみます。

[root@dirserv ~]# ldapsearch -x -D "cn=admin,cn=config" -b "cn=config" -w secret1 "(olcRootDN=*)"
[root@dirserv ~]# ldapsearch -x -D "cn=Manager,dc=example,dc=com" -b "dc=example,dc=com" -w secret2

良いようであれば、認証用のデータを入れて行きます。根元から作って行きます。

[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" -w secret2 <<EOF dn: dc=example,dc=com dc: example objectClass: dcObject objectClass: organization o: Example Corp. dn: cn=Manager,dc=example,dc=com cn: Manager objectClass: organizationalRole dn: o=Users,dc=example,dc=com o: Linux Users objectClass: organization dn: ou=People,o=Users,dc=example,dc=com ou: People objectClass: organizationalUnit dn: ou=Group,o=Users,dc=example,dc=com ou: Group objectClass: organizationalUnit EOF
adding new entry "dc=example,dc=com"

adding new entry "cn=Manager,dc=example,dc=com"

adding new entry "o=Users,dc=example,dc=com"

adding new entry "ou=People,o=Users,dc=example,dc=com"

adding new entry "ou=Group,o=Users,dc=example,dc=com"

ちょっと間違うと、途中まで作ったところでエラーになり、そこまでの分が半端に残ります。failure atomic ではありません…、トランザクションがあればいいのにね。今回はテストですので、そんな時は、以下のように根元からガバッと再帰で消してしまってください。

[root@dirserv ~]# ldapdelete -v -x -D "cn=Manager,dc=example,dc=com" -w secret2 -r "dc=example,dc=com"

ユーザ (foo と bar) を追加します。OpenLDAP のクライアント側 (SSSD, pam_ldap, nss-pam-ldapd 等) は、文書にはあまりキチンと書かれていないのですが (せいぜい /usr/share/doc/nss-pam-ldapd-0.7.5/README くらいか)、デフォルトで、クラス "posixAccount" や "posixShadow" を持つノードを、ユーザ情報として取りにきます。ですので、実はツリー構造は割と恣意的に決めてしまっても問題ありません。

[root@dirserv ~]# slappasswd -s secret3
{SSHA}LAs1y8ozcRtUNMVZh3Sj+t8Vn/48HJV3
[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" -w secret2 <<EOF dn: uid=foo,ou=People,o=Users,dc=example,dc=com objectClass: top objectClass: posixAccount objectClass: account gecos: ldap system users cn: foo uid: foo uidNumber: 1000 gidNumber: 1000 homeDirectory: /home/foo loginShell: /bin/bash userPassword: {SSHA}LAs1y8ozcRtUNMVZh3Sj+t8Vn/48HJV3 EOF
adding new entry "uid=foo,ou=People,o=Users,dc=example,dc=com"

[root@dirserv ~]# slappasswd -s secret4
{SSHA}8XOqcgEYQjI9vFreV8j6dc6B6Fjovfts
[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" -w secret2 <<EOF dn: uid=bar,ou=People,o=Users,dc=example,dc=com objectClass: top objectClass: posixAccount objectClass: account gecos: ldap system users cn: bar uid: bar uidNumber: 1001 gidNumber: 1001 homeDirectory: /home/bar loginShell: /bin/bash userPassword: {SSHA}8XOqcgEYQjI9vFreV8j6dc6B6Fjovfts EOF
adding new entry "uid=bar,ou=People,o=Users,dc=example,dc=com"

グループ (foo と bar) を追加します。こちらも同様に、クライアントはデフォルトで、クラス "posixGroup" を持つノードを探索に来ます。今回は、昨今の Linux ディストリビューションの構成と同様に、UPG (User Private Group) 式で、ユーザごとに個別に同名のグループを割り当てますが、LDAP 的には、共通のグループを割り当てる方が一般的かも知れません。

[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" -w secret2 <<EOF dn: cn=foo,ou=Group,o=Users,dc=example,dc=com cn: foo objectClass: posixGroup gidNumber: 1000 userPassword: {CRYPT}x EOF
adding new entry "cn=foo,ou=Group,o=Users,dc=example,dc=com"

[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" -w secret2 <<EOF dn: cn=bar,ou=Group,o=Users,dc=example,dc=com cn: bar objectClass: posixGroup gidNumber: 1001 userPassword: {CRYPT}x EOF
adding new entry "cn=bar,ou=Group,o=Users,dc=example,dc=com"

一般ユーザから、他人のパスワードが見えないことを確認しておきます。

[root@dirserv ~]# ldapsearch -x -D "uid=foo,ou=People,o=Users,dc=example,dc=com" -b "dc=example,dc=com" -w secret3 "(objectClass=posixAccount)"
[root@dirserv ~]# ldapsearch -x -D "uid=bar,ou=People,o=Users,dc=example,dc=com" -b "dc=example,dc=com" -w secret4 "(objectClass=posixAccount)"

認証依頼元の Linux ホストの設定

こちらも同様に、SELinux と iptables の無効化です。実運用のサー (以下テンプレ略)。

[root@linhost ~]# setenforce permissive
[root@linhost ~]# getenforce
Permissive
[root@linhost ~]# service iptables stop
iptables: ファイアウォールルールを消去中:                  [  OK  ]
iptables: チェインをポリシー ACCEPT へ設定中filter         [  OK  ]
iptables: モジュールを取り外し中:                          [  OK  ]
[root@linhost ~]#

RHEL5 系の nss_ldap から、RHEL6 系では PAM は pam_ldap に、NSS は nss_ldap からフォークした nss-pam-ldapd へと分離しており、また、それらを束ねる SSSD 抽象化レイヤ (詳細) が入っていたりと、設定ファイルが多岐に渡り、もはや手作業では手に負えなくなっているので、authconfig コマンドを使用します。

同時に、TLS (389 番ポートでの "STARTTLS" による接続) も設定します。authconfig では、認証局ナシでの TLS 設定ができないので、設定をした後で、ちょっとだけ手を入れる必要があります。

SSSD を用いる手順:

[root@linhost ~]# authconfig --disableforcelegacy --enablemkhomedir --enableldap --enableldapauth --enableldaptls --ldapserver=dirserv.vnat --ldapbasedn="dc=example,dc=com" --update
sssd を起動中:                                             [  OK  ]
oddjobd を起動中:                                          [  OK  ]
[root@linhost ~]# cat <<EOF >> /etc/sssd/sssd.conf ldap_tls_reqcert = never EOF
[root@linhost ~]# cat <<EOF >> /etc/nslcd.conf tls_cacertfile /etc/pki/tls/certs/ca-bundle.crt tls_checkpeer no EOF
[root@linhost ~]# service sssd restart
sssd を停止中:                                             [  OK  ]
sssd を起動中:                                             [  OK  ]

旧来の (legacy な) 手順:

[root@linhost ~]# authconfig --enableforcelegacy --enablemkhomedir --enableldap --enableldapauth --enableldaptls --ldapserver=dirserv.vnat --ldapbasedn="dc=example,dc=com" --update
nslcd を起動中:                                            [  OK  ]
oddjobd を起動中:                                          [  OK  ]
[root@linhost ~]# cat <<EOF >> /etc/nslcd.conf tls_cacertfile /etc/pki/tls/certs/ca-bundle.crt tls_reqcert no EOF
[root@linhost ~]# service nslcd restart
nslcd を停止中:                                            [  OK  ]
nslcd を起動中:                                            [  OK  ]
[root@linhost ~]# cat <<EOF >> /etc/pam_ldap.conf tls_cacertfile /etc/pki/tls/certs/ca-bundle.crt tls_checkpeer no EOF

では、動作を確認してみます。

[root@linhost ~]# id foo
uid=1000(foo) gid=1000(foo) 所属グループ=1000(foo)
[root@linhost ~]# su - foo
Creating home directory for foo.
[foo@linhost ~]$ su - bar
パスワード: secret4
Creating home directory for bar.
[bar@linhost ~]$ id
uid=1001(bar) gid=1001(bar) 所属グループ=1001(bar) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[bar@linhost ~]$ 

良いようですね。では。

タグ: , ,