ASP.NET 앱에서 Windows 서비스를 시작/중지하는 방법 - 보안 문제
-
03-07-2019 - |
문제
내 Windows/.NET 보안 스택은 다음과 같습니다.
- Windows Server 2003 상자에서 로컬 시스템으로 실행되는 Windows 서비스.
- "기본값"제작 서버 IIS 설정에서 동일한 상자에서 실행되는 .NET 3.5 웹 사이트 (아마 네트워크 서비스 사용자로서?)
기본값 vs2008 Dev 환경에는 ASP.NET 앱에서 호출되는이 하나의 방법이 있습니다.
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);
}
}
}
제작 서버에서 이것을 실행하면 ServiceController에서 다음 오류가 발생합니다.
출처 : System.ServiceProcess-> System.ServiceProcess.ServiceController-> intptr getServiceHandle (int32) -> System.InValidOperationException 메시지 : 컴퓨터에서 etimesheetreminderservice 서비스를 열 수 없습니다. '.
왜 이런 일이 일어나고 어떻게 해결합니까?
편집하다:
답은 아래에 있으며, 주로 의견에 있지만 명확하게 설명합니다.
- 문제는 보안 관련이었으며 NetworkService 계정이 서비스 시작/중지에 충분한 권한이 없기 때문에 발생했습니다.
- 로컬 사용자 계정을 만들어 PowerUsers 그룹에 추가했습니다 (이 그룹은 거의 관리자 권한이 있습니다).
- 나는 내 웹 앱 전체가 항상 그 사용자를 가장하는 것을 원하지 않으므로 서비스를 조작하는 방법에만 사칭합니다. 코드로 수행하는 데 도움이되는 다음 리소스를 사용 하여이 작업을 수행합니다.
MS KB 기사 그리고 이것은 더 나은 이해를 얻기 위해
노트: 나는 web.config를 통해 가장하지 않고 코드로 수행합니다. 위의 MS KB 기사를 참조하십시오.
해결책
이것을 web.config에 추가 해보십시오.
<identity impersonate="true"/>
다른 팁
IIS 특정 서비스를 시작/중지 할 수있는 권한을 부여하려면 :
- 다운로드 및 설치 subinacl.exe. (최신 버전을 얻으십시오! 일부 리소스 키트에 배포 된 이전 버전은 작동하지 않습니다!)
- 다음과 유사한 명령을 발행합니다.
subinacl /service {yourServiceName} /grant=IIS_WPG=F
이는 내장 IIS_WPG 그룹에 해당 특정 서비스에 대한 완전한 서비스 제어 권한을 부여합니다. (이것은 IIS6 / win2k3에서 작동합니다.) YMMV 최신 버전의 IIS를 위해.)
이것은 나를 흥미롭게 한 좋은 질문이었다 ...
그래서 여기에 내가이 문제를 해결하기 위해 한 일이 있습니다.
- 1 단계 : 최소한의 권한으로 로컬 컴퓨터에서 Windows 사용자 계정을 만듭니다.
- 2 단계 : Subinacl.exe를 통해 서비스를 시작하고 중지 할 수있는이 사용자 권한을 부여하십시오.
- IE subinacl.exe /서비스 WindowsServiceName /grant = pcname testuser = stoe
- 다우로드 : http://www.microsoft.com/en-za/download/details.aspx?id=23510
3 단계 : 사칭을 사용하여 1 단계에서 생성 된 사용을 가장하여 서비스를 시작하고 중지합니다.
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(); } } }
IIS 8에 대한 업데이트 (아마도 약간 이전 버전)
사용자 그룹 IIS_WPG 더 이상 존재하지 않습니다. 변경되었습니다 iis_iusrs.
또한 서비스를 중지하기 위해 전체 권한을 부여하는 것이 필요하지 않습니다 (F). 서비스를 시작, 중지 및 일시 중지 할 권한이 충분해야합니다. 따라서 명령은 다음과 같습니다.
subinacl /service {yourservicename} /grant = iis_iusrs = top
명령 프롬프트 (바람직하게는 관리자로 실행되기 위해 높음)를 C:\Windows\System32
이 명령을 실행하기 전에 폴더.
또한 subinacl.exe 파일을 C:\Windows\System32
설치 디렉토리에서 오류가있는 경우
직감에 불과하지만 오류는 반드시 보안과 관련이있는 것 같습니다. 프로덕션 서버에서 서비스에 동일한 이름을 부여 했습니까?
웹 응용 프로그램에 데이터베이스가 있고 Windows 서비스에 액세스 할 수있는 경우 DB의 플래그를 사용하여 서비스를 다시 시작할 수 있습니다. 서비스 에서이 플래그를 읽고 바쁘지 않은 경우 다시 시작할 수 있습니다. 서비스 코드를 수정할 수있는 경우에만 해당합니다. 타사 서비스 인 경우 고유 한 Windows 서비스를 작성하고 데이터베이스 구성을 사용하여 서비스를 다시 시작할 수 있습니다. 안전한 방법이며 훨씬 더 유연성과 보안을 제공합니다.