User Tools

Site Tools


Sidebar


Здравствуйте!

Меня зовут Михаил!
Я системный администратор
и наполняю эту wiki,
решая разнообразные IT-задачки.

Моя специализация - виртуализация!

Я всегда готов помочь Вам
наладить IT-инфраструктуру
за скромное вознаграждение!

mike@autosys.tk
+7 (910) 911-96-23

ms_windows_ms_sql:web_gui_for_powershell_scripts

PowerShell using ASP.NET application

У меня получилось сделать все что нужно (запустить от имени пользователя оболочку powershell, передать ей скрипт и получить обратно консольный вывод с помощью информации из этих ссылок):
Описано расширение стандартного класса Process и его примемение: https://odetocode.com/blogs/scott/archive/2004/10/29/createprocessasuser.aspx
Вот тут написано как вызывать эти методы: http://rsdn.org/forum/dotnet/3287068.1

Если необходимо, чтобы Application Pool работал в Integrated, то вызов методов, которые запускают процесс, нужно обертывать в using (WindowsImpersonationContext wic = clientId.Impersonate()). Об этом рассказано тут: https://stackoverflow.com/questions/12966286/impersonate-domain-user-with-integrated-pipeline .

Например, вот обработчик нажатия кнопки, который запускает powershell-скрипт от имени авторизованного пользователя:

protected void ExecuteCode_Click(object sender, EventArgs e)
        {

            ResultBox.Text = string.Empty;

            var current = System.Security.Principal.WindowsIdentity.GetCurrent();
            WindowsIdentity clientId = (WindowsIdentity)User.Identity;

            using (WindowsImpersonationContext wic = clientId.Impersonate())
            {
                UserSpecificProcess myProcess = new UserSpecificProcess();
                myProcess.StartInfo.FileName = "powershell.exe";
                myProcess.StartInfo.Arguments = @"-File c:\temp\test.ps1";
                myProcess.StartInfo.UseShellExecute = false;
                myProcess.StartInfo.CreateNoWindow = false;
                myProcess.StartInfo.WorkingDirectory = @"c:\temp\";
                myProcess.StartAsUser();
                StreamReader output = myProcess.StandardOutput;

                string line;
                StringBuilder builder = new StringBuilder();
                while ((line = output.ReadLine()) != null)
                {
                    // We use a string builder ton create our result text
                    builder.Append(line + "\r\n");
                }

                // Encode the string in HTML (prevent security issue with 'dangerous' caracters like < >
                ResultBox.Text = Server.HtmlEncode(builder.ToString());
            }
        }

Пример ASP.NET-приложения, запускающего скрипт от имени залогиненного пользователя

В итоге вот что у меня получилось. Солюшн для работы в VisualStudio 2017 и опубликованное приложение, которое имеет кнопку для запуска powershell-скрипта (c:\temp\test.ps1) и окошко для вывода результатов.
Протестировано на IIS под Windows Server 2012R2. Application pool .NET 4 работает в Integrated Mode, от имени Local System.
Приложение опубликовано без каких-либо двоичных файлов. То есть изменения можно вносить прямо в код опубликованного приложения.

IIS Impersonation и делегирование прав доступа к ресурсам

На эту тему написано очень много. Обычно это именуется impersonation double hop issue. Имеется в виду следующее.
Когда пользователь успешно имперсонирован (олицетворен) в приложении ASP.NET, то он получает доступ к ресурсам СЕРВЕРА, на котором работает приложение от своего имени. То есть если он обращается к файлам или SQL-базе, размещенной на этом сервере, то всё работает. Однако, если приложение пытается получить доступ к ресурсам, размещенным не на этом сервере, а например к файлам на сетевом ресурсе или SQL-базе на другом сервере, то ничего не выйдет.
Для того, чтобы все работало, необходимо, чтобы СЕРВЕР на котором исполняется приложение имел право что-то делать от имени пользователя, то есть - представлять пользователя. Это называется делегированием. Пользователь, аутентифицированный на сервере приложений ASP.NET, делегирует свои права серверу.
Права серверу на делегирование пользователей выдаются с помощью оснастки Active Directory Users and Computers. Нужно открыть свойства учетной записи сервера, на котором исполняется приложение ASP.NET, перейти на вкладку Delegation и включить делегирование.
При этом, если приложение ASP.NET запускает локальный процесс на web-сервере (например - powershell), который осуществляет доступ к нелокальным ресурсам (например подключается к экземплярам Microsoft Exchange или запрашивает данные Active Directory), то делегирование не требуется. Достаточно, чтобы имперсонированный пользователь был авторизован методом Kerberos, а процесс был запущен от имени этого пользователя.

Когда Windows Authentication и делегирование работает на **localhost**, но не работает на FQDN

Еще один важный момент.
Часто можно наблюдать ситуацию, когда при доступе к ASP.NET-приложению непосредственно с самого сервера по адресу localhost все работает. И прозрачная аутентификация и делегирование. Однако, если попытаться зайти на страничку приложения по FQDN-имени сервера (даже с самого этого сервера), то страничка запрашивает имя пользователя и пароль (хотя при заходе по localhost работает прозрачная аутентификация), нормально аутентифицирует пользователя и работает имперсонация, однако делегирование не работает и доступ к ресурсам вне web-сервера запрещен.
Это происходит из-за того, что FQDN-имя сервера не внесено в список Intranet (локальных) сайтов в конфигурации браузера. В результате, браузер аутентифицирует пользователя не по протоколу Kerberos, а по протоколу NTLM, а протокол NTLM не позволяет делегирование. Об этом мне удалось прочитать тут: http://www.justskins.com/forums/iis-and-fqdn-authentication-183966.html#
А вот тут подробнее от Microsoft: https://blogs.msdn.microsoft.com/besidethepoint/2010/05/08/double-hop-authentication-why-ntlm-fails-and-kerberos-works/
Вот тут: https://blogs.msdn.microsoft.com/friis/2013/01/08/asp-net-authentication-test-page/
приведен пример тестовой страницы, которая показывает детальную информацию о методе аутентификации текущего пользователя. Утащил к себе - authpage.zip.

Настройки браузеров для корректной работы имперсонации

Internet Explorer

Параметр настроек Internet Explorer, который предписывает использовать KERBEROS находится в Internet Explorer Optinons → Security → Custom Level и называется Automatic logon with current user name and password. Если он включен, то первым для аутентификации используется KERBEROS, а в случае неудачи - NTLM. Важно знать, что включенный параметр Prompt for user name and password разрешает аутентифицировать пользователей только по NTLM.

Firefox

Если для доступа к приложению используется Firefox, то для того, чтобы он аутентифицировался по KERBROS адрес сайта приложения нужно добавить в доверенные. Для этого открыввем в Firefox страницу about:config. В поле поиска вводим negotiate. Дважды кликаем по параметру network.negotiate-auth.trusted-uris и добавляем туда адрес сайта (например - example.com).

Chrome

Chrome в windows использует настройки Internet Explorer.
В других ОС передать Chrome список доверенных сайтов можно с помощью аргумента командной строки:

--auth-negotiate-delegate-whitelist="*.adexample.pingidentity.com"

Чтобы в приложении ASP.NET работала сквозная Windows-аутентификация, имперсонация и делегирование

В итоге. Чтобы в приложении ASP.NET работала сквозная Windows-аутентификация, имперсонация и делегирование необходимо соблюдение условий:

  • Для приложения вцелом (Classic Application Pool mode) или для куска кода (Integrated Application Pool mode) включена имперсонация.
  • Для приложения отключена аутентификация анонимных пользователей, потому, что IIS применяет первый успешный метод аутентификации.
  • Принудительно включен Kerberos (Windows AuthenticationProviders (справа) оставитьтолько Negotiate:Kerberos)
  • Если адрес к которому обращаются пользователи отличается от имени web-сервера, то для учетки web-сервера должен быть корректно прописан SPN. Толково написано тут: http://winitpro.ru/index.php/2016/05/18/nastrojka-kerberos-avtorizacii-na-sajte-iis/
  • Для учетной записи web-сервера разрешено делегирование.
  • Адрес сайта, на котором размещено приложение добавлен в список Интранет сайтов.

В противном случае - не будет корректно работать делегирование и будут ошибки при доступе к сетевым ресурсам и Active Directory (ошибки типа Exception calling “GetComputerSite” with “0” argument(s): “An operations error occurred.”
Траблшутинг аутентификации Kerberos толково описан тут: https://blogs.msdn.microsoft.com/friis/2009/12/31/things-to-check-when-kerberos-authentication-fails-using-iisie/

Для одного из сервисов я описал настройку web-сервера на базе Windows Server 2016: IIS Web Server Setup for correct impersonating

Еще немного полезных ссылок

Вот этот гайд описывает как запускать процесс от имени имперсонированного пользователя. Вторая ссылка - как получить то что выводится в стандартный вывод.
ASP.NET Run Process As Impersonated User: https://support.microsoft.com/en-us/help/889251/how-to-spawn-a-process-that-runs-under-the-context-of-the-impersonated
Вот тут написано как человек пытается получить вывод от процесса запущенного с помощью CreateProcessAsUser:
https://groups.google.com/forum/#!topic/microsoft.public.dotnet.framework.interop/dM6jE-3eRq4
А вот тут представлен класс, который по идее это все делает. но у меня не получилось :( https://stackoverflow.com/questions/27986399/capture-standard-output-from-createprocessasuser-in-c-sharp

Вот ссылки описывают как создать проект, однако в них не работает имперсонализация пользователя. http://jeffmurr.com/blog/?p=142
https://incoherenttruth.wordpress.com/2011/01/05/execute-powershell-cmdlets-from-asp-net-with-user-impersonation/
Change ASP.NET Settings per pool. https://weblogs.asp.net/owscott/setting-an-aspnet-config-file-per-application-pool
https://discussions.citrix.com/topic/277547-impersonation-seems-not-to-flow-to-powershell-runspace/

PowerShell UI Using Jenkins

Powershell UI Using ASP

http://waynes-world-it.blogspot.ru/2008/05/running-powersell-scripts-from-aspnet.html http://elbsolutions.com/projects/making-powershell-script-run-aspx-worked-12-hour/

Для создания web-страницы, которая сможет запускать PowerShell-скрипт потребуется:

  • Разместить aspx-файл в корневой папке web-проекта.
  • Разместить web.config в корневой папке web-проекта.
  • Если web-приложения работает от имени Network Service, то дать этой службе права на чтение.
  • Собственно сам скрипт PowerShell
  • Библиотека (dll) System.Management.Automation в папке .\bin

Для вывода строк следует использовать Out-String. Для передачи параметров следует использовать $args.

web.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.net>
 <defaultProxy enabled="false"/>
    </system.net>
    <system.web>
 <pages validateRequest="false"/>
 <compilation defaultLanguage="c#" debug="false"/>
 <customErrors mode="Off"/>
 <authentication mode="Windows"/>
 <identity impersonate="true"/>
    </system.web>
</configuration>

Powershell скрипт test.ps1

if ($args.count -ge 2)
{ $text = $args[1]}
write-output $text

Страница aspx - Test.aspx

<%@ Page language="c#" AutoEventWireup="true" Debug="true" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Management.Automation.Runspaces" %>
<%@ Import Namespace="System.Management.Automation" %>
<%@ Import Namespace="System.Collections.ObjectModel" %>


<script language="C#" runat="server">

// The previous lines use <%...%> to indicate script code, and they specify the namespaces to import. As mentioned earlier, the assemblies must be located in the \Bin subdirectory of the application's starting point.
// http://msdn.microsoft.com/en-us/library/aa309354(VS.71).aspx


private void Button3_Click(object sender, System.EventArgs e)
{
String fp = Server.MapPath(".") + "\\" + tPowerShellScriptName.Text;
StreamReader sr = new StreamReader(fp);
tPowerShellScriptCode.Text = sr.ReadToEnd();
sr.Close();
}

private void Button2_Click(object sender, System.EventArgs e)
{
tPowerShellScriptResult.Text = RunScript(tPowerShellScriptCode.Text);
}

// http://msdn.microsoft.com/en-us/library/ms714635(VS.85).aspx

private string RunScript(string scriptText)
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();

// Create a new runspaces.command object of type script
Command cmdScript = new Command(scriptText, true, false);
cmdScript.Parameters.Add("-t", txtInput.Text);
pipeline.Commands.Add(cmdScript);
//You could also use: pipeline.Commands.AddScript(scriptText);

// Re-format all output to strings
pipeline.Commands.Add("Out-String");

// Invoke the pipeline
Collection<PSObject> results = pipeline.Invoke();

//String sresults = pipeline.Output.Count.ToString();
//sresults = sresults + "," + results.Count.ToString();
String sresults = "";

foreach (PSObject obj in results)
{
sresults = sresults + obj.ToString();
}

// close the runspace and set to null
runspace.Close();
runspace = null;

return sresults;
}


</script>

<form id="Form1" method="post" runat="server">
<P> <asp:Label id="Label1" runat="server" Width="104px">Parameter:</asp:Label>
<asp:TextBox id="txtInput" runat="server"></asp:TextBox></P>
<P> <asp:Button id="Button3" runat="server" Text="Load" OnClick="Button3_Click"></asp:Button> </P>
<P> <asp:Button id="Button2" runat="server" Text="Run" OnClick="Button2_Click"></asp:Button> </P>
<P> <asp:Label id="Label2" runat="server" >Relative script name:</asp:Label>
<asp:TextBox id="tPowerShellScriptName" Text="test.ps1" runat="server"></asp:TextBox></P>
<P> <asp:TextBox rows="20" columns="120" TextMode="multiline" id="tPowerShellScriptCode" runat="server"></asp:TextBox></P>
<P> <asp:TextBox rows="8" columns="120" TextMode="multiline" id="tPowerShellScriptResult" runat="server"></asp:TextBox></P>
</form>

Discussion

Enter your comment. Wiki syntax is allowed:
V X Q E N
 
ms_windows_ms_sql/web_gui_for_powershell_scripts.txt · Last modified: 2018/07/20 15:06 by admin