この手の質問って、いろんな掲示板やフォーラムで回答されていますが、
綺麗にまとめているサイトが少ないなと思ったので、今更感バリバリですが、今後のために情報をまとめておこうと思います。
まず、この記事を参考される方は、下記の環境&現象に陥った方を前提とさせて頂きます。
1. Windows Server 2003 を使用している
2. IIS 6 を使用している ( IIS7 用については、次回エントリでご紹介します。GUI操作でだいたいできるレベルなので)
3. ASP.NET 2.0 を使用している
4. Webサーバーとは別の場所に、ASP.NETで処理したファイル(画像やCSVファイル)を
別サーバー(以下、外部ファイルサーバーとする)に配置する予定のある方(ただし、FTP転送ではない)
5.Webサーバーと外部ファイルサーバーのネットワークが、同じネットワークセグメントにあることを前提とします。
また、下記の例外が出てハマってしまった方も対象と致します。
1.Webサーバー → 外部ファイルサーバー に、ネットワークドライブ(例:Z:\ 、S:\ 等)を経由してI/O した際、
「System.IO.DirectorynotfoundException」例外が発生してしまった方
2.Webサーバー → 外部ファイルサーバー に、ネットワークドライブまたはUNCパス書式を使って、I/Oした際、
「System.IO.IOException」例外が発生してしまった方
それでは、上記のような問題にハマった方は、是非、下記をご覧ください。
A. ASP.NETの「セキュリティ アーキテクチャ」を軽くで良いので覚えましょう
今回のように、外部サーバーにファイルのI/Oを行うケースに限った場合は、
下記のようなイメージでIISとASP.NETは、ファイル等のリソース類にアクセスします。
(ちょっと大雑把なな図ですいません。厳密に書くともっと細かく書けるんですが)
( クリックすると、拡大表示できます )
用語が分からない方もいると思うので、少しだけ解説します。
・ 「アクセストークン」とは、アクセスするための許可情報みたいなものだと思って下さい。
・ 「匿名アクセス」 と 「認証済みアクセス」は、IISで設定した「認証方法」のことです。
・ 「フォーム認証」は、ASP.NETなどで作成した独自認証ページ等から、認証する仕組みのことです。
今回のケースの場合、「⑦目的ファイル等の各種リソース類」は、「外部ファイルサーバーにあるファイル」ということになるので、
「ASP.NET ワーカープロセスが、アクセスできるリソースでなければならない」ということが上記の図でわかります。
しかし、ASP.NETの既定では、「ASPNET」または「NETWORK_SERVICE」(Windows 2003 Server の場合)
というWindows ID と、アクセストークンで実行されているため、「ASPNET」または「NETWORK_SERVICE」が
アクセスできるリソースでなければ、当然アクセスすることができません。
この手の話は、他のWebサイトや掲示板、一般的な書籍でも紹介されているので、有名な話ですね。
そうなると、外部ファイルサーバーにファイルを読み書きできないの?諦めなきゃいけないの?
という話になりますが、ASP.NETワーカープロセス(aspnet_wp.exe)に要求を出したWindows IDが、
外部ファイルサーバーにアクセスできる権限(Windows ID) と同じID、もしくは同種のアクセストークンであれば、
この問題を解決することができます。
このように、特定のファイルやディレクトリ等のリソースにアクセスできるアクセストークンを持つ特定のWindows ID等を使って、
ASP.NETワーカープロセスがリソースにアクセスできるよう定義することを 「偽装」と呼んでいます。
B. 偽装の設定方法
web.config の 「system.web」タグ内に、「identity」タグを指定するようにすると、偽装できるようになります。
<identity impersonate="true" />
しかし、この設定だと偽装したWindows IDが、
IIS が匿名アクセスを使ってない場合: 認証に使用したWindowsユーザに偽装
IISが匿名アクセスを使っている場合:「IUSR_<マシン名>」ユーザで偽装
するので、相手先である 外部ファイルサーバーが、必ずしも偽装したWindows IDに対して権限を持っているとは限りません。
そのため、外部ファイルサーバーにもアクセスができ、ASP.NETアプリケーションも実行できる固定のWindows ID
(または、同種のアクセストークンを持つ別のWindows IDでも良い) を明示的にidentity タグに指定します。
<identity impersonate="true" userName="kero-mio\Administrator" password="password_1031" />
<!--
userName は、(マシン名 or ADドメイン名 or 同一ワークグループに存在する場合はワークグループ名を指定しない)\(偽装に使用するWindows ID) を指定する
password は、userName に紐づくパスワードを指定する
TODO:userName やパスワードは、レジストリに格納し、レジストリから取得することもできます。
-->
詳しい偽装設定の詳細については、MSDNのサイトをご覧ください。
http://msdn.microsoft.com/ja-jp/library/xh507fc5(VS.80).aspx
C. ネットワークドライブ経由ではなく、UNC PATHで外部ファイルサーバーに接続する
ASP.NETアプリケーションで「System.IO」名前空間にあるクラスやメソッドを何も気にせず使用し、
さらに対象ファイルの置き場がネットワークドライブ(例:Z:\ や S:\ 等)だった場合、「System.IO.DirectorynotfoundException」例外が発生します。
System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
System.IO.File.WriteAllBytes(String path, Byte[] bytes)
「web.configで偽装設定したのに、なんで?」と思われると思いますが、実は、System.IO名前空間にあるクラスやメソッドは、
ネットワークドライブに対するI/Oに対応していないのが原因で、「System.IO.DirectorynotfoundException」が発生しています。
可能であれば、UNCパス形式(例:\\servername\directory\) で外部ファイルサーバーにアクセスして頂きたいのですが、
業務の事情や保守の都合でUNCパス形式が嫌というお客様がもしいらっしゃるのであれば、「WNetGetConnection」というWinAPIが
あるので、「WNetGetConnection」でネットワークドライブに接続した後、「System.IO」名前空間にあるクラスやメソッドを使って
ファイルやディレクトリ操作することもできます。
(そういえば、「WNetGetConnection」って、しばらく使ってないなぁ・・・)
WNetGetConnection の使い方 (C++のリファレンスですが): http://msdn.microsoft.com/ja-jp/library/cc447035.aspx
ただし、ASP.NETアプリケーションから、「WNetGetConnection」APIを使ってネットワークドライブに接続するのは、
セキュリティ的にもアーキ的にもお勧めしかねるので、可能であれば、
「「UNCパス形式」で外部ファイルサーバーにアクセスする」作りにしておいてください。
D. ProcessModel と identity の違い
今回のように外部ファイルサーバーアクセスエラーが発生すると、意味がわからないまま、
machine.config で定義されている ProcessModel と 、web.config に設定したidentity、
両者に同じuserName, Password を設定し、とりあえず動くからOK!にしている人も結構いらっしゃるんじゃないかと思います。
MSDNでの書き方がASP.NETに不慣れな方からみると、混乱しやすいのかなぁと思い、大雑把にまとめてみました。
| |
概要 |
主な定義場所 |
対象 |
| ProcessModel |
マシン単位(Webサーバー)で、ASP.NET を動かすための 必要最小限の権限を設定するのに使用する。 既定では、ASPNET、または、NETWORK_SERVICE (Windows 2003 の場合)のWindows ID で実行されるように 作られていますが、特にuserName, Password の設定は不要 |
machine.config |
ASP.NET ワーカープロセス(Aspnet_wp.exe), ASP.NET State Server(aspnet_state.exe) ※早い話がマシン内に設置されている すべてのWebアプリケーション |
| Identity |
Webアプリケーション単位で、ASP.NET を動かすための 権限を設定するのに使用する。Web.config に identity の 設定がない場合はmachine.config にある ProcessModel の設定内容がそのまま使用される |
web.config |
配置しているWebアプリケーション内 |
ProcessModelには、ASP.NET が必要最低限の動作が行えるような弱い権限を持つWindows IDを設定しておき、、
Identity には、ProcessModel で設定した以上の権限を持つWindows IDを設定しておくと良いでしょう。
ただし、他のWebサイトにもまったく同じ偽装を施したい場合は、ProcessModelでuserNameとPasswordを設定するのも良いかなと思います。
そして、今回のように、外部ファイルサーバーにアクセスするだけのものであれば、「Identiy」に偽装設定しておくのが良いかなと思います。
もし、ProcessModel と Identity 両方に同じuserName, password を設定しなければ動かなかったという方が
いらっしゃいましたら、けろ-みお までお知らせ下さい。
※記事はある程度、裏を取ってからUPしているつもりですが、
文面ミスによって、嘘の情報を流してしまっている可能性もあります。その場合は、Blogコメント欄でご指摘下さい。