LDAPによるApacheの認証と認可

 ネットワーク管理者は、しばしばLDAP(Lightweight Directory Access Protocol)を使って集中型ディレクトリサーバの実装を行う。LDAPはApacheでのユーザ認証にも使用される。オープンソースのLDAPソリューションとしてよく知られているのはOpenLDAPRed Hat Directory Serverの2つであるが、ApacheのマニュアルによればNovell LDAPとiPlanet Directory Serverもサポートされている。本稿はOpenLDAPを対象としたものであるが、ここで紹介する考え方と例はほかのものにも当てはまるはずである。

 LDAPはITU-T X.500ディレクトリの仕様を簡素化したバージョンとして設計された。デフォルトのスキーマセットには、/etc/passwdおよび/etc/groupといった従来のLinuxファイルシステムやSunのNIS(Network Information System)に見られる情報のすべてが含まれている。このスキーム群は柔軟性を備えているため、利用者統計情報を追加するために拡張されたり、特定のアプリケーション向けにカスタマイズされたりすることが少なくない。

 以下に、LDIF(LDAP Data Interchange Format)に記述される一般的なLDAPのユーザレコードの例を示す。

dn: uid=keithw,ou=People,dc=company,dc=com
uid: keithw
cn: Keith Winston
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword: {crypt}$1$M/PZEwdp$KHjSay8JILX01YAHxjfc91
shadowLastChange: 13402
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 2741
gidNumber: 420
homeDirectory: /home/keithw
gecos: Keith Winston

 LDAPデータに対するクエリには、OpenLDAPの標準ユーティリティの1つであるコマンドラインプログラムldapsearchなど、数々のツールが使える。初めての人には、LDAPの用語や構文が難しく感じられるかもしれない。だが、LDAP検索の構文をしっかりと学んでおけば、あとになって標準でない属性を用いた高度なポリシーを作り上げるときに役立つはずである。

Apache 2.2の設定

 Apacheは、少なくともバージョン1.3からLDAPに対応している。ただし、以前からmod_auth_ldapを使用していた場合は、バージョン2.2になって付属の認証および認可モジュールがリファクタリングされているので注意が必要がある。最新のLDAPモジュールのロードは以下のディレクティブによって行われる。通常、これらはhttpd.confファイル内に記述する。

LoadModule ldap_module /path/to/mod_ldap.so
LoadModule authnz_ldap_module /path/to/mod_authnz_ldap.so

 モジュールのロードが済むと、特定の属性に関するクエリをディレクトリに対して実行することで、アクセス制御が行えるようになる。ApacheでLDAPサーバを指定する重要なディレクティブがAuthLDAPUrlである。一般的なAuthLDAPUrlディレクティブの記述は次のようになる。

AuthLDAPUrl ldap://ldap.company.com/ou=People,dc=company,dc=com?uid

 ここではLDAPサーバ、ベースの識別名(DN:Distinguished Name)、検索に用いる属性(通常は組織単位People内のUid)が定義されている。複雑なポリシーでは、検索フィルタの追加が必要になる可能性がある。

 以降のセクションでは、一般的なポリシーを実施するディレクティブの実例を紹介していく。それぞれのディレクティブセットはApacheのアクセス制御設定ファイル.htaccessに記述すればよい。

任意の正当なユーザ

 以下のディレクティブセットは、LDAPディレクトリ内の正当な全ユーザに対し、カレントディレクトリへのアクセスを許可するものである。これにより、ApacheはブラウザにユーザIDとパスワードを要求し、その結果をディレクトリと照合することになる。Apache Basic Authenticationに詳しい人であれば、新たに覚えるべきディレクティブはわずか数個しかない。

Order deny,allow
Deny from All
AuthName "Company.com Intranet"
AuthType Basic
AuthBasicProvider ldap
AuthzLDAPAuthoritative off
AuthLDAPUrl ldap://ldap.company.com/ou=People,dc=company,dc=com?uid
Require valid-user
Satisfy any

 AuthBasicProvider ldapが必要なのは、ローカルファイルではなくLDAPディレクトリを対象としたクエリであることをApacheに知らせるためである。

 AuthzLDAPAuthoritative offは必ず明示的に設定する必要がある。デフォルトの設定は“on”だが、そのままだと正当なユーザの認証ができない。このディレクティブで注意すべき点は、Require ldap-userなどその他のポリシーでは“on”にする必要があることである。またこの値をoffにすることで、ほかの認証手法をLDAPと組み合わせて使用することも可能になる。

 Satisfy anyというディレクティブは、厳密にいえばこの場合は必要ない。1つの条件しかテストしていないからである。

ユーザのリスト

 次のディレクティブセットでは、Require ldap-userディレクティブに記されているユーザに対してカレントディレクトリへのアクセスを許可する。

Order deny,allow
Deny from All
AuthName "Company.com Intranet"
AuthType Basic
AuthBasicProvider ldap
AuthzLDAPAuthoritative on
AuthLDAPUrl ldap://ldap.company.com/ou=People,dc=company,dc=com?uid
Require ldap-user keithw joeuser
Satisfy any

 AuthzLDAPAuthoritative onはデフォルトの設定なので省略可能であるが、設定内容を明らかにするために残してある。

 AuthLDAPUrlの設定がこれまでと変わっていない点に注意してもらいたい。前出の例と同じく、Uidが一致するものがディレクトリから検索される。

グループのメンバ

 次に紹介するディレクティブセットは、Require ldap-groupディレクティブで指定されているグループのプライマリまたはセカンダリメンバであるユーザに対し、カレントディレクトリへのアクセスを許可する。

 このグループの設定は、NISから変換されたディレクトリのスキーマ設計ゆえに非常に難解かもしれない(私の場合もそうだった)。初めに示したLDIFレコードを見返してみるとgidNumber属性が420になっていて、これと同じ値が私のディレクトリの“infosys”グループに割り当てられているのがわかる。これはユーザのプライマリグループに相当する。ただし、各グループのLDAPエントリには、そのグループのセカンダリメンバのユーザだけがmemberUid属性として記される。以下に、グループレコードの一部を示す。

dn: cn=infosys,ou=Group,dc=company,dc=com
objectClass: posixGroup
gidNumber: 420
memberUid: user1
memberUid: user2
memberUid: user3
...

 ここでもう1つのテストとして、グループのプライマリユーザを取り出すためのRequire ldap-attributeが必要になる。グループ自体にはプライマリユーザが記されていないからである。結果、グループ認証のためのApacheディレクティブは以下のようになる。

Order deny,allow
Deny from All
AuthName "Company.com Intranet"
AuthType Basic
AuthBasicProvider ldap
AuthzLDAPAuthoritative on
AuthLDAPUrl ldap://ldap.company.com/ou=People,dc=company,dc=com?uid
AuthLDAPGroupAttribute memberUid
AuthLDAPGroupAttributeIsDN off
Require ldap-group cn=infosys,ou=Group,dc=company,dc=com
Require ldap-attribute gidNumber=420
Satisfy any

 AuthzLDAPAuthoritative onはデフォルトの設定なので省略可能であるが、やはり設定内容を明示するために残してある。

 AuthLDAPGroupAttribute memberUidは、LDAPグループレコードのどの属性をUidとマッチングさせるか(ここではmemberUid)を指定する。グループレコードには、グループの(プライマリでない)メンバ用にmemberUid属性が1つずつ含まれている。

 AuthLDAPGroupAttributeIsDN offにより、Apacheはグループメンバシップのチェックにクライアントの識別名を使用することになる。それ以外の場合にはユーザ名が使用される。私のOpenLDAPディレクトリでNISから移行したのはユーザ名だけであり、この設定はデフォルトで“on”になっているので、“off”にする必要があった。LDAPディレクトリは全体の識別名を格納している可能性があるので、場合によっては自分のディレクトリに基づいてこの設定を変更する必要がある。

 Require ldap-groupの行は、“infosys”グループのメンバにアクセスを許可するものである。グループが複数になる場合は、グループごとにディレクティブを追加すればよい。

 Require ldap-attribute gidNumber=420は、処理の対象がグループ420、つまり“infosys”グループのプライマリユーザであることを示している。この条件がなければ、プライマリユーザのアクセスは拒否される。また、複数のグループを扱う場合は、グループごとにディレクティブを用意する。

 ここではSatisfy anyディレクティブが必要である。複数の条件をテストして1つでも条件を満たせばアクセスを許可するようにしたいからである。

ユーザとグループの組み合わせ

 最後に、ユーザとグループのディレクティブをまとめたものを示す。ただし、ひとつひとつのディレクティブに新たなものはない。

Order deny,allow
Deny from All
AuthName "Company.com Intranet"
AuthType Basic
AuthBasicProvider ldap
AuthzLDAPAuthoritative on
AuthLDAPUrl ldap://ldap.company.com/ou=People,dc=company,dc=com?uid
AuthLDAPGroupAttribute memberUid
AuthLDAPGroupAttributeIsDN off
Require ldap-group cn=infosys,ou=Group,dc=company,dc=com
Require ldap-attribute gidNumber=420
Require ldap-user keithw joeuser
Satisfy any

デバッグとデプロイ

 LDAP認証のテストをWebブラウザから行うと、ストレスが溜まることがある。アクセスが許可されたか否かしかわからないからである。Webブラウザでは、認証がうまくいかなかった際、その理由のフィードバックが得られない。認証プロセスの各段階の詳細情報を得るには、Apacheのhttpd.confにLogLevel debugオプションを設定する。このデバッグ設定をしておけば、LDAPサーバへの接続ステータス、要求された属性および値、返ってきた値、条件の一致または不一致の理由といった情報がApacheによって記録される。これらはLDAPによるアクセス制御の調整にあたって貴重な情報になるだろう。

Linux.com 原文