Comment démarrer / arrêter un service Windows à partir d'une application ASP.NET - Problèmes de sécurité

StackOverflow https://stackoverflow.com/questions/818512

Question

Voici ma pile de sécurité Windows / .NET:

  • Un service Windows s'exécutant en tant que LocalSystem sur une boîte Windows Server 2003.
  • Un site Web .NET 3.5 s'exécutant sur la même boîte, sous " défaut " Paramètres IIS du serveur de production (donc probablement en tant qu’utilisateur de NETWORKSERVICE?)

Sur mon environnement de développement VS2008 par défaut, cette méthode est appelée à partir de l'application ASP.NET, qui fonctionne correctement:

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

Lorsque j'exécute cette opération sur le serveur de production, le code d'erreur suivant indique l'erreur ServiceController:

  

Source: System.ServiceProcess - >   System.ServiceProcess.ServiceController - > IntPtr   GetServiceHandle (Int32) - > Message System.InvalidOperationException:   Impossible d'ouvrir le service eTimeSheetReminderService sur l'ordinateur '.'.

Pourquoi cela se produit-il et comment y remédier?

EDIT:

La réponse est ci-dessous, principalement dans les commentaires, mais pour clarifier:

  1. Le problème était lié à la sécurité et était dû au fait que le compte NETWORKSERVICE ne disposait pas des droits suffisants pour démarrer / arrêter un service
  2. J'ai créé un compte d'utilisateur local et je l'ai ajouté au groupe PowerUsers (ce groupe dispose des droits presque d'administrateur)
  3. Je ne veux pas que l'ensemble de mon application Web emprunte l'identité de cet utilisateur tout le temps. Par conséquent, je n'emprunte l'identité que de la méthode utilisée pour manipuler le service. Je le fais en utilisant les ressources suivantes pour m'aider à le faire dans le code:

article MS KB et ceci, juste pour mieux comprendre

REMARQUE: je ne personnifie pas l'identité via le fichier Web.config, je le fais en code. Voir l'article MS KB ci-dessus.

Était-ce utile?

La solution

Essayez d'ajouter ceci à votre Web.Config.

<identity impersonate="true"/>

Autres conseils

Pour autoriser IIS à démarrer / arrêter un service particulier:

  • Téléchargez et installez Subinacl.exe . ( Assurez-vous d'avoir la dernière version! Les versions antérieures distribuées dans certains kits de ressources ne fonctionnent pas! )
  • Émettez une commande semblable à: subinacl / service {yourServiceName} / grant = IIS_WPG = F

Ceci accorde des droits de contrôle de service complets pour ce service particulier au groupe IIS_WPG intégré. (Cela fonctionne pour IIS6 / Win2k3.) YMMV pour les versions plus récentes d’IIS.

C’était une bonne question qui m’intriguait aussi ...

Alors voici ce que j'ai fait pour résoudre ce problème:

  • Étape 1: créez un compte d'utilisateur Windows sur la machine locale avec des droits minimaux.
  • Étape 2: accordez à cet utilisateur le droit de démarrer et d’arrêter le service via subinacl.exe
  • c'est-à-dire. subinacl.exe / service WindowsServiceName / GRANT = PCNAME \ TestUser = STOE
  • Télécharger de: http://www.microsoft.com /en-za/download/details.aspx?id=23510
  • Étape 3: utilisez l'emprunt d'identité pour emprunter l'identité créée à l'étape 1 pour démarrer et arrêter le service

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

Mise à jour pour IIS 8 (et peut-être certaines versions légèrement antérieures)

Le groupe d'utilisateurs IIS_WPG n'existe plus. Il a changé en IIS_IUSRS .

De plus, pour démarrer arrêter un service, il n'est pas nécessaire de donner des autorisations complètes (F). Les autorisations pour démarrer, arrêter et suspendre un service (TOP) devraient suffire. En tant que telle, la commande devrait être:

  

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

Notez que vous devez faire en sorte que l'invite de commande (de préférence élevée pour être exécutée en tant qu'administrateur) sur le dossier C: \ Windows \ System32 avant d'exécuter cette commande.

Assurez-vous également que vous avez copié le fichier subinacl.exe dans C: \ Windows \ System32 à partir du répertoire d'installation en cas d'erreur.

Juste un pressentiment, mais il ne me semble pas que l'erreur soit nécessairement liée à la sécurité. Avez-vous donné le même nom au service sur le serveur de production?

Si votre application Web dispose de la base de données et que le service Windows peut y accéder, vous pouvez simplement utiliser l'indicateur de la base de données pour redémarrer le service. Dans le service, vous pouvez lire cet indicateur et le redémarrer s'il n'est pas occupé, etc. Seulement si vous pouvez modifier le code du service.        S'il s'agit d'un service tiers, vous pouvez créer votre propre service Windows et utiliser la configuration de la base de données pour contrôler (redémarrer) les services. C'est le moyen sûr et vous donne beaucoup plus de flexibilité et de sécurité.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top