今日、IIS 周りの設定を行っている時に「お前 ASP.NET/IIS の MVP のくせに IIS のこと何も知らないのかよ!」と説教され、そういえば IIS の上っ面しか使っていないと思ったので、理解が中途半端だったアプリケーションプールと権限について調べました。
ちなみに ASP.NET/IIS となっているのは、ASP.NET の Expertise が IIS と統合されただけです。*1
環境の準備
開発用のマシンには IIS をインストールしていますが、何時でもぶっ壊し可能な環境が欲しかったので Azure に Windows Server 2012 R2 な環境を用意しました。
当然ながら IIS 8.5 なのですが、アプリケーションプールや権限に関しては IIS 7 から変わっていないので問題ないはずです。
アプリケーションプール
とりあえずアプリケーションプールを新しく作成してみることにします。
CLR 4.0 で統合パイプラインを利用する、普通のアプリケーションプールです。
アプリケーションプールを実際に使うためには Web サイトの設定で選択する必要があります。
そしてブラウザでアクセスしてワーカープロセスを起動させ、Process Explorer で詳細な情報を確認してみました。注目するのは Security タブですかね。
IIS のワーカープロセスを動かしているのは IIS AppPool\shibayan というユーザーであることが分かります。実際には使用しているアプリケーションプール名になります。
なので、当然ながら権限に関してはこのユーザーに対して設定する必要があるようです。TechNet で詳しい記事を発見したので紹介しておきます。
アプリケーション プール ID | Microsoft Learn
さっきのダイアログのリストボックスにビルトインの IIS_IUSRS というグループが載っています。名前から想像付くように、これは IIS のワーカープロセスのグループになっています。
なので、アプリケーションプール単位ではなく IIS のワーカープロセスという括りで扱うには、こっちのグループを使えば良いようですね。
IIS 7.0 での組み込みユーザーとグループ アカウントとは | Microsoft Learn
ビルトインユーザーとグループに関しても TechNet に記事を見つけたので紹介しておきます。セキュリティ向上のために NETWORK_SERVICE で動かすことをやめて、アプリケーションプール毎にユーザーを作成することで、権限の分離を図ったのが IIS 7 以降ということになりますね。*2
NTFS アクセス許可
Linux などの環境では chmod とかでアクセス権を設定しましたが、同じようなことが Windows でも当然可能です。しかし、Linux のそれに比べると Windows の NTFS アクセス許可はとても高機能で複雑です。
ひとまずアプリケーションのルート以下にファイルを書き出す簡単なサンプルを用意したので、これを何も設定していない IIS 上で動かしてみます。
public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { var path = Server.MapPath("~/App_Data/test.log"); File.WriteAllText(path, DateTime.Now.ToString()); } }
結果は App_Data を用意しているにも関わらず、権限が無いためエラーになりました。
エラーになったのは App_Data を手動で作成したので、適切な権限が設定されなかったのが原因です。ちなみに MSDeploy を利用して Visual Studio からデプロイした場合には、自動的に権限が設定されます。
ファイル システム ACL を介した IIS のコンテンツのセキュリティ保護 | Microsoft Learn
TechNet に ACL を設定する記事があったので、これもまた紹介しておきます。
話を戻して App_Data に対してアクセス許可を追加することにします。この時、アプリケーションプール ID か IIS_IUSRS グループに対して権限を設定すれば問題無いようです。
今回は IIS_IUSRS グループに対して書き込みと変更の権限を追加し、動作を確認出来ました。
ついでに IIS のワーカープロセスから新しく作成されたプロセスが、一体どのような扱いになるのか気になったので、以下のようなコードを用意して一緒に調べてみました。
public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { var process = Process.Start(new ProcessStartInfo("cmd.exe", @"/c echo foo > C:\inetpub\wwwroot\App_Data\sample.log") { CreateNoWindow = true, UseShellExecute = false }); process.WaitForExit(); } }
結果としては実行ユーザーは IIS_AppPool\shibayan となり、IIS_IUSRS に権限を与えた App_Data に対して書き込みをちゃんと行えました。
ファイルへの書き出しに失敗する場合には、ASP.NET の場合アプリケーションプールのユーザーか IIS_IUSRS グループに適切に権限を与えているかチェックすれば良さそうです。
例外:CGI について
ここまでは割とシンプルだったのですが、恐らく唯一の例外として CGI の存在が挙げられます。CGI の実行ユーザーはデフォルトだとアクセスしているユーザーになります。
createProcessAsUser
Optional Boolean attribute. Specifies whether a CGI process is created in the system context or in the context of the requesting user. The default value is true.
CGI <cgi> | Microsoft Learn
createProcessAsUser を false にすることでシステムのコンテキスト、恐らく IIS のユーザーになるので権限周りでの問題を避けることが出来ます。*3
ちなみに認証せずにアクセスすることを IIS では匿名認証と呼ぶので、システム的には匿名認証で認証済みのユーザーと言う、なんとも分かりにくい形になります。
createProcessAsUser が true の場合には、この IUSR ユーザーとして実行されるので権限周りでエラーになりやすいという話のようでした。