
Для начала несколько основ:
- Я запускаю приложение сервера базы данных, которое работает как служба ServiceA.
- ServiceA установлен на Server1 и Server2
- ServiceA работает только на одном из двух серверов в любой момент времени (они являются резервными серверами: если один сервер выходит из строя, ServiceA на другом сервере запускается автоматически)
- Я не имею никакого контроля над кодом ServiceA
- ServiceA входит в систему как учетная запись локальной системы
- ServiceB должен работать как на Server1, так и на Server2.
- ServiceB входит в систему как учетная запись локальной системы
- Я могу создавать объекты конфигурации в базе данных ServiceA. Один тип объекта похож на язык сценариев, который ServiceA выполняет
- В языке сценариев имеются функции, позволяющие манипулировать файлами и выполнять команды в локальной системе — OPEN_FILE(), WRITE_FILE(), CLOSE_FILE() и SYSTEM().
- При выполнении этих команд они запускаются с использованием учетных данных локальной системной учетной записи.
- ServiceA генерирует файлы конфигурации для ServiceB, используя указанные выше команды для работы с файлами.
- ServiceB необходимо остановить перед созданием новых файлов конфигурации (я на самом деле создаю их во временной папке, останавливаю ServiceB, копирую файлы, а затем запускаю ServiceB)
- Server1 и Server2 — оба работают под управлением Windows 7 Enterprise.
- Server1 и Server2 находятся в одном домене.
Я пытаюсь заставить ServiceA:
- Завершите работу ServiceB на Server1 и Server2.
- Скопируйте некоторые файлы на Server1 и Server2
- Запустите ServiceB на Server1 и Server2.
Что я наделал:
- Создал учетную запись домена (DomainX/UserY) с паролем (PasswordZ)
- Создал общий ресурс на обоих серверах (ShareW)
- Предоставлено разрешение DomainX/UserY на доступ к ShareW на обоих серверах
- Использовал OPEN_FILE(), WRITE_FILE() и CLOSE_FILE() для создания файла INSTALL.BAT
- Использовал SYSTEM('C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1') для запуска INSTALL.BAT (запускается службой A как локальная система) и создания журнала отладки
Что работает из 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
Итак, общий доступ и разрешения настроены правильно.
Моя проблема заключается в попытке остановить и запустить службу B из INSTALL.BAT.
Я пытался:
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
Команда SC работает для локальной машины, но не работает для удаленной машины. Это вывод INSTALL.BAT, запущенного на 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.
(Я пробовал запустить ServiceA на Server1 и Server2 — результаты те же — локально работает, удаленно не работает)
Если я использую «Запуск от имени другого пользователя» для запуска CMD.EXE как DomainX/UserY, команда SC работает отлично. Так что на Server2, запуская INSTALL.BAT из CMD.EXE как DomainX/UserY, я получаю:
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 :
Итак, DomainX/UserY имеет необходимые разрешения для остановки и запуска службы на удаленном сервере. Похоже, это своего рода блокировка повышения привилегий из учетной записи Local System.
Я читал о том, что можно установить LocalAccountTokenFilterPolicy в HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ на 1 и перезагрузить. Я сделал это, но результат не изменился.
Я пробовал использовать psservice.exe с практически идентичными результатами (вывод из INSTALL.BAT, запущенного ServiceA на 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:
Затем я попытался использовать psexec (довольно замысловатым способом, чтобы перехватить все журналы)
INSTALL2.BAT
C:\Utilities\psexec -accepteula -u DomainX/UserY -p PasswordZ C:\Temp\INSTALL3.BAT >> C:\Temp\INSTALL3.LOG 2>&1
INSTALL3.BAT
C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1
Когда я запускаю INSTALL2.BAT из командной строки (от имени DomainX/UserY), все работает нормально.
Но запустив ServiceA, в INSTALL3.LOG я получаю:
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:
Поэтому я явно добавил DomainX/UserY с полным доступом к безопасности всех трех BAT-файлов. Результат был тот же (Доступ запрещен. в INSTALL3.LOG)
Я также пробовал отключить UAC, но ощутимого эффекта не произошло.
Итак, я в тупике — как мне заставить службу, работающую от имени локальной системы, выполнить BAT-файл, чтобы выдать себя за другого пользователя и разрешить ей останавливать и запускать службы на удаленной машине?
решение1
Ответ оказывается очень простым - используйте опцию -h psexec
Команда SYSTEM() скрипта, запущенного ServiceA, теперь выглядит следующим образом:
C:\Utilities\psexec -accepteula -h -u DomainX/UserY -p PasswordZ C:\Temp\INSTALL.BAT >> C:\Temp\INSTALL.LOG 2>&1
Работает отлично.