しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

IIS 7 以降でのアプリケーションプールと権限について調べた

今日、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 ユーザーとして実行されるので権限周りでエラーになりやすいという話のようでした。

*1:確か ASP.NET MVP に初めてなって数日後の出来事だった

*2:適切に設定することでサンドボックス化も出来る感じ

*3:Windows Azure Web サイトで Ruby を動かしてみた(CGI 編) - しばやん雑記