コンピュータを使う上で、検索はよく使う機能のひとつです。例えばユーザ名 からユーザIDやホームディレクトリ、シェルなどはpasswdデータベースを検索 することで得ることができます。このようにあるものをキーとして、それに付 随する情報を検索できるようなサービスを提供するサービスのことをディレク トリサービスといいます。ディレクトリ自体はコンピュータ上だけのものとは 限りません。例えば電話帳や住所録などは典型的なディレクトリの一種です。 これらは人の名前をキーとして、電話番号や住所などを得るためのディレクト リです。これらの例でわかる通りディレクトリというのは基本的に検索を主な 目的とするものです。電話帳や住所録は電話番号や住所をひくのが主で、普通 これに追加するのは情報をひくよりも稀なことです。
UNIXでも既に多くのディレクトリサービスが動いています。UNIXにおけるディ レクトリサービスとして代表的なものとしては以下のようなものがあります。
ドメイン情報
DNS
例えば、ファイルシステムはまさにディレクトリとよぶものでファイル名から ファイルの実体を参照するようになっています。/etc/passwdやNISというもの は、ユーザ名をキーとしてホームディレクトリやシェル、パスワードをひくた めのディレクトリだといえます。DNSはホスト名からIPアドレス、または逆に IPアドレスからホスト名の情報をひく世界にひろがった大規模なディレクトリ システムです。
つまり、ディレクトリサービスというのは既にシステムを動かすにはなくては ならない必要なサービスなのです。
このようなディレクトリサービスを汎用的に扱うようにできるようにするため の仕組として作られたのがLDAP(Lightweight Directory Access Protocol)で す。LDAPはディレクトリサービスを提供するために必要となる操作をおこなえ るプロトコルを定義したものです。LDAPは特定のディレクトリサービスを提供 するような限られたディレクトリシステムではなく、アプリケーションに必要 な情報を提供できるような枠組を提供しています。また、このプロトコルを利 用するためのAPI(Application Programming Interface)がさまざまなプログラ ミング言語で規定されています。既にC, C++, Java, Perl, Ruby, Pythonなど のプログラミング言語むけのAPIが存在しています。これらのAPIがそろってい るので、LDAPを利用したアプリケーションも作りやすくなっていますし、ディ レクトリそれ自体とそれを使うアプリケーションを分離することができます。
LDAPの前に、もともとCCITT/ITU-T(国際電気通信連合電気通信標準化部門)で X.500と呼ばれるディレクトリシステムの規格がさだめられていました。この X.500ディレクトリを使うための低コストで実現できるPCベースのフロントエ ンドを作ろうとしてLDAPが開発されました。当時のPCでも動くようにするため に、もともと複雑になりすぎていたX.500をTCP/IPの世界だけに限定すること で単純なものになりました。単純になったことでさまざまなシステムでも動く ようになっていき、結果的にひろく使えるようになっていったのです。X.500 ディレクトリにアクセスするためのプロトコルはDAP(Directory Access Protocol)と呼ばれていました。これを単純にしたものなので、Lightiweight DAPすなわちLDAPと呼ぶようになったのです。
LDAPを使ったシステムでは、LDAPで通信するクライアントサーバシステムとな ります。サーバ側にディレクトリ情報を格納しておき、クライアント側からそ れを検索したり、新たに登録したり、既にある情報を修正したり、削除したり することができます。LDAPでは、ディレクトリに格納する情報は階層構造をし ています(図1)。これをDIT(Directory Information Tree)と呼びます。UNIXのファイルシステムのディレクトリや、 DNSのドメイン名のような構造と同じような感じだと思えばいいでしょう。
| <root> | | | dc=jp | | | dc=example | +-------------+----------------+ | | | | ou=People ou=hosts | | | | | uid=foo uid=bar host=master
ファイルシステムでのフルパス名もしくはDNSでのFQDN(Fully Qualified Domain Name)に相当する、LDAPディレクトリ内でのエントリを一意に識別する ための名前を識別名(Distinguish Name;略してDN)と呼びます。ファイルシス テムのフルパス名ではパス名を/でつなげて記述していますし、DNSではドメイ ン名をドット(.)でつなげて記述していますが、LDAPでは識別名(DN)は相対識 別名(Relative Distinguish Name;略してRDN)をコンマ(,)でつなげて記述した ものです。
相対識別名(RDN)は「属性名(アトリビュート名)=属性値(アトリビュートの値)」 という形式で表現します。例えば「ou=People」のような表現になるわけです。
LDAPではDNSと同じように階層構造のトップから枝をたどるにしたがって前に 追加していって表現します。「dc=jp」から相対識別名(RDN) 「dc=example」でたどり、さらに相対識別名(RDN)「ou=People」 でたどり、さらに相対識別名(RDN)「uid=foo」でたどった時の識別名 (DN)は、これらを後からコンマ(,)でつないでいって 「uid=foo,ou=People,dc=example,dc=jp」と表現します (図2)。
| <root> | | | dc(domain component)がjp: dc=jp | | | dc(domain component)がexample: dc=example | | | ou(organizational unit)がPeople: ou=People | | | uid(user id)がfoo: uid=foo | ↓ | dn: uid=foo, ou=People, dc=example, dc=jp
DNSでサブドメインをきる時と同様に、LDAPでもある識別名(DN)に相対識別名 (RDN)を追加していくことでサブツリーを作っていくことになります。ひとつ のドメインに複数のサブドメインが作れるように、一つの識別名(DN)から複数 のサブツリーを作れます。もちろんその識別名(DN)からサブツリーを作る相対 識別名(RDN)はそれぞれ異なっていなければいけません。相対識別名(RDN)こそ がサブツリーを区別するものだからです。識別名(DN)では大文字小文字は区別 しません。従って「uid=foo,ou=people,dc=example,dc=jp」も 「UID=FOO,OU=PEOPLE,DC=EXAMPLE,DC=JP」も同じものを表しています。 またコンマ(,)の後の空白も無視されるので、「uid=foo, ou=people, dc=example,dc=jp」も同じものを表わすことになります。
それぞれの識別名(DN)は属性値(アトリビュート名)と属性値(アトリビュート の値)のペアの集りからなる情報を持つことができます。それがその識別名 (DN)によって表されるディレクトリエントリと呼びます。LDAPでよく使われる 属性名としてはリスト1のようなものがあ ります。ディレクトリエントリはこの属性名とその値のペアを一行づつ記述し ます。
common name, 人の名前
object class
domain component、ドメインの要素
organizational unit 組織の部局
organization 組織
country 国
street 住所
location 場所の名前
user id
surname ファミリーネーム
電話番号
例えばリスト2のように記述します。この記述方法を LDIF(LDAP Data Interchange Format)と呼びます。LDAPにおいてディレクトリ エントリの情報をテキストで表現する時にこのLDIF と呼ばれる表現形式を使 います。リスト2の情報を説明していくと次のようになり ます。
dn: uid=foo,ou=People,dc=example,dc=jp uid: foo objectclass: posixAccount uidNumber: 1001 gidNumber: 1001 homeDirectory: /home/foo loginShell: /bin/bash cn: foo bar
これはou=People,dc=example,dc=jpに相対識別名(RDN)uid=fooを追加したものです(図2) ouというのは組織の部局、dcというのはドメインの要素なのでou=People,dc=example,dc=jpというのはドメイン example.jpの Peopleという部局(人に関する情報をあつめたツリー)という意味になります。これに相対識別名(RDN)uid=fooを追加したものですから、example.jpドメインの人に関する組織の部局のユーザIDがfooの人のエントリであることを表現しています。
ユーザID(uid)がfooという情報を表しています。 この識別名(DN)の相対識別名(RDN)であるuid=fooと同じ情報です。これもディレクトリエントリの中に含めておきます。LDAPでは検索する時はディレクトリエントリの情報しか検索しないので、この行がないとuidがfooであるディレクトリエントリを探そうとしてもこのディレクトリエントリをみつけることができません。
このディレクトリエントリのobject classがposixAccountであるということをあらわしています。簡単に言ってしまうと、このディレクトリエントリはposixAccountであればもつべき情報をもっているということを表しているという意味になります。objectclassとは何かについては後述します。
ユーザID番号(uidNumber)が1001であることをあらわしています。
グループID番号(gidNumber)が1001であることをあらわしています。
ホームディレクトリ(homeDirectory)が/home/fooであることをあらわしています。
ログインシェル(loginShell)が/bin/bashであることをあらわしています。
この人の一般名(common name。略してcn)がfoo barであることをあらわしています。
このように識別名(DN)によって特定されるディレクトリエントリは属性値の集合によってその情報を表現することになります。
objectclassとは、そのディレクトリエントリがどのような属性を持たないと いけないか、どのような属性を持つことができるのかということをあらわすた めの特別な属性です。objectclass属性の値によって、そのディレクトリエン トリがどのような情報をもっているかを知ることができます。オブジェクト指 向でのオブジェクト(インスタンス)に対するクラスがなにかを表しているのと 同じような感じです。またデータベースでいうとスキーマ(schema)情報に相当 します。例えばposixAccountというobjectclassはRFC2307: An Approach for Using LDAP as a Network Information Serviceで次のように定義されていま す。
従ってobjectclass属性がposixAccountであるディレクトリエントリは必ずcn、uid、uidNumber、gidNumber、homeDirectory属性があることがわかるようになっています。
最初に説明した通り、ディレクトリサービスの基本機能は検索です。LDAPでも検索がよくおこなう操作となっています。LDAPで検索する時は次の情報を指定します。
ポート番号を指定しなかった時のLDAPのデフォルトのポート番号は389です。
既に説明した通り、LDAPではディレクトリ情報は階層構造になっています。検索をする時はその階層構造のどのサブツリーを検索するかを指定します。この時サブツリーのトップとなる識別名(DN)を指定します。これを検索ベース(basedn)と呼びます。
特に指定しないと、LDAPは検索ベースをトップとするサブツリー全部を検索対象とします。サブツリー全部を検索対象にするのでこれを検索スコープがsubであるといいます。 LDAPでは検索スコープsub以外にいくつか検索範囲を指定することができます。 検索ベースとその子供のディレクトリエントリだけを検索する時、つまり階層を辿って再帰的に孫のディレクトリエントリなどは検索しない時、一段階だけを検索対象とするのでこれを検索スコープがoneであるといいます。 また、検索ベースであらわされている識別名(DN)のディレクトリエントリだけを検索対象とすることもできます。これは検索ベースだけを検索対象とするので検索スコープがbaseになります(図3)。
| <root> | | | dc=jp | | | ____________________________ | | | | scope=base --> dc=example <--- basedn dc=example,dc=jp | | | | | | | | | | | +-------------+----------------+ | | scope=one | | | | +-> ou=People ou=hosts | | | | | | | | | | | | uid=foo uid=bar host=master | V scope=sub
LDAPでは検索するための条件はRFC2254で定義されている検索フィルターで表現します。検索フィルターでは次のような条件を記述することができます。
一致
「属性名=文字列」 指定した属性名の値が文字列と一致するディレクトリエントリを検索します。
uid=foo
属性uidの値がfooとなっているディレクトリエントリを検索します。
存在の確認
「属性名=*」 指定した属性をもっているディレクトリエントリを検索します。*はワイルドカードのようなもので、その属性名の値が何か存在しているものを検索する、つまりその属性をもっているディレクトリエントリを検索することになるわけです。
uid=*
属性uidを持つディレクトリエントリを検索します。
objectclass=*
LDAPではディレクトリエントリはobjectclass属性をもっているため、objectclass=*で検索することにより検索範囲内のすべてのディレクトリエントリを検索することができます。
部分文字列一致
「属性名=*文字列*」 指定した属性の値が指定した文字列を含むようなディレクトリエントリを検索します。
uid=*o*
属性uidの値にoを含むディレクトリエントリを検索します。
類似
「属性名~=文字列」 指定した属性の値が文字列に似ているようなディレクトリエントリを検索します。正確なスペルがわからない時などに使うことができます。
uid~=foo
属性uidの値がfuuに似たディレクトリエントリを検索します。
順序比較
「属性名>=値」 「属性名<=値」 指定した属性の値が指定した値以上のもの(「属性名>=値」の場合)もしくは値以下のもの(「属性名<=値」の場合)をもつディレクトリエントリを検索します。 「属性名>値」「属性名<値」などは指定できないことに注意してください。ただし、以下で説明する否定と組合せることで検索することはできます。
uidNumber>=2000
属性uidNumberが2000以上のディレクトリエントリを検索します。
否定
「(!(検索フィルター))」 検索フィルターで検索してひっかからなかった方のディレクトリエントリを検索結果にします。(!(属性名=文字列))だと、指定した属性の値が文字列ではないディレクトリエントリを検索することになります。 「属性名>値」に相当する検索をしたい時は「(!(属性名<=値))」で検索できます。「属性名<値」は「(!(属性名>=値))」になります。
(!(uidNumber>=1000))
uidNumberが1000以上でないディレクトリエントリ、すなわちuidNumberが1000より小さいディレクトリエントリを検索します。
(!(uidNumber<=2000))
uidNumberが2000以下でないディレクトリエントリ、すなわちuidNumberが2000より大きいディレクトリエントリを検索します。
論理積(&)
「(&(検索フィルター1)(検索フィルター2))」 検索フィルター1、検索フィルター2どちらの条件にもあうようなディレクトリエントリを検索します。さらに検索フィルターを追加したい時はうしろにどんどんつなげていくことになります。例えばもう一つ検索フィルターを追加する場合は「(&(検索フィルター1)(検索フィルター2)(検索フィルター3))」となります。Lispのandと同じようなものです。
cnにfooを含み、かつuidNumberが1000以上のエントリを検索します。
objectclassがposixAccountで、 uidNumberが1000以上2000以下のディレクトリエントリを検索します。
論理和(|)
「(|(検索フィルター1)(検索フィルター2))」 検索フィルター1もしくは検索フィルター2どちらかの検索条件でひっかかるディレクトリエントリを検索します。論理積の時と同様、さらに検索フィルターを追加したい時はうしろにどんどんつなげていくことになります。例えばもう一つ検索フィルターを追加する場合は「(|(検索フィルター1)(検索フィルター2)(検索フィルター3))」のようになります。Lispのorと同じようなものです。
LDAPはディレクトリサービスを提供するためのプロトコルです。あらゆる問題を解決できる魔法の道具ではありません。ディレクトリサービスというのはデータベースが提供する機能と似ているために、データベースでやるべきことまでディレクトリサービスでやろうとしてしまいがちになります。
リレーショナルデータベースでは、大量の更新をとりあつかうことができます。またトランザクションの概念もあります。SQLを使った真にリレーショナルな処理もおこなうことができます。LDAPにはこのような仕組はありません。
LDAPもファイルシステムもディレクトリ構造をもっていますが、LDAPには巨大なオブジェクトを扱うのは苦手ですし、そもそも検索用のシステムですから書きこみ処理はそれほど速くはありません。またエントリ内のデータをバイト単位でアクセスしたりすることもできませんし、ロックの概念もありません。
DNSはドメイン名を扱うのに最適化されています。そもそもLDAP自身もサーバに接続する時などにDNSを見ています。
それではLDAPでできることは何でしょうか? LDAPはコンピュータシステムにおける様々なディレクトリサービスを統一した方法で提供することができます。そのため様々なアプリケーションからLDAPという共通のインターフェースを使ってネットワークユーザやリソースの位置を特定したり、それらを管理したり、認証したりすることができます。LDAPの提供するディレクトリを使って誰が何にアクセスしていいかを許可したり拒否したりすることができるわけです。LDAPは認証のためのデータベースとして使うことができるのです。様々なリソース、アプリケーション、サービスに対するアクセスを制御するのにLDAPを使うことができます。 LDAPは汎用のディレクトリシステムなので、公開鍵暗号インフラストラクチャー(PKI)においてもLDAPが使われています。