Domanda

Ecco il mio stack di sicurezza Windows / .NET:

  • Un servizio Windows in esecuzione come LocalSystem su una scatola di Windows Server 2003.
  • Un sito Web .NET 3.5 in esecuzione nella stessa casella, in " default " impostazioni IIS del server di produzione (quindi probabilmente come utente NETWORKSERVICE?)

Nel mio ambiente VS2008 DEV predefinito ho questo metodo, che viene chiamato dall'app ASP.NET, che funziona benissimo:

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, ricevo il seguente errore da ServiceController:

  

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

Perché sta succedendo questo problema e come posso risolverlo?

Modifica

La risposta è di seguito, principalmente nei commenti, ma per chiarire:

  1. Il problema era legato alla sicurezza e si verificava perché l'account NETWORKSERVICE non aveva diritti sufficienti per avviare / interrompere un servizio
  2. Ho creato un account utente locale e l'ho aggiunto al gruppo PowerUsers (questo gruppo ha quasi i diritti di amministratore)
  3. Non voglio che la mia intera App Web impersoni sempre quell'utente, quindi impersono solo il metodo in cui manipolo il servizio. Lo faccio usando le seguenti risorse per aiutarmi a farlo nel codice:

articolo di MS KB e questo, solo per capire meglio

NOTA: non impersonare tramite web.config, lo faccio in codice. Vedi l'articolo di MS KB sopra.

È stato utile?

Soluzione

Prova ad aggiungere questo al tuo Web.Config.

<identity impersonate="true"/>

Altri suggerimenti

Per autorizzare IIS ad avviare / interrompere un determinato servizio:

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

Ciò garantisce i diritti di controllo del servizio completo per quel particolare servizio al gruppo IIS_WPG integrato. (Funziona con IIS6 / Win2k3.) YMMV per le versioni più recenti di IIS.)

Questa è stata una buona domanda che mi ha incuriosito ...

Quindi, ecco cosa ho fatto per risolvere questo problema:

  • Passaggio 1: creare un account utente di Windows sul computer locale con diritti minimi.
  • Passaggio 2: concedere a questo utente i diritti di avviare e arrestare il servizio tramite subinacl.exe
  • vale a dire. subinacl.exe / service WindowsServiceName / GRANT = PCNAME \ TestUser = STOE
  • Scarica da: http://www.microsoft.com /en-za/download/details.aspx?id=23510
  • Passaggio 3: utilizzare la rappresentazione per rappresentare l'utilizzo creato nel passaggio 1 per avviare e interrompere 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();
            }
    
        }
    }
    

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

Il gruppo utenti IIS_WPG non esiste più. È stato modificato in IIS_IUSRS .

Inoltre, per avviare un servizio non è necessario fornire autorizzazioni complete (F). Le autorizzazioni per l'avvio, l'arresto e la pausa di un servizio (TOP) dovrebbero essere sufficienti. Pertanto, il comando dovrebbe essere:

  

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

Nota che devi puntare il prompt dei comandi (preferibilmente elevato per essere eseguito come amministratore) su C: \ Windows \ System32 prima di eseguire questo comando.

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

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

Se la tua applicazione web ha il database e il servizio Windows può accedervi, puoi semplicemente usare il flag nel DB per riavviare il servizio. Nel servizio è possibile 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 un servizio 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 dà molta più flessibilità e sicurezza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top