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

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

  • Служба Windows, работающая как LocalSystem в Windows Server 2003.
  • Веб-сайт .NET 3.5, работающий в том же окне с настройками IIS рабочего сервера по умолчанию (так, вероятно, для пользователя 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:

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

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

EDIT:

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

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

MS KB статья и это, только для лучшего понимания

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

Ответы [ 6 ]

13 голосов
/ 03 мая 2012

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

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

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

6 голосов
/ 04 мая 2009

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

<identity impersonate="true"/>
1 голос
/ 14 июля 2017

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

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

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

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

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

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

1 голос
/ 13 октября 2014

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

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

  • Шаг 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();
            }
    
        }
    }
    
0 голосов
/ 07 октября 2016

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

0 голосов
/ 04 мая 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...