
Em primeiro lugar, alguns princípios básicos:
- Estou executando um aplicativo de servidor de banco de dados que é executado como um serviço ServiceA.
- ServiceA está instalado em Server1 e Server2
- O ServiceA só é executado em um dos dois servidores por vez (eles são servidores redundantes; se um servidor falhar, o ServiceA no outro servidor será iniciado automaticamente)
- Não tenho controle sobre o código ServiceA
- ServiceA faz logon como conta do Sistema Local
- ServiceB precisa ser executado no Servidor1 e no Servidor2
- ServiceB faz logon como conta do sistema local
- Posso criar objetos de configuração no banco de dados ServiceA. Um tipo de objeto é como uma linguagem de script que o ServiceA executa
- A linguagem de script possui funções para permitir a manipulação de arquivos e execução de comandos no sistema local - OPEN_FILE(), WRITE_FILE(), CLOSE_FILE() e SYSTEM()
- Quando esses comandos são executados, eles são executados com as credenciais da conta do Sistema Local
- ServiceA gera arquivos de configuração para ServiceB usando os comandos de manipulação de arquivo acima
- O ServiceB precisa ser encerrado antes que os novos arquivos de configuração sejam criados (na verdade, eu os crio em uma pasta temporária, encerro o ServiceB, copio os arquivos e, em seguida, inicio o ServiceB)
- Server1 e Server2 são ambos Windows 7 Enterprise
- Server1 e Server2 estão no mesmo domínio
Estou tentando fazer com que o ServiceA:
- Desligue o ServiceB no Servidor1 e no Servidor2
- Copie alguns arquivos para Server1 e Server2
- Inicie o ServiceB no Servidor1 e no Servidor2
O que eu fiz:
- Criou uma conta de domínio (DomainX/UserY) com uma senha (PasswordZ)
- Criou um compartilhamento em ambos os servidores (ShareW)
- Dada permissão DomainX/UserY para acessar ShareW em ambos os servidores
- Usado OPEN_FILE(), WRITE_FILE() e CLOSE_FILE() para criar um arquivo INSTALL.BAT
- Usei SYSTEM('C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1') para executar o INSTALL.BAT (executado pelo Serviço A como Sistema Local) e criar um log de depuração
O que funciona no arquivo BAT
NET USE \\Server1\ShareW PasswordZ /USER:DomainX/UserY
NET USE \\Server2\ShareW PasswordZ /USER:DomainX/UserY
DEL /F /Q "\\Server1\ShareW\*.*
DEL /F /Q "\\Server2\ShareW\*.*
XCOPY /Y "C:\Temp\*.*" "\\Server1\ShareW\"
XCOPY /Y "C:\Temp\*.*" "\\Server2\ShareW\"
NET USE \\Server1\ShareW /DELETE
NET USE \\Server2\ShareW /DELETE
Portanto, o compartilhamento e as permissões estão configurados corretamente
Meu problema é tentar parar e iniciar o serviço B de dentro do INSTALL.BAT
Eu tentei:
NET USE \\Server1\IPC$ PasswordZ /USER:DomainX/UserY
SC \\Server1 STOP "Service B"
NET USE \\Server1\IPC$ /DELETE
NET USE \\Server2\IPC$ PasswordZ /USER:DomainX/UserY
SC \\Server2 STOP "Service B"
NET USE \\Server2\IPC$ /DELETE
O comando SC funciona para a máquina local, mas falha para a máquina remota. Esta é a saída do INSTALL.BAT em execução no Server2:
C:\Windows\system32>NET USE \\Server1\IPC$ PasswordZ /USER:DomainX/UserY
The command completed successfully.
C:\Windows\system32>SC \\Server1 STOP ServiceB
[SC] OpenService FAILED 5:
Access is denied.
(Tentei com o ServiceA em execução no Servidor1 e no Servidor2 - mesmos resultados - Funciona localmente, falha remotamente)
Se eu usar 'Executar como usuário diferente' para executar CMD.EXE como DomainX/UserY, o comando SC funciona perfeitamente. Portanto, no Server2, executando INSTALL.BAT do CMD.EXE como DomainX/UserY, recebo:
C:\Temp>NET USE \\Server1\IPC$ PasswordZ /USER:DomainX/UserY
The command completed successfully.
C:\Temp>SC \\Server1 START ServiceB
SERVICE_NAME: ServiceB
TYPE : 110 WIN32_OWN_PROCESS (interactive)
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x1
WAIT_HINT : 0xbb8
PID : 4856
FLAGS :
Portanto o DomainX/UserY possui as permissões necessárias para parar e iniciar o serviço no servidor remoto. Parece ser algum tipo de bloqueio de escalonamento de privilégios da conta do Sistema Local.
Eu li sobre como definir LocalAccountTokenFilterPolicy em HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ como 1 e reinicializar. Eu fiz isso sem alterações no resultado.
Tentei usar psservice.exe com resultados praticamente idênticos (saída de INSTALL.BAT executado por ServiceA no Server2)
C:\Windows\system32>C:\Utilities\psservice.exe \\Server1 -u DomainX/UserY -p PasswordZ -accepteula stop ServiceB
PsService v2.24 - Service information and configuration utility
Copyright (C) 2001-2010 Mark Russinovich
Sysinternals - www.sysinternals.com
Access is denied.
Access is denied.
Error querying services on Server1:
Error opening ServiceB on Server1:
Então tentei usar o psexec (de uma maneira bastante complicada para capturar todos os registros)
INSTALAR2.BAT
C:\Utilities\psexec -accepteula -u DomainX/UserY -p PasswordZ C:\Temp\INSTALL3.BAT >> C:\Temp\INSTALL3.LOG 2>&1
INSTALAR3.BAT
C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1
Quando executo INSTALL2.BAT na linha de comando (executando como DomainX/UserY), tudo funciona bem.
Mas execute a partir do ServiceA, em INSTALL3.LOG eu recebo:
PsExec v2.11 - Execute processes remotely
Copyright (C) 2001-2014 Mark Russinovich
Sysinternals - www.sysinternals.com
Access is denied.
PsExec could not start C:\Temp\INSTALL3.BAT:
Portanto, adicionei explicitamente DomainX/UserY com controle total à segurança de todos os três arquivos BAT. O resultado foi o mesmo (Acesso negado. em INSTALL3.LOG)
Também tentei desligar o UAC sem nenhum impacto perceptível
Então, estou praticamente preso agora - como posso fazer com que um arquivo BAT seja executado por um serviço executado como sistema local para representar outro usuário e permitir que ele interrompa e inicie serviços em uma máquina remota?
Responder1
A resposta é muito simples - use a opção -h do psexec
O comando SYSTEM() do script executado pelo ServiceA agora é:
C:\Utilities\psexec -accepteula -h -u DomainX/UserY -p PasswordZ C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1
Funciona perfeitamente