Как запустить/остановить службу Windows из приложения ASP.NET - Проблемы безопасности

Здесь мой стек безопасности Windows/.NET:

  • Служба Windows, работающая под управлением локальной системы Windows Server 2003.
  • Веб-сайт .NET 3.5, работающий в одном и том же поле, в настройках IIS для рабочих станций по умолчанию (возможно, как пользователь NETWORKSERVICE?)

В моей стандартной среде VS2008 DEV у меня есть этот один метод, который вызывается из приложения ASP.NET, который отлично работает:

private static void StopStartReminderService() {

    ServiceController svcController = new ServiceController("eTimeSheetReminderService");

    if (svcController != null) {
        try {
            svcController.Stop();
            svcController.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(10));
            svcController.Start();
        } catch (Exception ex) {
            General.ErrorHandling.LogError(ex);
        }
    }
}

Когда я запускаю это на производственном сервере, я получаю следующую ошибку от ServiceController:

Источник: System.ServiceProcess → System.ServiceProcess.ServiceController → IntPtr GetServiceHandle (Int32) → System.InvalidOperationException Сообщение: Не удается открыть службу eTimeSheetReminderService на компьютере. '.

Почему это происходит и как его исправить?

EDIT:

Ответ ниже, в основном в комментариях, но для уточнения:

  • Проблема связана с безопасностью и произошла потому, что учетная запись NETWORKSERVICE не имела достаточных прав на запуск/остановку службы
  • Я создал локальную учетную запись пользователя и добавил ее в группу PowerUsers (эта группа имеет почти права администратора).
  • Я не хочу, чтобы все мое веб-приложение постоянно выдавало себя за этого пользователя, поэтому я олицетворяю только метод, в котором я манипулирую службой. Я делаю это, используя следующие ресурсы, чтобы помочь мне сделать это в коде:

статья MS KB и это, просто чтобы получить лучшее понимание

ПРИМЕЧАНИЕ: Я не выдаю себя за помощью через web.config, я делаю это в коде. См. Статью MS KB выше.

Ответы

Ответ 1

Попробуйте добавить это в свой Web.Config.

<identity impersonate="true"/>

Ответ 2

Чтобы предоставить разрешение IIS для запуска/остановки определенной службы:

  • Загрузите и установите Subinacl.exe. (Обязательно получите последнюю версию! Ранние версии, распространяемые в некоторых наборах ресурсов, не работают!)
  • Выполните команду, похожую на: subinacl /service {yourServiceName} /grant=IIS_WPG=F

Это предоставляет права управления полным сервисом для этой конкретной службы во встроенную группу IIS_WPG. (Это работает для IIS6/Win2k3.) YMMV для новых версий IIS.)

Ответ 3

Это был хороший вопрос, который тоже заинтриговал меня...

Итак, вот что я сделал для решения этой проблемы:

  • Шаг 1. Создайте учетную запись пользователя Windows на локальном компьютере с минимальными правами.
  • Шаг 2. Предоставьте этим пользователям права запускать и останавливать службу через subinacl.exe
  • то есть. subinacl.exe/service WindowsServiceName/GRANT = PCNAME\TestUser = STOE
  • Загрузите из: http://www.microsoft.com/en-za/download/details.aspx?id=23510
  • Шаг 3: Используйте олицетворение для олицетворения использования, созданного на шаге 1, для запуска и остановки службы

    public const int LOGON32_PROVIDER_DEFAULT = 0;
    
    WindowsImpersonationContext _impersonationContext;
    
    [DllImport("advapi32.dll")]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern int LogonUserA(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);
    
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern bool RevertToSelf();
    
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    // ReSharper disable once MemberCanBePrivate.Global
    public static extern bool CloseHandle(IntPtr handle);
    
    private bool _impersonate;
    
    public bool ImpersonateValidUser(String userName, String domain, String password)
    {
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;
    
        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    var tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    _impersonationContext = tempWindowsIdentity.Impersonate();
                    if (_impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        _impersonate = true;
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        _impersonate = false;
        return false;
    }
    
    #region Implementation of IDisposable
    
    
    
    
    #endregion
    
    #region Implementation of IDisposable
    
    private void Dispose(bool dispose)
    {
        if (dispose)
        {
            if (_impersonate)
                _impersonationContext.Undo();
            _impersonationContext.Dispose();
        }
    }
    
    public void Dispose()
    {
        Dispose(true);
    }
    #endregion
    
    public static void StartStopService(bool startService, string serviceName)
    {
        using (var impersonateClass = new Impersonation())
        {
            impersonateClass.ImpersonateValidUser(Settings.Default.LocalUsername, Settings.Default.Domain, Settings.Default.Password);
            using (var sc = new ServiceController(serviceName))
            {
                if (startService)
                    sc.Start();
                else if (sc.CanStop)
                    sc.Stop();
            }
    
        }
    }
    

Ответ 4

Обновление для IIS 8 (и, возможно, несколько более ранних версий)

Сгруппировка IIS_WPG больше не существует. Он изменился на IIS_IUSRS.

Кроме того, чтобы начать остановку службы, необязательно давать полные разрешения (F). Разрешений на запуск, остановку и приостановку обслуживания (TOP) должно быть достаточно. Таким образом, команда должна быть:

subinacl/service {yourServiceName}/grant = IIS_IUSRS = TOP

Обратите внимание, что перед запуском этой команды вам нужно указать командную строку (предпочтительно повышенную для запуска от имени администратора) до C:\Windows\System32 Folder.

Также убедитесь, что вы скопировали файл subinacl.exe в C:\Windows\System32 из каталога установки, если есть ошибка.

Ответ 5

Просто догадка, но мне не кажется, что ошибка обязательно связана с безопасностью. Вы предоставляли услугу с тем же именем на производственном сервере?

Ответ 6

Если ваше веб-приложение имеет доступ к базе данных и службе Windows, вы можете просто использовать флаг в БД для перезапуска службы. В службе вы можете прочитать этот флаг и перезапустить, если не заняты и т.д. Только в том случае, если вы можете изменить код службы.      Если это сторонний сервис, вы можете создать свою собственную службу Windows и использовать конфигурацию базы данных для управления (перезапуска) служб. Это безопасный способ и дает вам гораздо большую гибкость и безопасность.