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


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

  • служба Windows, работающая как LocalSystem в окне Windows Server 2003.
  • веб-сайт .NET 3.5, работающий в том же окне, в разделе" default " production server IIS settings (так, вероятно, как пользователь NETWORKSERVICE?)

В моей среде разработки VS2008 по умолчанию у меня есть один метод, который вызывается из 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:

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

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

Редактировать:

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

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

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

Примечание: я не выдаю себя через интернет.config, я делаю это в коде. Смотрите статью MS KB выше.

6 14

6 ответов:

Попробуйте добавить это в свой веб.Конфиг.

<identity impersonate="true"/>

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

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

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

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

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

    Шаг 1: создайте учетную запись пользователя Windows на локальном компьютере с минимальными правами. Шаг 2: предоставьте этому пользователю права на запуск и остановку службы через subinacl.exe
  • , т. е. программы SubInACL.exe / service WindowsServiceName / GRANT=PCNAME\TestUser=STOE
  • Dowload от : 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();
            }
    
        }
    }
    

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

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

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

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

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

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

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

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