首页 文章

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

提问于
浏览
6

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

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

  • 检测是否安装了IIS 7.x,如果安装,则检测哪些组件 .

  • 将IIS 7.x安装或升级到所需组件的提供列表 .

  • 通过IIS创建/管理一个或多个网站 .

  • 每个网站自动创建/管理一个应用程序池

上下文是可执行安装程序将此库用于在Windows Server操作系统上自动部署Web服务器和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用户管理控件列出的,并且在跟随this example on SO时似乎无法通过诸如 System.DirectoryServices.AccountManagement 之类的库访问 . 此外,有关应用程序池标识的其他问题似乎与referencing it from within a child website有关,而不是与安装上下文有关 .

那么,有谁知道适当的方法是什么

  • a)以编程方式引用和访问应用程序池标识 .

  • b)通过添加用户组来授予应用程序池标识权限 .

2 回答

  • 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();
            }
        }
    }
    
  • 5

    一个解决方案比我预期的要早,但它不是我喜欢的解决方案 . 对于任何有兴趣的人,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);
    }
    

    希望这对其他人感兴趣,我仍然想知道是否有人遇到过工作的,完全托管的解决方案 .

相关问题