LDAPによるApacheの認証と認可
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によるアクセス制御の調整にあたって貴重な情報になるだろう。