Windows 2008 RenderFarm 服务:CreateProcessAsUser“会话 0 隔离”和OpenGL
我有一个旧版 Windows 服务器服务和(生成的)应用程序,在 XP-64 和 W2K3 中运行良好,但在 W2K8 上失败。我相信这是因为新的“会话 0 隔离< /a>”功能。
因此,我正在寻找代码示例/安全设置魔力,让您可以从 Windows 2008 Server 的 Windows 服务创建一个新进程,以便我可以恢复(并可能超越)以前的行为。我需要一个解决方案:
- 在非零会话中创建新进程以绕过会话 0 隔离限制(无法从会话 0 访问图形硬件) - MS 对此的官方路线是:
因为会话0不再是用户 会话,正在运行的服务 会话 0 无权访问 视频驱动程序。这意味着任何 服务尝试渲染 图形失败。查询显示 会话中的分辨率和颜色深度 0 报告正确的结果 系统最高可达 1920x1200 每像素 32 位。
新进程获取可用于创建 Windows DC 的 Windows 工作站/桌面(例如 winsta0/default)。我在这里找到了一个解决方案(在交互式会话中正常启动): 在 C++ 中启动交互式客户端进程
Windows DC,用作OpenGLDescribePixelFormat枚举,能够找到并使用硬件加速格式(在适当配备OpenGL硬件的系统上)。请注意,我们当前的解决方案工作正常在 XP-64 和 W2K3 上,除非终端服务会话正在运行(VNC 工作正常)。一个还允许进程工作的解决方案(即,即使终端服务会话打开,也可以使用 OpenGL 硬件加速运行)将是疯狂的,尽管不是必需的。
我目前陷入了第 1 项,尽管有一些类似的帖子讨论了这个问题(例如 这个,以及这个 - 它们不是合适的解决方案,因为不能保证登录的用户会话已经“采取”会话ID,我也不是从本地系统帐户运行(我是从服务的域帐户运行,我可以在合理的范围内调整其权限,尽管我不想升级优先级以包括SeTcbPrivileges。)
例如 - 这是一个我认为应该可以工作的存根,但总是在 SetTokenInformation 调用上返回错误 1314 (即使 AdjustTokenPrivileges 没有返回错误)我也使用了一些涉及“LogonUser”的替代策略(而不是打开现有的进程令牌),但我似乎无法交换会话 ID。
我还对在所有情况下使用 WTSActiveConsoleSessionId 表示怀疑(例如,如果没有交互式用户登录) - 尽管在没有登录会话的情况下运行的服务的快速测试似乎返回了合理的会话值 (1)。
为了便于阅读,我已经删除了错误处理(仍然有点混乱 - 抱歉)
//Also tried using LogonUser(..) here
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
| TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_SESSIONID
| TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY
| TOKEN_DUPLICATE, &hToken)
GetTokenInformation( hToken, TokenSessionId, &logonSessionId, sizeof(DWORD), &dwTokenLength )
DWORD consoleSessionId = WTSGetActiveConsoleSessionId();
/* Can't use this - requires very elevated privileges (LOCAL only, SeTcbPrivileges as well)
if( !WTSQueryUserToken(consoleSessionId, &hToken))
...
*/
DuplicateTokenEx(hToken, (TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_SESSIONID | TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE), NULL, SecurityIdentification, TokenPrimary, &hDupToken))
// Look up the LUID for the TCB Name privilege.
LookupPrivilegeValue(NULL, SE_TCB_NAME, &tp.Privileges[0].Luid))
// Enable the TCB Name privilege in the token.
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hDupToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, 0))
{
DisplayError("AdjustTokenPrivileges");
...
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
DEBUG( "Token does not have the necessary privilege.\n");
} else {
DEBUG( "No error reported from AdjustTokenPrivileges!\n");
} // Never errors here
DEBUG(LM_INFO, "Attempting setting of sessionId to: %d\n", consoleSessionId );
if (!SetTokenInformation(hDupToken, TokenSessionId, &consoleSessionId, sizeof(DWORD)))
*** ALWAYS FAILS WITH 1314 HERE ***
所有调试输出看起来都很好,直到 SetTokenInformation 调用 - 我看到会话 0 是我当前的进程会话,在我的情况下,它正在尝试设置会话1(WTSGetActiveConsoleSessionId 的结果)。 (请注意,我是通过 VNC 而不是 RDC 登录到 W2K8 盒子的)
因此,问题是:
- 这种方法是否有效,或者所有服务启动的进程是否都故意限制在会话 0 中?
- 是否有更好的方法(缺少“登录时启动”和服务器自动登录?)
- 此代码是否有问题,或者创建可以交换的进程令牌的不同方法输出会话 ID 来指示我想在新会话中生成该进程?我确实尝试使用 LogonUser 而不是 OpenProcessToken,但这也不起作用。 (我不在乎此时所有生成的进程是否共享相同的非零会话。)
非常感谢任何帮助 - 谢谢!
I have a legacy Windows server service and (spawned) application that works fine in XP-64 and W2K3, but fails on W2K8. I believe it is because of the new "Session 0 isolation" feature.
Consequently, I'm looking for code samples/security settings mojo that let you create a new process from a windows service for Windows 2008 Server such that I can restore (and possibly surpass) the previous behavior. I need a solution that:
- Creates the new process in a non-zero session to get around session-0 isolation restrictions (no access to graphics hardware from session 0) - the official MS line on this is:
Because Session 0 is no longer a user
session, services that are running in
Session 0 do not have access to the
video driver. This means that any
attempt that a service makes to render
graphics fails. Querying the display
resolution and color depth in Session
0 reports the correct results for the
system up to a maximum of 1920x1200 at
32 bits per pixel.
The new process gets a windows station/desktop (e.g. winsta0/default) that can be used to create windows DCs. I've found a solution (that launches OK in an interactive session) for this here: Starting an Interactive Client Process in C++
The windows DC, when used as the basis for an OpenGL DescribePixelFormat enumeration, is able to find and use the hardware-accelerated format (on a system appropriately equipped with OpenGL hardware.) Note that our current solution works OK on XP-64 and W2K3, except if a terminal services session is running (VNC works fine.) A solution that also allowed the process to work (i.e. run with OpenGL hardware acceleration even when a terminal services session is open) would be fanastic, although not required.
I'm stuck at item #1 currently, and although there are some similar postings that discuss this (like this, and this - they are not suitable solutions, as there is no guarantee of a user session logged in already to "take" a session id from, nor am I running from a LocalSystem account (I'm running from a domain account for the service, for which I can adjust the privileges of, within reason, although I'd prefer to not have to escalate priorities to include SeTcbPrivileges.)
For instance - here's a stub that I think should work, but always returns an error 1314 on the SetTokenInformation call (even though the AdjustTokenPrivileges returned no errors) I've used some alternate strategies involving "LogonUser" as well (instead of opening the existing process token), but I can't seem to swap out the session id.
I'm also dubious about using the WTSActiveConsoleSessionId in all cases (for instance, if no interactive user is logged in) - although a quick test of the service running with no sessions logged in seemed to return a reasonable session value (1).
I’ve removed error handling for ease of reading (still a bit messy - apologies)
//Also tried using LogonUser(..) here
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
| TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_SESSIONID
| TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY
| TOKEN_DUPLICATE, &hToken)
GetTokenInformation( hToken, TokenSessionId, &logonSessionId, sizeof(DWORD), &dwTokenLength )
DWORD consoleSessionId = WTSGetActiveConsoleSessionId();
/* Can't use this - requires very elevated privileges (LOCAL only, SeTcbPrivileges as well)
if( !WTSQueryUserToken(consoleSessionId, &hToken))
...
*/
DuplicateTokenEx(hToken, (TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_SESSIONID | TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE), NULL, SecurityIdentification, TokenPrimary, &hDupToken))
// Look up the LUID for the TCB Name privilege.
LookupPrivilegeValue(NULL, SE_TCB_NAME, &tp.Privileges[0].Luid))
// Enable the TCB Name privilege in the token.
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hDupToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, 0))
{
DisplayError("AdjustTokenPrivileges");
...
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
DEBUG( "Token does not have the necessary privilege.\n");
} else {
DEBUG( "No error reported from AdjustTokenPrivileges!\n");
} // Never errors here
DEBUG(LM_INFO, "Attempting setting of sessionId to: %d\n", consoleSessionId );
if (!SetTokenInformation(hDupToken, TokenSessionId, &consoleSessionId, sizeof(DWORD)))
*** ALWAYS FAILS WITH 1314 HERE ***
All the debug output looks fine up until the SetTokenInformation call - I see session 0 is my current process session, and in my case, it's trying to set session 1 (the result of the WTSGetActiveConsoleSessionId). (Note that I'm logged into the W2K8 box via VNC, not RDC)
So - a the questions:
- Is this approach valid, or are all service-initiated processes restricted to session 0 intentionally?
- Is there a better approach (short of "Launch on logon" and auto-logon for the servers?)
- Is there something wrong with this code, or a different way to create a process token where I can swap out the session id to indicate I want to spawn the process in a new session? I did try using LogonUser instead of OpenProcessToken, but that didn't work either. (I don't care if all spawned processes share the same non-zero session or not at this point.)
Any help much appreciated - thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
对于任何有兴趣解决此问题的人:
我与 LogonSDK 团队的 MS 支持人员讨论了此问题。看来不可能以编程方式完全模拟交互式用户,这样您就可以获得物理控制台和关联的 GDI 结构,而且到目前为止我们基本上“很幸运”它一直有效。他们确实确认会话 0 隔离是回归的根本原因。
他们的建议是启用交互式会话的自动登录,并重构服务以与交互式会话中的新客户端组件进行通信。为了解决此问题的安全缺点,他们建议实施外壳替换,以在登录时将服务器置于“Kiosk”模式(例如,没有适当的凭据,则无法访问资源管理器等)。从
好的方面来说,这应该可以解决我们遇到的问题。我们遇到过终端服务会话杀死我们的硬件加速的情况。
我将向 MS 提交请求,考虑在未来版本中支持“代理用户会话”的这种“渲染农场”用例,以便服务器可以生成硬件加速进程,而不会因需要现有客户端用户进程而造成安全隐患在控制台登录。
For anyone interested in the resolution of this issue:
I discussed this issue with MS Support for the LogonSDK team. It appears that it is not possible to fully impersonate an interactive user programatically, such that you get a physical console and associated GDI constructs, and we've essentially been "just lucky" that it worked until now. They did confirm that session 0 isolation was the root cause of the regression.
Their recommendation is to enable auto-logon to an interactive session, and refactor the service to speak to a new client component in the interactive session. To address the security disadvantage of this, they recommend implementing a shell replacement to place the server in a "Kiosk" mode on logon (e.g. no Explorer access without appropriate credentials, etc.)
On the up-side, this should address the issues we've been encountering with terminal service sessions killing our hardware acceleration.
I will be submitting a request to MS consider this kind of "render farm" use case for "proxy user session" support in future releases, such that a server can spawn hardware-accelerated processes without the security compromise of requiring an existing client user process to be logged on at the console.
我尚未完成培训课程,但我在 Microsoft-MSDN 站点上找到了“会话 0 隔离解决方法”教程:
http://msdn.microsoft.com/en-us/windows7trainingcourse_sessionisolation_unit.aspx
我在计划任务中也出现了相同的 session-0-isolation 问题。
I have not completed the training course, but I have found a "Session 0 isolation workaround" tutorial on the Microsoft-MSDN site:
http://msdn.microsoft.com/en-us/windows7trainingcourse_sessionisolation_unit.aspx
I have the same session-0-isolation problem manifested within a scheduled task.
farmComm 将在会话 0 中启动您选择的应用程序,不显示任何 GUI,但无论用户是否登录,都可以访问图形硬件。它还响应任何活动会话中的用户活动(包括会话 0 或“安全桌面”,当它是活动会话时)。它旨在在用户空闲时启动应用程序,并在用户从空闲状态恢复时终止应用程序,但这些运行条件可以在源 AutoHotkey 脚本中轻松更改。
https://github.com/r-alex-hall/farmComm
它在以下位置生成应用程序session 0“不可见”,但可以很容易地对其进行修改(将值为“隐藏”的变量更改为“显示”)以使生成的进程的 GUI 可见(如果它们有 GUI)。但是,如果它们可见,则它们可能会触发 Windows nags 在会话 0 中查看“消息”,和/或仅在会话 0 中可见(这似乎包括“安全桌面”可见的任何时间 - 对于例如,当工作站被锁定、或与用户会话断开连接、或没有用户登录时)。
在撰写本文时,如果在 farmComm 生成的进程运行时启动任何远程桌面 (RDP) 会话,farmComm 将终止这些进程并尝试重新启动它们以响应 RDP 会话,如果它们是尝试访问的应用程序图形硬件,可能会导致它们崩溃(因为 RDP 限制对图形硬件的访问)。也许这个 RDP 问题也可以解决。 。 。或者您可以调整源以永远不会终止进程,或者永远不会迁移到其他会话。 (注意:一个可能的计划更改是使您能够编写脚本来确定 farmComm 是否以及何时终止、不终止、挂起或恢复进程,或者就此而言,编写脚本以在用户从空闲状态恢复时运行完全不同的进程)。
这些脚本可以编译为可执行文件,在我的发行版中,它们就是这样。
该工具集的关键是 paexec 的特定版本(它将应用程序启动到会话 0),以及 AutoHotkey 对用户活动(或缺乏用户活动)的非常可靠的响应,以及有关会话的系统信息的检索。启动“隐藏”进程(不可见 GUI)的选项也可通过 AutoHotkey 实现。
披露:我编写(或编码)farmComm,并将其发布到公共领域。
farmComm will launch the application(s) of your choosing into session 0, with no GUI visible, but with access to graphics hardware, whether users are logged in or not. It also responds to user activity in whatever session is active (including session 0 or the "secure desktop," when that is the active session). It is designed to launch applications thus when users are idle, and terminate them upon user resume from idle, but those run conditions could easily be altered in the source AutoHotkey scripts.
https://github.com/r-alex-hall/farmComm
It spawns applications in session 0 "invisibly," but it can very easily be modified (change a variable with the value "hide" to "show") to have the GUIs of spawned processes visible (if they have a GUI). If they are visible, however, they may either trigger Windows nags to see "messages" in session 0, and/or only be visible from session 0 (which, it seems, includes any time the "secure desktop" is visible--for example when a workstation is locked, or disconnected from user sessions, or no users are logged on).
At this writing, if any Remote Desktop (RDP) session is begun while processes spawned by farmComm run, farmComm will terminate those processes and attempt to re-launch them to respond to the RDP session, which, if they are applications which attempt to access graphics hardware, may cause them to crash (because RDP restricts access to graphics hardware). Probably this RDP problem could be worked around, too . . . or you can tweak the source to never terminate processes, or never migrate to other sessions. (NOTE: a possible planned change is to enable you to script whether and when farmComm terminates, does not terminate, suspends, or resumes processes--or for that matter, script it to run entirely different processes when users resume from idle).
The scripts can be compiled to executables, which in my distribution, they are.
The linchpins of this toolset are a particular version of paexec (which launches applications into session 0), and AutoHotkey's very reliable responses to user activity (or lack thereof), and retrieval of system information about a session. The option to launch processes "hidden" (with no GUI visible) is also via AutoHotkey.
Disclosure: I scripted (or coded) farmComm, and released it into the Public Domain.