funini.com 自由研究 ldap-new

LDAP on Debian

Debianクラスターのユーザー管理のためにLDAPを使うことになりました。
前にVineでさんざんはまってるから今回も…と思ったらやっぱりはまりました。 LDAPまわりが面倒な原因は、 あたりでしょうか。とても嫌です。
Debianユーザーの方は、このマニュアルを上から順に丁寧に真似れば動くと思います。

LDAP入門 (ldif)

ldapでは、全部の要素は「オブジェクト」として管理されています。 オブジェクトは何行かのテキストで定義されていて、dn=ではじまる最初の一行で識別できます。
例えばこんな感じ。
dn: uid=kei,ou=People,o=funini
uid: kei
userPassword: (ひみつ)
loginShell: /bin/tcsh
homeDirectory: /home/kei
objectClass: posixAccount
...
これはどういう風に読むかというと… で、それぞれのou=Peopleとかo=funiniとかが別に定義されています。 あと、ouとかoとかは「オブジェクトを構成する要素」として存在していないといけない。
dn:の次の行からはじまるのは、実際の属性です。(パスワードとか) 子オブジェクトは親オブジェクトが持っている属性を引き継ぎます。 また、objectClass:という行で、その他のオブジェクトを継承することができます。 例えばここではposixAccountというのを引き継いでいます。 posixAccountというobjectClassには、ホームディレクトリの場所とかユーザーIDとかシェルとかの情報が 定義されていて、このuid=keiというオブジェクトもその中身がちゃんと埋められていなければなりません。 そうすることで、このオブジェクトをunixのログイン情報として使えるようになります。
こういう風にオブジェクトの関係をテキストファイルに書いたのを、ldifと呼びます。冗長ではあるけど、XMLよりは書きやすいでしょう。LDAPに入っているのは、こういうたくさんのldifで書かれたオブジェクトです。

認証までの道のり

色々と混乱しやすいので、ここでは「どのパッケージが何をするものか」簡単にまとめます。これら全てを正しく設定して、はじめて認証が成功します。
slapd
LDAPはデータベースで、サーバーとクライアントがあります。通常はサーバーは一台、クライアントはたくさんになるでしょう。Debianの場合、サーバーにはslapdというパッケージに入っていて、サーバーを操作するコマンドは「slapなんとか」という名前です。これらのコマンドは直接ファイルを操作するため、原則サーバーが動いているマシンで、サーバーが停止している時のみ用いることができます。
サーバーを操作する時には、LDAPのパスワードは必要ありません。設定ファイルは/etc/slapd.confです。データベースの実体は/var/lib/ldapにあるので、これらのファイルをクリアすれば、データベースを初期化できます。(その場合の動作の保証はしませんが…)
よく使うコマンドはこんな感じです。
ldap-utils
サーバーがあるマシンにおいてでも、サーバー自体を直接いじることはあまりなくて、通常はldapのクライアントを介して操作します。これはldap-utilsというパッケージに入っています。コマンド名はldapaddやldapdeleteなどといった、「ldapなんとか」となっています。これらのクライアントの設定ファイルは/etc/ldap/ldap.confです。
nss-ldap
LDAPを使うのは、ログインとかSSHとかsuの認証です。これらでLDAPを用いるのに用いられる基本的な方法は、このnss-ldapを用いる方法です。nss-ldapを導入すると、nsswitch.confでldapというオプションを用いることができます。nsswitch.confは認証を切り替える最も基本的なファイルで、nisの設定は主にここで行います。特にデーモンなどは立っていないので、設定の変更は即座に反映されます。nss-ldap自体の設定ファイルは/etc/libnss-ldap.confです。
一つ注意する点は、この方法を用いる場合には、ldifのユーザー情報のパスワードの欄がCRYPT形式になっていなければならない点です。このため、nss-ldap単独で認証を行うのは希で、通常はlibnss-ldapに加えてlibpam-ldapを用いて認証を行います。ただ、この方法はlibpam-ldapよりはまりどころが少ないので、サーバーがちゃんと動いていることの確認として一度試してみるといいかもしれません。
libpam-ldap
PAMは、Linuxの新しい認証システムです。設定は/etc/pam.dにあり、様々な認証ポリシーを適用することができます。sshやsuといったソフトも、基本的にpamに対応しています。PAMを用いる設定では、nsswitch.confよりきめ細かい設定が出来ます。自由度が高い分、はまりどころも多くなっています。なお、これも設定は即座に反映されます。
libpam-ldapパッケージは、pamで用いることができる、LDAP用の認証モジュールです。設定ファイルは/etc/pam_ldap.confです。(他のDistributionでは/etc/ldap.confとなっているものもあります) 実際にpamに組み込む設定は、/etc/pam.d以下で行います。各ソフト共通の設定は/etc/pam.d/common-authと/etc/pam.d/common-accountに書かれているので、これらにpam_ldapを記述するのが普通の方法でしょう。

インストール・設定

debianでは、パッケージのインストール時に様々な設定が対話的に行われます。慣れるまでは、一度変な設定をするとどこが書き換えられるのか分からなくて、けっこう困ります。一応dpkg-reconfigure (パッケージ名)で呼び出せますが、一つずつ設定ファイルを自分で書くのがおすすめです。
slapd
aptでさくっとインストール。
# apt-get install slapd
slapd.confを書きます。
include         /etc/ldap/schema/core.schema
include         /etc/ldap/schema/cosine.schema
include         /etc/ldap/schema/nis.schema
include         /etc/ldap/schema/inetorgperson.schema

schemacheck     on
pidfile         /var/run/slapd/slapd.pid
argsfile        /var/run/slapd/slapd.args
loglevel        0
modulepath	/usr/lib/ldap
moduleload	back_bdb
sizelimit 500
tool-threads 1

backend		bdb
checkpoint 512 30
database        bdb

suffix          "o=santa"
rootdn          "cn=root,o=santa"
rootpw		{CRYPT}m66BJOZilrzn2

directory       "/var/lib/ldap"

dbconfig set_cachesize 0 2097152 0

dbconfig set_lk_max_objects 1500
dbconfig set_lk_max_locks 1500
dbconfig set_lk_max_lockers 1500
index           objectClass eq
lastmod         on

access to attrs=userPassword,shadowLastChange
        by dn="cn=admin,o=santa" write
        by anonymous auth
        by self write
        by * none

access to dn.base="" by * read

access to *
        by dn="cn=admin,o=santa" write
        by * read
だらだら長いですが、標準設定から書き換えたのはオレンジ色のとこだけです。
suffixは、このLDAPサーバーに入るオブジェクト全てが継承するオブジェクト(ルートオブジェクト)です。通常はドメイン名(dc=なんとか、の繰り返し)を用いるようですが、今回は簡単にo=santaという風にo(organization)を用いました。これは各自に環境で変えましょう。
データベースには、全てのデータを扱うことができるrootアカウントが必要です。他のユーザーはLDAPの中に書きますが、rootだけはslapd.confに書きます。(そうでないとサーバーに追加できるユーザーがいないから) rootpwは平文でもOKですが、実際運用するときはこのようにCRYPT暗号化(ハッシュ化)しましょう。このCRYPT化したパスワードの作り方は、
$ slappasswd -h {CRYPT}
New password : (パスワードを入力)
とするとよいです。たぶんMD5も使えます。
それと別に、パスワードまわりを扱える(パスワード変更とか)アカウントを作っておくのが普通みたいです。ここではcn=admin,o=santaとしました。このアカウントのパスワードはこの設定ファイルに書くのではなく、LDAP内のオブジェクトとして作ります。
Debianのapt(dpkg)はインストール時に色々質問して、ルートオブジェクトとルートユーザー名を作ってくれて、LDAPサーバーも立ち上げてくれるようです。でも僕はその後でルートオブジェクトとかを変えてしまったので、LDAPに追加しなければいけません。そこで一度LDAPを止めて、新しいルートオブジェクト(ほか)を作成します。
# /etc/init.d/slapd stop
入れるldifはこんな感じ。
init.ldif
dn:o=santa
objectClass: organization
o:santa

dn: cn=admin,o=santa
objectClass: person
cn: admin
sn: admin
userPassword: {CRYPT}12I1Q4IqrllgM
今はサーバーが止まっているので、slapaddで追加できます。
# slapadd < init.ldif
slapcatで確認しましょう。追加したのと同じオブジェクトが入っていればOKです。
# slapcat
...
良さそうなら、LDAPサーバーを立ち上げます。
# /etc/init.d/slapd start
ldap-utils
次にコマンドラインでLDAPを操作するための、LDAPクライアントをインストールします。
# apt-get install ldap-utils
こっちも設定しちゃいましょう。設定ファイルは/etc/ldap/ldap.confです。
BASE	o=santa
HOST	127.0.0.1
SSL経由とか、ポートを指定したい時はHOSTの代わりにURIを設定します。
これで設定はおしまいです。ldapaddとかldapdelete, ldapsearchを使って、オブジェクト作ったり消したりしてみましょう。
まずは追加。slapaddとは違って、動いているサーバーに対して行います。LDAPプロトコルを用いるので、rootとかadminとかで認証する必要があります。
ユーザーkeiをkei.ldifファイルに書いてみます。
dn: uid=mai,ou=People,o=santa
uid: kei
cn:  kei
objectClass: posixAccount
objectClass: posixGroup
userPassword: hoge
loginShell: /bin/tcsh
uidNumber: 10001
gidNumber: 10001
homeDirectory: /tmp
これを追加します。
$ ldapadd -D "cn=root,o=santa" -v -x -W -f ~/kei.ldif 
検索してみます。
$ ldapsearch -C -x -D "cn=admin,o=santa" -W "uid=kei,ou=People,o=santa"
...
# kei, People, funini
dn: uid=kei,ou=People,o=santa
uid: kei
...
消してみます。
$ ldapdelete -D"cn=root,o=santa" -v -x -W  "uid=kei,ou=People,o=santa"


☆以下のようなエラーが出たときは、ホストを探せてません。
ldap_bind: Can't contact LDAP server (81)
まずはサーバーが立ち上がっているか確認して、さらに/etc/openldap/ldap.confでホストの設定を確認するか、ホスト名を-hで指定しましょう。-h localhostみたいに。

☆ldapはデータベースで、対話式にデータを足したり消したりできます。 一度ファイルに書き出した場合はコマンドラインから操作する方が楽ですが、 いくつかGUIツールもあって、そっちの方がわかりやすいです。
GUIツールとしては以下のものを紹介しておきます。

nss-ldap

ここからいよいよ認証に用いる設定です。まずはインストール。
# apt-get install libnss-ldap
設定ファイル書きます。
/etc/libnss-ldap.conf
base o=santa
uri ldap://127.0.0.1/
ldap_version 3
rootbinddn cn=admin,o=santa
あと、adminのパスワードを/etc/libnss-ldap.secretに(平文で)書きます。 これのパーミッションは400にします。 こうして作ったエントリを認証に使えるようにします。 これでエントリはできてるはずなので、次に認証でちゃんとLDAP見るようにします。
# auth-config
で設定するのがお手軽です。設定は速攻で反映されます。 僕の環境では「User Information (一枚目)はLDAP・Authentication Configuration (二枚目)はUse Shadow PasswdでLDAP」 でした。 この設定中にはごく簡単にログインできなくなったりするので、前もってrootの端末を二つ開いておいて、 片方を設定用、もう片方をsuやsshでのログインテスト用にするのが良さそうです。
自分で設定したい人は、/etc/nsswitch.confと/etc/pam.d/system-authを編集します。 /etc/pam.d以下の設定は認証のために呼び出すモジュールを指定しています。 何回ログインに失敗したらログインできない…とかいう設定もここで出来るようです。
nsswitch.confとPAMの関係はよく分かりません…
/etc/pam.d/system-auth
auth        required      /lib/security/pam_env.so
auth        sufficient    /lib/security/pam_unix.so likeauth nullok
auth        sufficient    /lib/security/pam_ldap.so use_first_pass
auth        required      /lib/security/pam_deny.so

account     required      /lib/security/pam_unix.so
account     required      /lib/security/pam_ldap.so

password    required      /lib/security/pam_cracklib.so retry=3 type=
password    sufficient    /lib/security/pam_unix.so nullok use_authtok shadow
password    sufficient    /lib/security/pam_ldap.so use_authtok
password    required      /lib/security/pam_deny.so

session     required      /lib/security/pam_limits.so
session     required      /lib/security/pam_unix.so
session     optional      /lib/security/pam_ldap.so
/etc/pam.d/su
auth       sufficient   /lib/security/pam_ldap.so
auth       required     /lib/security/pam_unix_auth.so use_first_pass
account    sufficient   /lib/security/pam_ldap.so
account    required     /lib/security/pam_unix_acct.so
password   required     /lib/security/pam_cracklib.so
password   sufficient   /lib/security/pam_ldap.so
password   required     /lib/security/pam_pwdb.so use_first_pass
session    required     /lib/security/pam_unix_session.so
/etc/pam.d/sshd
auth       required     /lib/security/pam_nologin.so
#auth       required     /lib/security/pam_unix_auth.so try_first_pass

auth       sufficient   /lib/security/pam_ldap.so
auth       required     /lib/security/pam_unix_auth.so use_first_pass
account    sufficient   /lib/security/pam_ldap.so
account    required     /lib/security/pam_unix_acct.so
password   required     /lib/security/pam_cracklib.so
password   sufficient   /lib/security/pam_ldap.so
password   required     /lib/security/pam_pwdb.so use_first_pass
session    required     /lib/security/pam_unix_session.so
/etc/nsswitch.conf
passwd:     files ldap
shadow:     files ldap
group:      files ldap
できたらログインテスト。 ldapにしかいないユーザーを作ってみて、rootでsuします。 rootがsuするときにはパスワードは聞かれないから、結果はすぐわかります。
$ sudo su -
# su - rei
su: user rei does not exist (失敗)
...
# su - rei
$ whoami
rei  (成功)
また、こういう風に書き換えると、passwdコマンドがldap対応になるようです。 rootで
# passwd rei
てすると、ldapのパスワードを更新してくれます。 しかし、reiとしてログインしててpasswdって打つと、なぜかldapとそれ以外とパスワードを二回設定させられる。 でも/etc/passwdにいないユーザーだと、前者(ldapのほう)しか使われてないっぽい。

Try & errors

suは通るけど、sshで入れない…
/var/log/secureを確認したら、こんなエラー。
fatal: monitor_read: unsupported request: 24
いろいろいじっているうちに/etc/pam.d/sshdが書き換わっていたらしい。 上に書いたように設定して一件落着。

migration tools

既存の/etc/passwdの 移行用のスクリプトが/usr/share/openldap/migration/以下にあるので利用するのですが… 環境によってはさくっと行かないようです。 それにこの方法は簡単だけどSamba連携ができないし、ユーザーの追加・削除も手でLDAP叩かないといけないから、 認証の実験程度だと思っていて下さい。お勧めはSamba-LDAPを持いる方です。
本来はmigrate_all_online.plを実行するとおしまいなのですが、うちではエラーが出て止まってしまいました。 とはいえとりあえず僕はユーザーアカウントさえ移行できればいいので、migrate_passwd.plとmigrate_groups.plを 用いることにしました。
まずは/usr/share/openldap/migration/migrate_common.phを編集。 MAIL_DOMAINはログインだけならまぁ適当でいいようです。
$DEFAULT_MAIL_DOMAIN = "funini.com";
$DEFAULT_BASE = "o=funini";
で、/etc/passwdを移行。/etc/shadow使ってても大丈夫。
# /usr/share/openldap/migration/migrate_passwd.pl /etc/passwd > ~/ldif/passwd.ldif
# ldapadd -D "cn=root,o=funini" -v -x -w hoge  -f ~/ldif/passwd.ldif
同様にgroupも移行。
# /usr/share/openldap/migration/migrate_passwd.pl /etc/passwd > ~/ldif/passwd.ldif
# ldapadd -D "cn=root,o=funini" -v -x -w hoge  -f ~/ldif/passwd.ldif

現状の課題

ldapで管理してるユーザーのホームにpublic_htmlを作っても、apacheが~ユーザー名というURLに変換してくれない。(例えばユーザーmeiを作って、/home/mei/public_htmlにファイル置いてhttp://localhost/~mei/を見ても表示されない)
ln -s /home/mei/public_html/ \~mei
によってさしあたりの解決はできたけど、ちょっとあんまりですよね…
…と思ったら、pam.dあたりをいじっていたらいつの間にか直っていた。不思議不思議。