问题:创建新的IIS应用程序池并将其设置为使用应用程序池标识获取权限时,我不确定如何将这些标识添加到用户组(如管理员或性能计数器用户)。
背景:我目前正在编写一个使用Microsoft.Web.Administration的C#.NET库,以便执行以下操作:
上下文是,可执行安装程序将使用此库在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有关,而不是与安装上下文有关。
那么,有没有人知道适当的方法是什么
答案 0 :(得分:5)
感谢你写得很好的问题。这正是我昨晚试图解决的问题,它让我足够继续,我终于能够拼凑出一个只使用托管代码的答案。我发现有三个步骤可以让框架找到并使用虚拟用户:
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();
}
}
}
答案 1 :(得分:1)
一个解决方案比我预期的更早出现,尽管它不是我喜欢的解决方案。对于任何感兴趣的人,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);
}
希望其他人会感兴趣,我仍然想知道是否有人遇到过完全可管理的解决方案。