funini.com 自由研究 LDAP

ldap

☆Debian用になりますが、LDAPに関して新しいページを書きました。こちらの方が正確に書かれていると思います。
nisよりきれいにユーザー管理。sambaもメールも管理。

ldap入門。

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のログイン情報として使えるようになります。

インストール

まずはaptでさくっとインストール。
# apt-get install ldapd ldap nss_ldap
ldapdがサーバー。ldapが対話式のクライアントで、nss_ldapはログインとかSambaで使われるライブラリです。 サーバー関連のコマンド/ファイルはslapなんとかって名前が多いです。 サーバーの設定ファイルは/etc/openldap/slapd.conf、クライアントは/etc/ldap.confと/etc/openldap/ldap.conf。 この二つの違いは、ldapaddとかldapsearchみたいなopenldap付属のコマンドが使用するのが /etc/openldap/ldap.confで、nss_ldapの設定が/etc/ldap.confと使い分けられています。 debianだと後者は/etc/nss_ldap.confってファイル名になるらしい。
とりあえず、ドメイン名とルート(ldapを操作するための)のパスワードを設定。
/etc/openldap/slapd.conf
database        ldbm
suffix          "o=funini"
rootdn          "cn=root,o=funini"
rootpw		secret
# rootpw         {crypt}ijFYNcSNctBYg
このsuffixというところには、そのディレクトリを識別できるものを書きます。 基本的な文法は
first_name=kei,family_name=takahashi
みたいに、左に属性、右にその値というのの繰り返しです。 この属性は/etc/openldap/schema/core.schemaとかで定義されています。 ちゃんとしたサーバだとドメインを使って、ldap.funini.com→dc=ldap,dc=funini,dc=comみたいに書くのが普通らしい。 (dcはdomain componentの略) でも要はそのサーバーの中に来たリクエストが混信しなければいいんだから、とりあえずo=hogeでもいいと思う。
cn=rootというのは、このldapサーバーにエントリを追加しなくても操作できる人の名前です。(この場合はroot) rootpwのとこは、本気で運用するときはcryptパスワードとかMD5パスワード使いましょう。これらは、
# slappasswd -s secret -h {crypt}
{CRYPT}pGmONGC/tgv2M
みたいにして作れます。
設定済んだら下のコマンドでスタート。
# /etc/init.d/ldap start

クライアントの方も設定しちゃいましょう。 設定ファイル二つの違いは先に述べた通り。どっちも同じことを設定します。
/etc/openldap/ldapd.conf (ldapadd/ldapsearch用)
HOST            123.45.6.7
BASE            o=funini
/etc/ldapd.conf  (nss_ldapを使うその他のアプリケーション用)
HOST            123.45.6.7
BASE            o=funini
ここ(↓)にルートのアカウントを設定
rootbinddn cn=root,o=funini
/etc/ldapd.secret  (上のrootbinddnに対応したパスワード。)
hoge
パスワードを書いとくのはちょっとこわいんだけど、説明読むと 「rootとしてLDAPにアクセスしてきたときに使われる」みたいに読めるからいいのかなぁ、と。 パーミッションは400です。
これを設定しなくてもしないと、端末からのログインはできるけどsshとかsuとかがうまくいかなかった。

ルートオブジェクト作成

まずはo=funiniという、このサーバーに属するオブジェクト全てが親に持つ「ルートオブジェクト」を作ります。 オブジェクトを一個作る、とも見れるし、属性みたいなものを一つ作る、とも見れます。
まずは下のようなファイルを書きましょう。
dn: o=funini
objectClass: top
objectClass: organization
o:  funini
o=funiniって風にorganizationを指定するから、ちゃんとobjectClassとしてorganizationを指定します。
で、これをファイルに保存して突っ込みます。とりあえず~/funini.ldifに保存しました。
$ ldapadd -D "cn=root,o=funini" -v -x -w secret  -f ~/funini.ldif 
-v が対話モードの設定、-xが認証の設定、-wがパスワード、-fが入力ファイルです。
このo=funiniオブジェクトを元に、色んなオブジェクトを作ります。 とりあえずログイン用のPeopleとGroupを定義。
dn: ou=People,o=funini
objectClass: organizationalUnit
ou: People

dn: ou=Group,o=funini
objectClass: organizationalUnit
ou: Group
こっちもファイルに保存して突っ込みます。
$ ldapadd -D "cn=root,o=funini" -v -x -w secret  -f ~/people_and_groups.ldif 
このPeopleとGroupという名前を変更した場合は、/etc/ldap.confの以下の部分を編集します。
nss_base_passwd        ou=People,
...
ともあれこれで三つオブジェクトができたはず。
ちゃんと出来ているか、slapcatで確認。
# slapcat
dn: o=funini
objectClass: top
objectClass: organization
o: funini
....
ちゃんと表示されれば成功です。
以下のようなエラーが出たときは、ホストを探せてません。
ldap_bind: Can't contact LDAP server (81)
/etc/openldap/ldap.confでホストの設定を確認するか、ホスト名を-hで指定しましょう。-h localhostみたいに。

エントリ作ったり消したり。

ldapはデータベースで、対話式にデータを足したり消したりできます。 一度ファイルに書き出した場合はコマンドラインから操作する方が楽ですが、 いくつかGUIツールもあって、そっちの方がわかりやすいです。
GUIツールとしては以下のものを紹介しておきます。 まずは追加。これは実はいままでもやってますね。
ユーザーkeiをkei.ldifファイルに書いてみます。
dn: uid=mai,ou=People,o=funini
uid: kei
cn:  kei
objectClass: posixAccount
objectClass: posixGroup
userPassword: hoge
loginShell: /bin/tcsh
uidNumber: 510
gidNumber: 510
homeDirectory: /home/kei
gecos:kei
これを追加します。
$ ldapadd -D "cn=root,o=funini" -v -x -w hoge  -f ~/kei.ldif 
検索してみます。
$ ldapsearch -C -x -D "cn=root,o=funini" -w root "(&(uid=kei)(objectclass=posixAccount))"

# extended LDIF
#
# LDAPv3
# base <> with scope sub
# filter: (&(uid=kei)(objectclass=posixAccount))
# requesting: ALL
#

# kei, People, funini
dn: uid=kei,ou=People,o=funini
uid: kei
...

消してみます。
$ ldapdelete -D"cn=root,o=funini" -v -x -w hoge  "uid=kei,ou=People,o=funini"

ログインに使えるようにする

こうして作ったエントリを認証に使えるようにします。 これでエントリはできてるはずなので、次に認証でちゃんと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あたりをいじっていたらいつの間にか直っていた。不思議不思議。