以编程方式将 IIS 应用程序池标识“用户”分配给组

Posted

技术标签:

【中文标题】以编程方式将 IIS 应用程序池标识“用户”分配给组【英文标题】:Programmatically assigning IIS Application Pool Identity "users" to Groups 【发布时间】:2013-01-20 16:44:39 【问题描述】:

问题:当创建新的 IIS 应用程序池并将其设置为使用应用程序池标识获取权限时,我不确定如何将这些标识添加到用户组,例如管理员或性能计数器用户。

背景:我目前正在编写一个 C#.NET 库,它使用 Microsoft.Web.Administration 来执行以下操作:

检测是否安装了 IIS 7.x,如果安装了,安装了哪些组件。 安装或升级 IIS 7.x 到提供的所需组件列表。 通过 IIS 创建/管理一个或多个网站。 自动为每个网站创建/管理一个应用程序池

上下文是,可执行安装程序将使用此库在 Windows Server 操作系统上提供 Web 服务器和网站/服务的自动部署,作为更大软件部署的一部分。到目前为止,上述所有内容都已实现、测试并且(大部分)可以正常工作,除了需要在应用程序池/网站创建时执行的一些权限的自动化。

在我安装新网站的方法中,我创建了一个新的应用程序池并强制它使用应用程序池标识:

static public void InstallSite(string name, string path, int port)

    Site site;
    var appPoolName = ApplicationPoolBaseName + name;

    using (var iisManager = new ServerManager())
    
        // Set up a custom application pool for any site we run.
        if (!iisManager.ApplicationPools.Any(pool => pool.Name.Equals(appPoolName)))
        
            iisManager.ApplicationPools.Add(appPoolName);
            iisManager.ApplicationPools[appPoolName].ManagedRuntimeVersion = "v4.0";
        
        iisManager.CommitChanges();
    

    // ... other code here ('site' gets initialized) ...

    using (var iisManager = new ServerManager())
    
        // Set anonymous auth appropriately
        var config = iisManager.GetWebConfiguration(site.Name);
        var auth = config.GetSection("system.web/authentication");
        auth.SetMetadata("mode", "Windows");
        var authSection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication");
        authSection.SetAttributeValue("enabled", true);
        authSection.SetAttributeValue("userName", string.Empty); // Forces the use of the Pool's Identity.
        authSection = config.GetSection("system.webServer/security/authentication/basicAuthentication");
        authSection.SetAttributeValue("enabled", false);
        authSection = config.GetSection("system.webServer/security/authentication/digestAuthentication");
        authSection.SetAttributeValue("enabled", false);
        authSection = config.GetSection("system.webServer/security/authentication/windowsAuthentication");
        authSection.SetAttributeValue("enabled", false);

        iisManager.CommitChanges();
    

    // ... other code here ...

据我了解,这将是最佳安全做法,然后我会添加对特定网站的权限,以实现最低系统访问权限以外的任何内容。此过程的一部分是将这些应用程序池身份添加到用户组,例如管理员或性能监视器用户。这就是出现并发症的地方。

现在,作为documented elsewhere,每个应用程序池标识都以IIS AppPool\\<pool_name> 的格式存在,但是这个虚假用户没有通过普通的GUI 用户管理控件列出,并且似乎无法通过@ 等库访问987654326@ 关注this example on SO。此外,有关应用程序池标识的其他问题似乎与 referencing it from within a child website 有关,而不是来自安装上下文。

那么,有谁知道正确的方法是什么

a) 以编程方式引用和访问应用程序池标识。 b) 通过添加用户组来授予应用程序池身份权限。

【问题讨论】:

【参考方案1】:

感谢您写得很好的问题。这正是我昨晚试图解决的问题,它给了我足够的继续,我终于能够拼凑出一个只使用托管代码的答案。我发现让框架找到虚拟用户并与之合作的三个步骤:

使用new System.Security.Principal.NTAccount(@"IIS APPPOOL\<appPoolName>") 获取帐户的句柄。 使用.Translate(typeof (System.Security.Principal.SecurityIdentifier)) 将其转换为 SID 了解Principal.FindByIdentity() 将 SID 视为一个组,而不是一个用户

一个最终的工作程序(我的测试是Windows Server 2012)如下:

using System;
using System.DirectoryServices.AccountManagement;

namespace WebAdminTest

    internal class Program
    
        private static void Main(string[] args)
        
            var user = new System.Security.Principal.NTAccount(@"IIS APPPOOL\10e6c294-9836-44a9-af54-207385846ebf");
            var sid = user.Translate(typeof (System.Security.Principal.SecurityIdentifier));

            var ctx = new PrincipalContext(ContextType.Machine);

            // This is weird - the user SID resolves to a group prinicpal, but it works that way.
            var appPoolIdentityGroupPrincipal = GroupPrincipal.FindByIdentity(ctx, IdentityType.Sid, sid.Value);

            Console.WriteLine(appPoolIdentityGroupPrincipal.Name);
            Console.WriteLine(appPoolIdentityGroupPrincipal.DisplayName);

            GroupPrincipal targetGroupPrincipal = GroupPrincipal.FindByIdentity(ctx, "Performance Monitor Users");

            // Making appPoolIdentity "group" a member of the "Performance Monitor Users Group"
            targetGroupPrincipal.Members.Add(appPoolIdentityGroupPrincipal);
            targetGroupPrincipal.Save();

            Console.WriteLine("DONE!");
            Console.ReadKey();
        
    

【讨论】:

我将尝试将我的解决方案换成这个,以重现结果。这看起来很有希望,感谢分享!【参考方案2】:

一个解决方案比我预期的要早出现,尽管它不是我喜欢的那个。对于任何感兴趣的人,this pinvoke page 上还有几个附加选项。托管解决方案对我不起作用,但使用 DllImport 的示例有效。我最终调整示例以处理基于将枚举映射到 SID 字符串的任意组,并包括另一个 DllImport:

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool ConvertStringSidToSid(
    string StringSid,
    out IntPtr ptrSid);

修改后的(工作)函数如下所示:

static public bool AddUserToGroup(string user, UserGroup group)

    var name = new StringBuilder(512);
    var nameSize = (uint)name.Capacity;
    var refDomainName = new StringBuilder(512);
    var refDomainNameSize = (uint)refDomainName.Capacity;
    var sid = new IntPtr();
    switch (group)
    
        case UserGroup.PerformanceMonitorUsers:
            ConvertStringSidToSid("S-1-5-32-558", out sid);
            break;
        case UserGroup.Administrators:
            ConvertStringSidToSid("S-1-5-32-544", out sid);
            break;
        // Add additional Group/cases here.
    

    // Find the user and populate our local variables.
    SID_NAME_USE sidType;
    if (!LookupAccountSid(null, sid, name, ref nameSize,
        refDomainName, ref refDomainNameSize, out sidType))
        return false;

    LOCALGROUP_MEMBERS_INFO_3 info;
    info.Domain = user;

    // Add the user to the group.
    var val = NetLocalGroupAddMembers(null, name.ToString(), 3, ref info, 1);

    // If the user is in the group, success!
    return val.Equals(SUCCESS) || val.Equals(ERROR_MEMBER_IN_ALIAS);

希望这会引起其他人的兴趣,我仍然想知道是否有人遇到过有效的、完全托管的解决方案。

【讨论】:

以上是关于以编程方式将 IIS 应用程序池标识“用户”分配给组的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式获取应用程序池标识

在 IIS 10 / Windows Server 2019 中以编程方式回收应用程序池

IIS7.5 用 IIS AppPool应用程序池名 做账号 将各站点权限分开

使用 WIX 将不同的 IIS 应用程序池分配给 Web 服务

当它设置为 ApplicationPoolIdentity 时,如何设置 IIS 应用程序池标识用户区域设置

以编程方式将视网膜照片分配给 UIButton