2009-05-04 10 views
14

Aquí está mi pila de seguridad/.NET de Windows:Cómo iniciar/detener un servicio de Windows desde una aplicación ASP.NET - Problemas de seguridad

  • Un servicio de Windows que se ejecuta como LocalSystem en una máquina Windows Server 2003.
  • Un .NET 3.5 Sitio web que se ejecuta en la misma caja, bajo el servidor de producción "por defecto" IIS configuración (lo que probablemente como usuario NETWORKSERVICE?)

En mi entorno por defecto VS2008 DEV tengo éste método, que se llamada desde la aplicación ASP.NET, que funciona bien:

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); 
     } 
    } 
} 

Cuando ejecuto esto en el servidor de producción, me sale el siguiente error de la ServiceController:

Sourc e: System.ServiceProcess -> System.ServiceProcess.ServiceController -> IntPtr GetServiceHandle (Int32) -> System.InvalidOperationException Mensaje: No se puede abrir el servicio eTimeSheetReminderService en la computadora '.'.

¿Por qué ocurre esto y cómo lo soluciono?

EDIT:

La respuesta está abajo, sobre todo en los comentarios, pero para aclarar:

  1. El tema fue relacionada con la seguridad, y se produjo porque la cuenta NETWORKSERVICE no tiene derechos suficientes para Iniciar/Detener un servicio
  2. Creé una cuenta de usuario local y la agregué al grupo de PowerUsers (este grupo tiene casi derechos de administrador)
  3. No quiero toda mi aplicación web para suplantar a ese usuario todo el tiempo, por lo que me hago pasar por el método en el que manipulo el servicio. Hago esto mediante el uso de los siguientes recursos para ayudarme a hacerlo en código:

MS KB article y this, just to get a better understanding

NOTA: No suplantar a través del web.config, lo hago en el código. Ver el artículo de MS KB arriba.

+0

¿Qué tipo de excepción era? A 'System.InvalidOperationException'? – Phaedrus

+0

@Phaedrus: hey, sí, sí lo era. He puesto más información de error en mi edición. vítores – andy

Respuesta

6

Intente agregar esto a su Web.Config.

<identity impersonate="true"/> 
+0

hey phaedrus, mismo error – andy

+0

¿Está habilitado el acceso anónimo en IIS? – Phaedrus

+0

sí, "Habilitar el acceso anónimo" está marcado – andy

0

Solo una corazonada, pero no me parece que el error esté necesariamente relacionado con la seguridad. ¿Le dio el mismo nombre al servicio en el servidor de producción?

+0

@cdonner: sí, sospeché eso también, pero no sabía cómo podría probarlo? Sí, el nombre es el mismo, se define en el código, en el componente ServiceInstaller. ¿Alguna idea sobre cómo podría obtener una respuesta definitiva sobre si se trata de seguridad? cheers – andy

+0

¿Puede iniciarlo y detenerlo desde la línea de comando, usando NET START/STOP? – cdonner

+0

hola hombre, sí, ejecutando "NET START eTimeSheetReminderService" en el símbolo del sistema inicia el servicio con éxito – andy

12

dar permiso IIS para iniciar/detener un servicio en particular:

  • Descargar e instalar Subinacl.exe. ( ¡Asegúrese de obtener la última versión! ¡Las versiones anteriores distribuidas en algunos kits de recursos no funcionan!)
  • emitir un comando similar a: subinacl /service {yourServiceName} /grant=IIS_WPG=F

Esto concede derechos de control de servicio completo para ese servicio en particular al grupo integrado IIS_WPG. (Esto funciona para IIS6/Win2k3.) YMMV hay nuevas versiones de IIS)

+0

Perfecto, hice el truco para mí y ni siquiera tuve que agregar la suplantación a mi web.config. ¡Aclamaciones! – Nabster

+0

Esta es la mejor solución en mi opinión. Gracias Martin_ATS. – amiir

+0

Utilice esta herramienta para otorgar derechos de usuario, mediante el cual utiliza la suplantación para suplantar al usuario que tiene derechos para iniciar y detener el servicio. – 130nk3r5

1

Ésta era una buena pregunta que me ha intrigado, así ...

Así que aquí está lo que hice para solucionar este problema:.

  • Paso 1: Cree una cuenta de usuario de Windows en la máquina local con derechos mínimos.
  • Paso 2: Dar a este derecho de usuario para iniciar y detener el servicio a través de subinacl.exe
  • es decir subinacl.exe/servicio WindowsServiceName/GRANT = pcname \ usuarioDePrueba = STOE
  • Dowload de: http://www.microsoft.com/en-za/download/details.aspx?id=23510
  • Paso 3: uso de suplantación de hacerse pasar por el uso creado en el paso 1 para iniciar y detener el servicio

    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

LOGON32_LOGON_INTERACTIVE no funcionó, pero gracias a esta respuesta https://stackoverflow.com/a/28503968/4568373 Lo cambié a LOGON32_LOGON_NETWORK y funcionó bien. –

0

Si su aplicación web tiene la base de datos y el servicio de Windows puede acceder a él, solo puede usar el indicador en el DB para reiniciar el servicio. En el servicio, puede leer este indicador y reiniciar si no está ocupado, etc. Solo en caso de que pueda modificar el código del servicio. Si se trata de un servicio de terceros, puede crear su propio servicio de Windows y usar la configuración de la base de datos para controlar (reiniciar) los servicios. Es la manera segura y le da mucha más flexibilidad y seguridad.

1

actualización para IIS 8 (y tal vez algunas versiones ligeramente anteriores)

El grupo de usuarios IIS_WPG ya no existe. Ha cambiado a IIS_IUSRS.

Además, para iniciar la detención de un servicio no es necesario otorgar permisos completos (F). Los permisos para iniciar, detener y pausar un servicio (TOP) deberían ser suficientes. Como tal, el comando debe ser:

subinacl/servicio {yourServiceName}/conceder = IIS_IUSRS = TOP

cuenta que es necesario señalar el símbolo del sistema (elevada preferentemente a ejecutar como administrador) a C:\Windows\System32 Carpeta antes de ejecutar este comando.

También asegúrese de haber copiado el archivo subinacl.exe a C:\Windows\System32 desde el directorio de instalación si hay un error.

Cuestiones relacionadas