[英]Programmatically assigning IIS Application Pool Identity “users” to Groups
The Problem: When new IIS Application Pools are created and set to use the Application Pool Identity for permissions, I am unsure how to add those identities to User Groups such as Administrator or Performance Counter Users. 问题:创建新的IIS应用程序池并将其设置为使用应用程序池标识获取权限时,我不确定如何将这些标识添加到用户组(如管理员或性能计数器用户)。
The Background: I'm currently writing a C#.NET library which uses Microsoft.Web.Administration in order to do the following: 背景:我目前正在编写一个使用Microsoft.Web.Administration的C#.NET库,以便执行以下操作:
The context is that this library is to be used by executable installers to provide automated deployment of a web server and web sites/services on Windows Server OSes as part of a larger software deployment. 上下文是可执行安装程序将此库用于在Windows Server操作系统上自动部署Web服务器和Web站点/服务,作为更大的软件部署的一部分。 So far, all of the above has been implemented, tested, and is (mostly) functional except for the automation of some permissions that need to be performed on Application Pool / Website creation.
到目前为止,除了需要在应用程序池/网站创建上执行的某些权限的自动化之外,所有上述内容都已实现,测试并且(大部分)功能正常。
In my method for installing a new website, I create a new Application Pool and force it to use the Application Pool Identity: 在我安装新网站的方法中,我创建了一个新的应用程序池并强制它使用应用程序池标识:
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 ...
}
As I understand it, this would be the best security practice, and I would then add permissions to specific web sites for anything more than minimal system access. 据我所知,这将是最好的安全实践,然后我会为特定网站添加权限,而不仅仅是最小的系统访问。 Part of this process would be to add these Application Pool identities to User Groups, such as Administrator or Performance Monitor Users.
此过程的一部分是将这些应用程序池标识添加到用户组,例如管理员或性能监视器用户。 This is where complications arise.
这是出现复杂情况的地方。
Now, as documented elsewhere , each Application Pool Identity exists in the format of IIS AppPool\\\\<pool_name>
but this faux-user is not listed through the normal GUI user management controls, and does not seem to be accessible through libraries such as System.DirectoryServices.AccountManagement
when following this example on SO . 现在,正如其他地方所述 ,每个应用程序池标识都以
IIS AppPool\\\\<pool_name>
的格式存在,但是这个faux-user没有通过普通的GUI用户管理控件列出,并且似乎无法通过诸如System.DirectoryServices.AccountManagement
库访问System.DirectoryServices.AccountManagement
以下时上SO这个例子 。 Also, other questions about the Application Pool Identity seem to relate to referencing it from within a child website , not from within an installation context. 此外,有关应用程序池标识的其他问题似乎与在子网站内引用它有关,而不是在安装上下文中引用它 。
So, does anyone know what the proper methods are for 那么,有谁知道适当的方法是什么
Thanks for your well-written question. 谢谢你写得很好的问题。 It is exactly the problem that I was trying to solve last night and it gave me enough to go on that I was able finally cobble together an answer that uses only managed code.
这正是我昨晚试图解决的问题,它让我足够继续,我终于能够拼凑出一个只使用托管代码的答案。 There were three steps that I found to getting the framework to find and work with the virtual user:
我发现有三个步骤可以让框架找到并使用虚拟用户:
new System.Security.Principal.NTAccount(@"IIS APPPOOL\\<appPoolName>")
to get a handle on the account. new System.Security.Principal.NTAccount(@"IIS APPPOOL\\<appPoolName>")
来获取帐户的句柄。 .Translate(typeof (System.Security.Principal.SecurityIdentifier))
to convert it to a SID .Translate(typeof (System.Security.Principal.SecurityIdentifier))
将其转换为SID Principal.FindByIdentity()
treats that SID like it is a group, rather than a user Principal.FindByIdentity()
将SID视为一个组,而不是用户 A final working program (Windows Server 2012 for my test) is as follows: 最终的工作程序(我的测试的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();
}
}
}
A solution presented itself sooner than I expected, though it's not the one I preferred. 一个解决方案比我预期的要早,但它不是我喜欢的解决方案。 For anyone interested, there are a couple of additional options on this pinvoke page .
对于任何感兴趣的人, 此pinvoke页面上还有一些其他选项。 The managed solution did not work for me, but the sample using DllImport worked.
托管解决方案对我不起作用,但使用DllImport的示例工作正常。 I ended up adjusting the sample to handle arbitrary groups based on mapping an enum to SID strings, and including another DllImport for:
我最后调整了样本来处理任意组,基于将枚举映射到SID字符串,并包括另一个DllImport:
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool ConvertStringSidToSid(
string StringSid,
out IntPtr ptrSid);
The modified (working) function looks something like this: 修改后的(工作)函数看起来像这样:
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);
}
Hopefully this will be of interest to someone else, and I would still like to know if anyone comes across a working, fully managed solution. 希望这对其他人感兴趣,我仍然想知道是否有人遇到过工作的,完全托管的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.