何故SSL経由かと言うと、ActiveDirectoryのパスワード情報をLDAP通信で変更したい場合、SSL経由の接続じゃないとサーバーに怒られるからです.
※変更しないなら、SSL有効時においても平文で通信可能ではあります.
// 2008-04-18 WEBサーバーにCentOS 5.1を使用した場合のメモを追加.
今回の主な環境.
・ActiveDirectoryサーバー … Windows Server 2003(Std/Ent)
・WEBサーバー
Windows … Windows XP Professional(IIS 5.1)
Linux … CentOS 5.1(Apache2.2.3)
・PHP
Windows … Version 5.2.5
※php.iniの extension=php_ldap.dll のコメントアウトを解除
Linux … Version 5.1.6
尚、今回WEBサーバー代わりとなったXPは、テスト用ActiveDirectoryには参加していません.
1. ActiveDirectoryのインストール.
普通にインストールします.
以上.
…と思ったけどもう少し書きます.
「スタート」→「サーバーの役割管理」を選択.
「サーバーの役割管理」ウィンドウで「役割を追加または削除する」を選択.
「サーバーの構成ウィザード」ダイアログ「サーバーの役割」で「ドメインコントローラ(Active Directory)」を選択.
後はお好きな様に設定.
必要に応じてDNSサーバーも自動的にインストールしてくれます.
結局書いても書かなくても同じだったか orz
2. SSL証明書をドメインコントローラに設定.
色々方法はある様ですが、今回は手っ取り早くWindowsサーバーの「証明書サービス」を利用します.
※環境により、インストール中にOSのCDを要求されます.
※ここでの設定方法は独学であるため、ほぼ100%の確率で誤りがあるかと思われます.
「スタート」→「コントロールパネル」→「プログラムの追加と削除」を選択.
「プログラムの追加と削除」ダイアログで、左側の「Windowsコンポーネントの追加と削除(A)」ボタンを選択.
「Windows コンポーネント ウィザード」ダイアログの一覧から、「証明書サービス」にチェックをつけて「次へ」→「完了」を選択.
「CAの種類」の指定で、「エンタープライズのルートCA」を選択します.
「スタンドアロンのルートCA」でも行けるかもしれませんが未確認.
時間があれば後日入れなおしてみるかもしれません.
※面倒になったのでここから画像なしで行きます orz
「CA識別情報」の指定で、共通名に適当(適切)な名称を指定します.
ここで指定した名称は、証明書の名称であったり、識別名(ダイアログの下の方)で参照されます.
「証明書データベース」の指定では構成情報の保存場所を指定しますが、後からここに出力されたセキュリティ証明書(*.crt)を参照する事になるので、分かりやすい場所がいいかと思います※1.
インストール後、「スタート」→「コントロールパネル」→「管理ツール」→「証明機関」と言う項目が増えます.
基本的には、この段階でActiveDirectoryのSSL通信がサポートされている…はずです.
続いて実際に通信の為のセキュリティ設定を行います.
「スタート」→「コントロールパネル」→「管理ツール」→「既定のドメイン セキュリティの設定」を選択.
「既定のドメイン セキュリティの設定」ウィンドウの左ペイン(ツリー)から、「セキュリティの設定」→「公開キーのポリシー」→「信頼されたルート証明機関」を右クリックしてプロパティを開きます.
ここで「登録された Active Directory およびユーザープリンシパル名(UPN)の名前制約条件を満たす証明機関(D)」を選択しないと、SSLを有効にした状態ではPHPからユーザー情報の編集が出来ませんでした.
…私が他の設定を抜かしているだけかもしれませんけどw
// 2008-04-18 追記 ↑まったく関係ありませんでした orz
上記項目選択後、「OK」ボタンを選択してプロパティ画面を終了します.
再度「信頼されたルート証明機関」を右クリックし、「インポート」を選択します.
「証明書のインポート ウィザード」で「次へ」→ファイルの指定で、本文『※1』で指定したフォルダにある「****.crt」を指定→「次へ」→「次へ」(選択不可になってるはず)→「完了」を選択して、証明書をインポートします.
「スタート」→「ファイル名を指定して実行」で
gpupdate /Force
と入力し、ポリシーを最新の状態に更新します.【WEBサーバーがLinuxの場合のみ、以下を追加で実行】
「スタート」→「コントロールパネル」→「管理ツール」→「証明機関」を選択.
「証明機関」スナップインで、左ペインのツリーから目的のサーバーを探します…と言っても、多分ひとつだけしか無いと思いますけどw
目的のサーバーを右クリック→「プロパティ」→「全般」タブの「CA証明書」一覧で証明書(多分「証明書 #0」と言う様な名称)が選択されている事を確認して「証明書の表示」ボタン選択.
更に出てきたダイアログの「詳細」タブを選択→「ファイルにコピー」ボタン選択.
「証明書のエクスポート ウィザード」で「次へ」→「Base 64 encoded X509(CER)(S)」を選択して「次へ」→保存パスを指定して「次へ」→「完了」ボタンを選択して証明書をエクスポートします※2.
後でWEBサーバーにエクスポートしたCERファイルを渡す必要があるので、その点を考慮して保存パスを指定した方が良いかと思われます.
※画像が無いので説明が分かり難いかも…英語だけど→「LDAP over SSL - Modifying Active Directory with PHP」の方が分かり易いかもしれません.
これでActiveDirectoryサーバー側の設定は終了です(本当かどうかは知りませんがw).
3. SSL証明書をWEBサーバーに設定.
次に、ActiveDirectoryサーバー(本当は証明書サーバーですが)で生成した証明書をWEBサーバー機に設定します.
【Windowsの場合】
「スタート」→「ファイル名を指定して実行」で
mmc
と入力し、管理用コンソール(GUIです)を起動します.スナップインとかMMCとか言われているアレです.
コンソールのメニューから「ファイル」→「スナップインの追加と削除」を選択し、「スナップインの追加と削除」ダイアログを表示します.
上記ダイアログの「追加」ボタン(ダイアログ下部)を選択し、「スタンドアロン スナップインの追加」ダイアログを表示します.
スナップインの一覧が表示されているので、一覧の下の方にある「証明書」を選択状態にして「追加」ボタンを選択します.
更にもうひとつ「証明書スナップイン」と言うダイアログが表示されますので、「コンピュータアカウント」を選択し「次へ」.
管理対象に「ローカルコンピュータ(このコンソールを実行しているコンピュータ)」が選択されている事を確認し、「完了」を選択します.
「スタンドアロン スナップインの追加」ダイアログの「閉じる」ボタンを選択します.
「スナップインの追加と削除」ダイアログの空だった一覧に「証明書」が追加されている事を確認して、「OK」ボタンを選択します.
証明書の分類一覧がツリー表示されたコンソールが表示されるので、「信頼されたルート証明機関」を右クリック→「すべてのタスク」→「インポート」を選択します.
本文『※1』で出力した「***.crt」ファイルを選択→「次へ」→「証明書をすべて次のストアに配置する」が選択されている事を確認して「次へ」→「完了」を選択し、証明書をインポートします.
あー長い orz
この段階で、正常に設定が完了しているか確認するには、Windows Server 2003のCDに入っている「ldp.exe」を使うのが良いかと思います.
インストーラの保存パスは
[CD]:\SUPPORT\TOOLS\SUPTOOLS.exe
です.
このインストーラでインストール後、
C:\Program Files\Support Tools\ldp.exe
を起動します.
使い方は…面倒なのでヘルプ参照でw
ちなみに、既にPHPでLDAP接続を試してて「つながんねぇ~!!」という状況の場合は、IISを再起動させる必要があります(コード上で接続プールをクリア出来るのかもしれませんが、私は知りませんので再起動 orz).
「スタート」→「ファイル名を指定して実行」で「cmd」と入力してEnter.
出てきたコマンドプロンプトで
C:\> net stop w3svc
C:\> net start w3svc
C:\> net start w3svc
【Linuxの場合】
OpenSSLの力を借りてCERをPEMにコンバートし、ldap.confに鍵情報を追加設定します.
本文『※2』で出力したCERファイルが「export.cer」と言う名称であったと仮定した場合のコマンドは次の通りです.
[??@centos]# openssl x509 -in export.cer -out export.pem
[??@centos]# cp export.pem /path to openldap/cacerts/export.pem
[??@centos]# vi /path to ldap.conf
# 以下、変更箇所
host lightmaterial.example.com
base dc=lightmaterial,dc=example,dc=com
uri ldaps://lightmaterial.example.com
# 以下、追加文言
TLS_CACERT /path to openldap/cacerts/export.pem
TLS_REQCERT never
# 保存
[??@centos]# /etc/init.d/httpd restart
[??@centos]# cp export.pem /path to openldap/cacerts/export.pem
[??@centos]# vi /path to ldap.conf
# 以下、変更箇所
host lightmaterial.example.com
base dc=lightmaterial,dc=example,dc=com
uri ldaps://lightmaterial.example.com
# 以下、追加文言
TLS_CACERT /path to openldap/cacerts/export.pem
TLS_REQCERT never
# 保存
[??@centos]# /etc/init.d/httpd restart
これでWEBサーバー側の設定は終了.
後はコードを書くだけだ.
4. PHPからSSL経由でActiveDirectoryに接続してみる.
ここでは、ActiveDirectoryに以下のユーザー情報が登録されているものとします.
・sAMAccountName=t_hokkaido
・cn=北海道 太郎 T.H
sn … 北海道
givenName … 太郎
initial … T.H
・SELFオブジェクトのアクセス許可
パスワードのリセット … ON
パスワードの変更 … ON
尚、上記のアクセス許可を確認するには「Active Directory ユーザーとコンピュータ」右ペインの何も無い所で右クリック→「表示」→「拡張機能」を選択した状態で、各ユーザーのプロパティを開き、「セキュリティ」タブのユーザー一覧から「SELF」を選択する事で確認出来ます.
別に「パスワードの変更」だけでも良さそうですが、「パスワードのリセット」も選択しないとパスワード変更出来ませんでした…またバカなミス(文末追記参照)をしてるのかもしれませんが orz
ActiveDirectoryの情報は次の通りとします.
・DN … OU=Sapporo,DC=lightmaterial,DC=example,DC=com
こっからはさっさと行きます.
// Windows環境下では、↓の環境設定をしなければ接続時にエラーが出ます.
putenv('LDAPTLS_REQCERT=never')
or die("couldn't setup reqcert-environment...");
// LDAPSを用いてドメインを指定しつつ接続.
// ※ActiveDirectoryがSSL接続時に使用する標準ポートは「636」です.
$ldapConn = ldap_connect('ldaps://lightmaterial.example.com', 636)
or die("couldn't connect to LDAP Server...");
// オプション設定.
// すべて設定しないと、Windows環境では上手くつながらない場合があります.
ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3)
or die("couldn't set protocol version...");
ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0)
or die("couldn't set referrals...");
// バインド.
if ($ldapConn) {
ldap_bind($ldapConn, 't_hokkaido@lightmaterial.example.com', 'this_is_password')
or die("ldap_binding get an error.<br>reasons... " . ldap_error($ldapConn) . "<br>");
echo "ldap_binding is success.<br>";
// ログインユーザーの情報を検索してみる.
if (! ($result = ldap_search($ldapConn,
'OU=Sapporo,DC=lightmaterial,DC=example,DC=com',
'(sAMAccountName=t_hokkaido)',
array('cn', 'sn', 'givenName', 'displayName', 'sAMAccountName')))) {
// ユーザー情報が見つかりません.
echo "user not found.";
} else {
// 今回はパスワードを変更してみる.
// パスワード文字列を加工してあるのは、渡す値がUnicode文字列である為.
// ※パスワード文字列は「"」(ダブルクォーテーション)で囲む必要があります.
$plainPasswd = '"this_is_new_password"';
$length = strlen($plainPasswd);
$unicodePasswd = '';
for ($position=0; $position<$length; $position++)
$unicodePasswd .= "{$plainPasswd{$position}}\000";
// ldap_modifyを使って実際に属性値を更新(ldap_mod_replaceでもOK).
// ちなみに、パスワード変更後も変更前のパスワードで認証出来てしまう場合は、
// 2回ldap_modifyしてあげるしか対処法は無さそうです.
// 多分ldap_unbindしてもログオフした事にならないのが原因だと思います.
ldap_modify($ldapConn,
ldap_get_dn($ldapConn, ldap_first_entry($ldapConn, $result)),
array("unicodePwd" => $unicodePasswd));
}
// バインド開放(クローズ).
ldap_unbind($ldapConn);
}
echo "fin.";
これまでの設定に間違いが無く、且つ私がボケていなければLDAPSで接続した上でパスワードを変更出来たはずです.putenv('LDAPTLS_REQCERT=never')
or die("couldn't setup reqcert-environment...");
// LDAPSを用いてドメインを指定しつつ接続.
// ※ActiveDirectoryがSSL接続時に使用する標準ポートは「636」です.
$ldapConn = ldap_connect('ldaps://lightmaterial.example.com', 636)
or die("couldn't connect to LDAP Server...");
// オプション設定.
// すべて設定しないと、Windows環境では上手くつながらない場合があります.
ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3)
or die("couldn't set protocol version...");
ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0)
or die("couldn't set referrals...");
// バインド.
if ($ldapConn) {
ldap_bind($ldapConn, 't_hokkaido@lightmaterial.example.com', 'this_is_password')
or die("ldap_binding get an error.<br>reasons... " . ldap_error($ldapConn) . "<br>");
echo "ldap_binding is success.<br>";
// ログインユーザーの情報を検索してみる.
if (! ($result = ldap_search($ldapConn,
'OU=Sapporo,DC=lightmaterial,DC=example,DC=com',
'(sAMAccountName=t_hokkaido)',
array('cn', 'sn', 'givenName', 'displayName', 'sAMAccountName')))) {
// ユーザー情報が見つかりません.
echo "user not found.";
} else {
// 今回はパスワードを変更してみる.
// パスワード文字列を加工してあるのは、渡す値がUnicode文字列である為.
// ※パスワード文字列は「"」(ダブルクォーテーション)で囲む必要があります.
$plainPasswd = '"this_is_new_password"';
$length = strlen($plainPasswd);
$unicodePasswd = '';
for ($position=0; $position<$length; $position++)
$unicodePasswd .= "{$plainPasswd{$position}}\000";
// ldap_modifyを使って実際に属性値を更新(ldap_mod_replaceでもOK).
// ちなみに、パスワード変更後も変更前のパスワードで認証出来てしまう場合は、
// 2回ldap_modifyしてあげるしか対処法は無さそうです.
// 多分ldap_unbindしてもログオフした事にならないのが原因だと思います.
ldap_modify($ldapConn,
ldap_get_dn($ldapConn, ldap_first_entry($ldapConn, $result)),
array("unicodePwd" => $unicodePasswd));
}
// バインド開放(クローズ).
ldap_unbind($ldapConn);
}
echo "fin.";
長かった…
またしばらく長い投稿は止めよう orz
今回は、かなり参照させてもらったサイトがありますので、一応↓に挙げておきます(100%自己メモ).
[参照サイト]
・Using Ldp.exe to Find Data in the Active Directory
・How To Enable Secure Socket Layer (SSL) Communication over LDAP for Windows 2000 Domain Controllers
・LDAP over SSL - Modifying Active Directory with PHP
・unicodepwd and ldifde
・PHPマニュアル LDAP関数
※↑このページの下の方の英語でかかれたnoteに情報がある.
注)この投稿…投稿してはミス発覚の連続で10回くらい修正してます orz
そろそろ本気で直書き止めるかな…
/**
* 2008-04-18 追記/修正.
* ・WEBサーバーにCentOS追加
* ・DNにbloggerと使っていたものをexampleに変更
* ・下記問題に対応
*/
はい、アホ過ぎます orz
投稿当初、認証ユーザーが「Account Operators」に属している必要がある…と書きましたが、これが何も変更出来なくなる一番の原因だった様です(本文を見直した時に邪魔なので、斜線ではなく該当する文言ごと削除しました).
【現象】
1. 「Account Operators」に属するユーザーで認証(ldap_bind)
2. 自身のエントリーを取得
3. パスワード変更→正常に処理完了
4. しばらく普通に属性の変更やパスワードを再変更出来る
5. 何かの拍子に何の属性も変更出来なくなる
【原因】
何も変更出来なくなった状態のユーザーを改めて確認すると、所属するグループに「Account Operators」が登録されているが、「セキュリティ」タブのグループから「Account Operators」が消えていた.
更に「SELF」のアクセス許可が「パスワード変更」のみONになっていた(それ以外はすべてOFF).
つまる所、「Account Operators」権限で自分自身のパスワードをむやみに変更すると、何か変な事になる、と言う事ですね!!
…全然つまってないじゃん orz
何でこんな事になるのか分かりませんが、また時間を作って原因追求と言うか解析してみたいと思います.
本当に無駄な時間過ごしたなぁ…
あ、直しついでに「WEBサーバーとしてLinux(CentOS)を使った場合」も追記しました.
0 件のコメント:
コメントを投稿