2009-05-04 10 views
14

Ecco il mio Windows pila/protezione di .NET:Come avviare/arrestare un servizio Windows da un'applicazione ASP.NET - Sicurezza emette

  • un servizio di Windows in esecuzione come LocalSystem su una macchina Windows Server 2003.
  • un sito web NET 3.5 in esecuzione sulla stessa macchina, in "default" server di produzione IIS impostazioni (quindi probabilmente come utente NETWORKSERVICE?)

Sul mio ambiente predefinito VS2008 DEV Ho questo un metodo, che ottiene chiamato dalla app ASP.NET, che funziona bene:

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

Quando eseguo questo sul server di produzione, ottengo il seguente errore dal ServiceController:

Sourc e: System.ServiceProcess -> System.ServiceProcess.ServiceController -> IntPtr GetServiceHandle (Int32) -> System.InvalidOperationException Messaggio: Impossibile aprire il servizio eTimeSheetReminderService sul computer ".".

Perché sta succedendo e come lo risolvo?

EDIT:

La risposta è al di sotto, per lo più nei commenti, ma per chiarire:

  1. Il problema era legato di sicurezza, e si è verificato perché l'account NetworkService non ha diritti sufficienti per Avviare/interrompere un servizio
  2. ho creato un account utente locale, e ha aggiunto al gruppo PowerUsers (questo gruppo ha i diritti di amministratore quasi)
  3. non voglio Tutta la mia app Web per impersonare quell'utente in ogni momento, quindi impersono solo nel metodo in cui manipolo il servizio. Lo faccio utilizzando le seguenti risorse per aiutarmi a fare nel codice:

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

NOTA: Non impersonate tramite web.config, lo faccio in codice. Vedere l'articolo MS KB sopra.

+0

Che tipo di eccezione era? Un 'System.InvalidOperationException'? – Phaedrus

+0

@Phaedrus: hey, si, si lo era. Ho inserito ulteriori informazioni sull'errore nella mia modifica. evviva – andy

risposta

6

Prova ad aggiungere questo al tuo Web.Config.

<identity impersonate="true"/> 
+0

hey phaedrus, stesso errore – andy

+0

L'accesso anonimo è abilitato in IIS? – Phaedrus

+0

sì, "Abilita accesso anonimo" è spuntato – andy

0

Solo un sospetto, ma non mi sembra che l'errore sia necessariamente correlato alla sicurezza. Hai dato al servizio lo stesso nome sul server di produzione?

+0

@cdonner: si, lo sospettavo anch'io, ma non sapevo come avrei potuto testarlo? Sì, il nome è lo stesso, è definito nel codice, nel componente ServiceInstaller. Qualche idea su come ottenere una risposta definitiva sul fatto che sia anche la sicurezza? cheers – andy

+0

è possibile avviare e arrestare dalla riga di comando, utilizzando NET START/STOP? – cdonner

+0

hey man, sì, l'esecuzione di "NET START eTimeSheetReminderService" nel prompt dei comandi avvia correttamente il servizio – andy

12

di dare il permesso di IIS per avviare/fermare un particolare servizio:

  • Scaricare e installare Subinacl.exe. ( Assicurati di avere l'ultima versione! Le versioni precedenti distribuite in alcuni kit di risorse non funzionano!)
  • emettere un comando simile a: subinacl /service {yourServiceName} /grant=IIS_WPG=F

Questo garantisce diritti di controllo a servizio completo per quel particolare servizio al gruppo predefinito IIS_WPG. (Questo funziona per IIS6/Win2k3.) YMMV versioni più recenti delle IIS)

+0

Perfetto, ha fatto il trucco per me e non ho nemmeno dovuto aggiungere la rappresentazione al mio web.config. Saluti! – Nabster

+0

Questa è la soluzione migliore secondo me. Grazie Martin_ATS. – amiir

+0

Utilizzare questo strumento per assegnare un diritto utente, in cui si utilizza la rappresentazione per impersonare l'utente che dispone dei diritti per l'avvio e l'interruzione del servizio. – 130nk3r5

1

Questa era una buona domanda che mi ha incuriosito così ...

Così qui è quello che ho fatto per risolvere questo problema:.

  • Passaggio 1: creare un account utente Windows sul computer locale con diritti minimi.
  • Fase 2: Dare a questo i diritti degli utenti per avviare e arrestare il servizio tramite SUBINACL.EXE
  • cioè subinacl.exe/servizio WindowsServiceName/GRANT = pcname \ TestUser = STOE
  • Dowload da: http://www.microsoft.com/en-za/download/details.aspx?id=23510
  • Passo 3: utilizzare la rappresentazione per rappresentare l'utilizzo creata nel passaggio 1 per avviare e arrestare il servizio

    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 non ha funzionato per me ma grazie a questa risposta https://stackoverflow.com/a/28503968/4568373 l'ho modificato in LOGON32_LOGON_NETWORK e ho funzionato bene. –

0

Se l'applicazione web ha il database e il servizio Windows può accedervi, è sufficiente utilizzare il flag nel DB per riavviare il servizio. Nel servizio, puoi leggere questo flag e riavviare se non occupato ecc. Solo nel caso in cui sia possibile modificare il codice del servizio. Se si tratta di servizi di terze parti è possibile creare il proprio servizio Windows e utilizzare la configurazione del database per controllare (riavviare) i servizi. È il modo sicuro e ti offre molta più flessibilità e sicurezza.

1

Aggiornamento per IIS 8 (e forse alcune versioni leggermente precedenti)

Il Gruppo nuovo IIS_WPG non esiste più. È stato modificato in IIS_IUSRS.

Inoltre, per avviare l'arresto di un servizio non è necessario fornire le autorizzazioni complete (F). Le autorizzazioni per avviare, arrestare e mettere in pausa un servizio (TOP) dovrebbero essere sufficienti. Come tale, il comando dovrebbe essere:

subinacl/servizio {yourServiceName}/concedere = IIS_IUSRS = TOP

Nota che è necessario puntare il prompt dei comandi (preferibilmente elevati per l'esecuzione come amministratore) per C:\Windows\System32 Cartella prima di eseguire questo comando.

Assicurarsi inoltre di aver copiato il file subinacl.exe su C:\Windows\System32 dalla directory di installazione in caso di errore.

Problemi correlati