私は Java サーバー アプリケーション (サービスとして Windows で実行) に取り組んでおり、次のシナリオを実現したいと考えています。
- ユーザーがサーバーに POST 要求を発行します。ユーザーは Kerberos (SPNEGO、エンタープライズ環境の SSO) で認証されます。
- サービスは新しい Java プロセスを作成します。プロセスは、サービス ユーザー (偽装/委任) ではなく、認証されたユーザーとして実行する必要があります。
- 新しいプロセスは、ユーザーのセキュリティ コンテキストで実行する必要があります。Kerberos 認証を必要とする他のリモート システム (ファイル共有、他の Web サービスなど) と通信する必要があります。
実用的な概念実証:
- Kerberos 認証用のWaffleを使用した Spring Boot アプリケーション。
- 認証済みユーザーの新しいプロセスを作成します。JNAと Windows ネイティブ関数CreateProcessAsUserを使用します。
- プロセスは、認証されたユーザーとして作成されます。これは、Process Explorer ユーティリティで確認できます。
不足しているものと機能していないもの:
- プロセスは他の Kerberos チケットを要求できません (たとえば、InitializeSecurityContext() を呼び出して)。
- プロセスはネットワーク共有にアクセスできません。
- このプロセスは、Kerberos 認証を必要とする他の Web サービスと通信できません。
私の質問:
- コードに何が欠けているか、または何が間違っている可能性がありますか?
- 私が達成したいことを実装することさえ可能ですか?
サーバー - 認証 (短縮、Waffle から抽出):
final byte[] tokenBuffer = authorizationHeader.getTokenBytes();
CredHandle serverCredHandle = new CredHandle();
TimeStamp clientLifetime = new TimeStamp();
int rc = Secur32.INSTANCE.AcquireCredentialsHandle(
null,
"Negotiate",
Sspi.SECPKG_CRED_INBOUND,
null,
null,
null,
null,
serverCredHandle,
clientLifetime);
SecBufferDesc pbClientToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, tokenBuffer);
CtxtHandle phNewServerContext = new CtxtHandle();
SecBufferDesc pbServerToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
IntByReference pfClientContextAttr = new IntByReference();
rc = Secur32.INSTANCE.AcceptSecurityContext(
serverCredHandle,
null,
pbClientToken,
Sspi.ISC_REQ_CONNECTION,
Sspi.SECURITY_NATIVE_DREP,
phNewServerContext,
pbServerToken,
pfClientContextAttr,
null);
rc = Advapi32.INSTANCE.ImpersonateLoggedOnUser(/* provide the security context token */)
サーバー - CreateProcessAsUser:
// get impersonation token of user
WinNT.HANDLEByReference threadToken = new WinNT.HANDLEByReference();
WinNT.HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread();
boolean threadTokenResult = Advapi32.INSTANCE.OpenThreadToken(
threadHandle,
WinNT.TOKEN_QUERY | WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_ASSIGN_PRIMARY,
false, /* TRUE if the access check is to be made against the process-level security context. FALSE if the access check is to be made against the current security context of the thread calling the OpenThreadToken function. */
threadToken);
// create primary token by duplicating impersonation token
WinNT.HANDLEByReference primaryToken = new WinNT.HANDLEByReference();
boolean primaryTokenResult = Advapi32.INSTANCE.DuplicateTokenEx(
threadToken.getValue(), /* hExistingToken */
WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY | WinNT.TOKEN_ASSIGN_PRIMARY, /* dwDesiredAccess */
null, /* lpTokenAttributes */
WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityDelegation, /* ImpersonationLevel */
WinNT.TOKEN_TYPE.TokenPrimary, /* TokenType */
primaryToken); /* phNewToken */
String environment = createEnvironment(primaryToken);
WinBase.STARTUPINFO startupInfo = new WinBase.STARTUPINFO();
WinBase.PROCESS_INFORMATION processInfo = new WinBase.PROCESS_INFORMATION();
boolean createProcessResult = Advapi32.INSTANCE.CreateProcessAsUser(
primaryToken.getValue(), /* hToken */
null, /* lpApplicationName */
command, /* lpCommandLine */
null, /* lpProcessAttributes */
null, /* lpThreadAttributes */
false, /* bInheritHandles */
WinNT.CREATE_NEW_CONSOLE | WinNT.CREATE_UNICODE_ENVIRONMENT, /* dwCreationFlags */
environment, /* lpEnvironment */
processDirectory, /* lpCurrentDirectory */
startupInfo, /* lpStartupInfo */
processInfo); /* lpProcessInformation */